summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig13
-rw-r--r--drivers/input/Makefile1
-rw-r--r--drivers/input/evdev.c5
-rw-r--r--drivers/input/ff-core.c14
-rw-r--r--drivers/input/ff-memless.c18
-rw-r--r--drivers/input/input-leds.c212
-rw-r--r--drivers/input/input-mt.c35
-rw-r--r--drivers/input/input.c2
-rw-r--r--drivers/input/joydev.c61
-rw-r--r--drivers/input/joystick/xpad.c21
-rw-r--r--drivers/input/keyboard/Kconfig24
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/adp5589-keys.c6
-rw-r--r--drivers/input/keyboard/atkbd.c6
-rw-r--r--drivers/input/keyboard/bcm-keypad.c458
-rw-r--r--drivers/input/keyboard/clps711x-keypad.c7
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c36
-rw-r--r--drivers/input/keyboard/gpio_keys_polled.c2
-rw-r--r--drivers/input/keyboard/ipaq-micro-keys.c168
-rw-r--r--drivers/input/keyboard/lm8333.c4
-rw-r--r--drivers/input/keyboard/max7359_keypad.c31
-rw-r--r--drivers/input/keyboard/samsung-keypad.c2
-rw-r--r--drivers/input/keyboard/spear-keyboard.c2
-rw-r--r--drivers/input/keyboard/tc3589x-keypad.c42
-rw-r--r--drivers/input/misc/Kconfig47
-rw-r--r--drivers/input/misc/Makefile4
-rw-r--r--drivers/input/misc/adxl34x-i2c.c21
-rw-r--r--drivers/input/misc/da9063_onkey.c226
-rw-r--r--drivers/input/misc/drv260x.c13
-rw-r--r--drivers/input/misc/drv2665.c322
-rw-r--r--drivers/input/misc/gpio-beeper.c7
-rw-r--r--drivers/input/misc/max77843-haptic.c358
-rw-r--r--drivers/input/misc/mma8450.c35
-rw-r--r--drivers/input/misc/palmas-pwrbutton.c2
-rw-r--r--drivers/input/misc/pm8941-pwrkey.c293
-rw-r--r--drivers/input/misc/pwm-beeper.c9
-rw-r--r--drivers/input/misc/regulator-haptic.c2
-rw-r--r--drivers/input/misc/retu-pwrbutton.c3
-rw-r--r--drivers/input/misc/soc_button_array.c1
-rw-r--r--drivers/input/misc/tps65218-pwrbutton.c2
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c3
-rw-r--r--drivers/input/misc/twl6040-vibra.c3
-rw-r--r--drivers/input/misc/wm831x-on.c3
-rw-r--r--drivers/input/mouse/Kconfig12
-rw-r--r--drivers/input/mouse/Makefile1
-rw-r--r--drivers/input/mouse/alps.c627
-rw-r--r--drivers/input/mouse/alps.h79
-rw-r--r--drivers/input/mouse/cyapa.c4
-rw-r--r--drivers/input/mouse/cyapa_gen3.c3
-rw-r--r--drivers/input/mouse/cyapa_gen5.c23
-rw-r--r--drivers/input/mouse/elan_i2c.h13
-rw-r--r--drivers/input/mouse/elan_i2c_core.c112
-rw-r--r--drivers/input/mouse/elan_i2c_i2c.c41
-rw-r--r--drivers/input/mouse/elan_i2c_smbus.c18
-rw-r--r--drivers/input/mouse/elantech.c32
-rw-r--r--drivers/input/mouse/elantech.h1
-rw-r--r--drivers/input/mouse/focaltech.c13
-rw-r--r--drivers/input/mouse/lifebook.c6
-rw-r--r--drivers/input/mouse/psmouse-base.c59
-rw-r--r--drivers/input/mouse/psmouse.h1
-rw-r--r--drivers/input/mouse/sentelic.h4
-rw-r--r--drivers/input/mouse/synaptics.c49
-rw-r--r--drivers/input/mouse/synaptics.h1
-rw-r--r--drivers/input/mouse/synaptics_i2c.c6
-rw-r--r--drivers/input/mouse/vmmouse.c508
-rw-r--r--drivers/input/mouse/vmmouse.h30
-rw-r--r--drivers/input/serio/gscps2.c1
-rw-r--r--drivers/input/serio/i8042.c28
-rw-r--r--drivers/input/touchscreen/Kconfig44
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/ar1021_i2c.c2
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c540
-rw-r--r--drivers/input/touchscreen/bcm_iproc_tsc.c522
-rw-r--r--drivers/input/touchscreen/chipone_icn8318.c316
-rw-r--r--drivers/input/touchscreen/cyttsp4_core.c5
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c6
-rw-r--r--drivers/input/touchscreen/elants_i2c.c14
-rw-r--r--drivers/input/touchscreen/goodix.c90
-rw-r--r--drivers/input/touchscreen/of_touchscreen.c62
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c2
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c2
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c39
-rw-r--r--drivers/input/touchscreen/sun4i-ts.c46
-rw-r--r--drivers/input/touchscreen/sur40.c434
-rw-r--r--drivers/input/touchscreen/sx8654.c286
-rw-r--r--drivers/input/touchscreen/tsc2007.c11
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c11
-rw-r--r--drivers/input/touchscreen/zforce_ts.c1
88 files changed, 6071 insertions, 563 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74a5127..a35532ec00e4 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -25,6 +25,19 @@ config INPUT
if INPUT
+config INPUT_LEDS
+ tristate "Export input device LEDs in sysfs"
+ depends on LEDS_CLASS
+ default INPUT
+ help
+ Say Y here if you would like to export LEDs on input devices
+ as standard LED class devices in sysfs.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called input-leds.
+
config INPUT_FF_MEMLESS
tristate "Support for memoryless force-feedback devices"
help
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f631497f..0c9302ca9954 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
+obj-$(CONFIG_INPUT_LEDS) += input-leds.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index a18f41b89b6a..9d35499faca4 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -422,10 +422,7 @@ static int evdev_release(struct inode *inode, struct file *file)
evdev_detach_client(evdev, client);
- if (is_vmalloc_addr(client))
- vfree(client);
- else
- kfree(client);
+ kvfree(client);
evdev_close_device(evdev);
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index f50f6dd92274..8f4a30fccbb6 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -23,8 +23,6 @@
/* #define DEBUG */
-#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
-
#include <linux/input.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -72,7 +70,7 @@ static int compat_effect(struct ff_device *ff, struct ff_effect *effect)
return -EINVAL;
/*
- * calculate manginude of sine wave as average of rumble's
+ * calculate magnitude of sine wave as average of rumble's
* 2/3 of strong magnitude and 1/3 of weak magnitude
*/
magnitude = effect->u.rumble.strong_magnitude / 3 +
@@ -116,7 +114,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
!test_bit(effect->type, dev->ffbit)) {
- pr_debug("invalid or not supported effect type in upload\n");
+ dev_dbg(&dev->dev, "invalid or not supported effect type in upload\n");
return -EINVAL;
}
@@ -124,7 +122,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
(effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
!test_bit(effect->u.periodic.waveform, dev->ffbit))) {
- pr_debug("invalid or not supported wave form in upload\n");
+ dev_dbg(&dev->dev, "invalid or not supported wave form in upload\n");
return -EINVAL;
}
@@ -215,7 +213,7 @@ static int erase_effect(struct input_dev *dev, int effect_id,
/**
* input_ff_erase - erase a force-feedback effect from device
* @dev: input device to erase effect from
- * @effect_id: id of the ffect to be erased
+ * @effect_id: id of the effect to be erased
* @file: purported owner of the request
*
* This function erases a force-feedback effect from specified device.
@@ -246,7 +244,7 @@ static int flush_effects(struct input_dev *dev, struct file *file)
struct ff_device *ff = dev->ff;
int i;
- pr_debug("flushing now\n");
+ dev_dbg(&dev->dev, "flushing now\n");
mutex_lock(&ff->mutex);
@@ -316,7 +314,7 @@ int input_ff_create(struct input_dev *dev, unsigned int max_effects)
int i;
if (!max_effects) {
- pr_err("cannot allocate device without any effects\n");
+ dev_err(&dev->dev, "cannot allocate device without any effects\n");
return -EINVAL;
}
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
index 74c0d8c6002a..fcc6c3368182 100644
--- a/drivers/input/ff-memless.c
+++ b/drivers/input/ff-memless.c
@@ -237,6 +237,18 @@ static u16 ml_calculate_direction(u16 direction, u16 force,
(force + new_force)) << 1;
}
+#define FRAC_N 8
+static inline s16 fixp_new16(s16 a)
+{
+ return ((s32)a) >> (16 - FRAC_N);
+}
+
+static inline s16 fixp_mult(s16 a, s16 b)
+{
+ a = ((s32)a * 0x100) / 0x7fff;
+ return ((s32)(a * b)) >> FRAC_N;
+}
+
/*
* Combine two effects and apply gain.
*/
@@ -247,7 +259,7 @@ static void ml_combine_effects(struct ff_effect *effect,
struct ff_effect *new = state->effect;
unsigned int strong, weak, i;
int x, y;
- fixp_t level;
+ s16 level;
switch (new->type) {
case FF_CONSTANT:
@@ -255,8 +267,8 @@ static void ml_combine_effects(struct ff_effect *effect,
level = fixp_new16(apply_envelope(state,
new->u.constant.level,
&new->u.constant.envelope));
- x = fixp_mult(fixp_sin(i), level) * gain / 0xffff;
- y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff;
+ x = fixp_mult(fixp_sin16(i), level) * gain / 0xffff;
+ y = fixp_mult(-fixp_cos16(i), level) * gain / 0xffff;
/*
* here we abuse ff_ramp to hold x and y of constant force
* If in future any driver wants something else than x and y
diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c
new file mode 100644
index 000000000000..074a65ed17bb
--- /dev/null
+++ b/drivers/input/input-leds.c
@@ -0,0 +1,212 @@
+/*
+ * LED support for the input layer
+ *
+ * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+
+#if IS_ENABLED(CONFIG_VT)
+#define VT_TRIGGER(_name) .trigger = _name
+#else
+#define VT_TRIGGER(_name) .trigger = NULL
+#endif
+
+static const struct {
+ const char *name;
+ const char *trigger;
+} input_led_info[LED_CNT] = {
+ [LED_NUML] = { "numlock", VT_TRIGGER("kbd-numlock") },
+ [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") },
+ [LED_SCROLLL] = { "scrolllock", VT_TRIGGER("kbd-scrolllock") },
+ [LED_COMPOSE] = { "compose" },
+ [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") },
+ [LED_SLEEP] = { "sleep" } ,
+ [LED_SUSPEND] = { "suspend" },
+ [LED_MUTE] = { "mute" },
+ [LED_MISC] = { "misc" },
+ [LED_MAIL] = { "mail" },
+ [LED_CHARGING] = { "charging" },
+};
+
+struct input_led {
+ struct led_classdev cdev;
+ struct input_handle *handle;
+ unsigned int code; /* One of LED_* constants */
+};
+
+struct input_leds {
+ struct input_handle handle;
+ unsigned int num_leds;
+ struct input_led leds[];
+};
+
+static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev)
+{
+ struct input_led *led = container_of(cdev, struct input_led, cdev);
+ struct input_dev *input = led->handle->dev;
+
+ return test_bit(led->code, input->led) ? cdev->max_brightness : 0;
+}
+
+static void input_leds_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct input_led *led = container_of(cdev, struct input_led, cdev);
+
+ input_inject_event(led->handle, EV_LED, led->code, !!brightness);
+}
+
+static void input_leds_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+}
+
+static int input_leds_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct input_leds *leds;
+ unsigned int num_leds;
+ unsigned int led_code;
+ int led_no;
+ int error;
+
+ num_leds = bitmap_weight(dev->ledbit, LED_CNT);
+ if (!num_leds)
+ return -ENXIO;
+
+ leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds),
+ GFP_KERNEL);
+ if (!leds)
+ return -ENOMEM;
+
+ leds->num_leds = num_leds;
+
+ leds->handle.dev = dev;
+ leds->handle.handler = handler;
+ leds->handle.name = "leds";
+ leds->handle.private = leds;
+
+ error = input_register_handle(&leds->handle);
+ if (error)
+ goto err_free_mem;
+
+ error = input_open_device(&leds->handle);
+ if (error)
+ goto err_unregister_handle;
+
+ led_no = 0;
+ for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
+ struct input_led *led = &leds->leds[led_no];
+
+ led->handle = &leds->handle;
+ led->code = led_code;
+
+ if (WARN_ON(!input_led_info[led_code].name))
+ continue;
+
+ led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
+ dev_name(&dev->dev),
+ input_led_info[led_code].name);
+ if (!led->cdev.name) {
+ error = -ENOMEM;
+ goto err_unregister_leds;
+ }
+
+ led->cdev.max_brightness = 1;
+ led->cdev.brightness_get = input_leds_brightness_get;
+ led->cdev.brightness_set = input_leds_brightness_set;
+ led->cdev.default_trigger = input_led_info[led_code].trigger;
+
+ error = led_classdev_register(&dev->dev, &led->cdev);
+ if (error) {
+ dev_err(&dev->dev, "failed to register LED %s: %d\n",
+ led->cdev.name, error);
+ kfree(led->cdev.name);
+ goto err_unregister_leds;
+ }
+
+ led_no++;
+ }
+
+ return 0;
+
+err_unregister_leds:
+ while (--led_no >= 0) {
+ struct input_led *led = &leds->leds[led_no];
+
+ led_classdev_unregister(&led->cdev);
+ kfree(led->cdev.name);
+ }
+
+ input_close_device(&leds->handle);
+
+err_unregister_handle:
+ input_unregister_handle(&leds->handle);
+
+err_free_mem:
+ kfree(leds);
+ return error;
+}
+
+static void input_leds_disconnect(struct input_handle *handle)
+{
+ struct input_leds *leds = handle->private;
+ int i;
+
+ for (i = 0; i < leds->num_leds; i++) {
+ struct input_led *led = &leds->leds[i];
+
+ led_classdev_unregister(&led->cdev);
+ kfree(led->cdev.name);
+ }
+
+ input_close_device(handle);
+ input_unregister_handle(handle);
+
+ kfree(leds);
+}
+
+static const struct input_device_id input_leds_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_LED) },
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(input, input_leds_ids);
+
+static struct input_handler input_leds_handler = {
+ .event = input_leds_event,
+ .connect = input_leds_connect,
+ .disconnect = input_leds_disconnect,
+ .name = "leds",
+ .id_table = input_leds_ids,
+};
+
+static int __init input_leds_init(void)
+{
+ return input_register_handler(&input_leds_handler);
+}
+module_init(input_leds_init);
+
+static void __exit input_leds_exit(void)
+{
+ input_unregister_handler(&input_leds_handler);
+}
+module_exit(input_leds_exit);
+
+MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
+MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
+MODULE_DESCRIPTION("Input -> LEDs Bridge");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index cb150a1dbaff..54fce56c8023 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -88,10 +88,13 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
goto err_mem;
}
- /* Mark slots as 'unused' */
+ /* Mark slots as 'inactive' */
for (i = 0; i < num_slots; i++)
input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
+ /* Mark slots as 'unused' */
+ mt->frame = 1;
+
dev->mt = mt;
return 0;
err_mem:
@@ -365,27 +368,35 @@ static void input_mt_set_slots(struct input_mt *mt,
int *slots, int num_pos)
{
struct input_mt_slot *s;
- int *w = mt->red, *p;
+ int *w = mt->red, j;
- for (p = slots; p != slots + num_pos; p++)
- *p = -1;
+ for (j = 0; j != num_pos; j++)
+ slots[j] = -1;
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
if (!input_mt_is_active(s))
continue;
- for (p = slots; p != slots + num_pos; p++)
- if (*w++ < 0)
- *p = s - mt->slots;
+
+ for (j = 0; j != num_pos; j++) {
+ if (w[j] < 0) {
+ slots[j] = s - mt->slots;
+ break;
+ }
+ }
+
+ w += num_pos;
}
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
if (input_mt_is_active(s))
continue;
- for (p = slots; p != slots + num_pos; p++)
- if (*p < 0) {
- *p = s - mt->slots;
+
+ for (j = 0; j != num_pos; j++) {
+ if (slots[j] < 0) {
+ slots[j] = s - mt->slots;
break;
}
+ }
}
}
@@ -439,6 +450,8 @@ EXPORT_SYMBOL(input_mt_assign_slots);
* set the key on the first unused slot and return.
*
* If no available slot can be found, -1 is returned.
+ * Note that for this function to work properly, input_mt_sync_frame() has
+ * to be called at each frame.
*/
int input_mt_get_slot_by_key(struct input_dev *dev, int key)
{
@@ -453,7 +466,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key)
return s - mt->slots;
for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
- if (!input_mt_is_active(s)) {
+ if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) {
s->key = key;
return s - mt->slots;
}
diff --git a/drivers/input/input.c b/drivers/input/input.c
index cc357f1516a7..f31578423636 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -2262,7 +2262,7 @@ EXPORT_SYMBOL(input_unregister_handler);
*
* Iterate over @bus's list of devices, and call @fn for each, passing
* it @data and stop when @fn returns a non-zero value. The function is
- * using RCU to traverse the list and therefore may be usind in atonic
+ * using RCU to traverse the list and therefore may be using in atomic
* contexts. The @fn callback is invoked from RCU critical section and
* thus must not sleep.
*/
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index f362883c94e3..1d247bcf2ae2 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -747,6 +747,63 @@ static void joydev_cleanup(struct joydev *joydev)
input_close_device(handle);
}
+static bool joydev_dev_is_absolute_mouse(struct input_dev *dev)
+{
+ DECLARE_BITMAP(jd_scratch, KEY_CNT);
+
+ BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT);
+
+ /*
+ * Virtualization (VMware, etc) and remote management (HP
+ * ILO2) solutions use absolute coordinates for their virtual
+ * pointing devices so that there is one-to-one relationship
+ * between pointer position on the host screen and virtual
+ * guest screen, and so their mice use ABS_X, ABS_Y and 3
+ * primary button events. This clashes with what joydev
+ * considers to be joysticks (a device with at minimum ABS_X
+ * axis).
+ *
+ * Here we are trying to separate absolute mice from
+ * joysticks. A device is, for joystick detection purposes,
+ * considered to be an absolute mouse if the following is
+ * true:
+ *
+ * 1) Event types are exactly EV_ABS, EV_KEY and EV_SYN.
+ * 2) Absolute events are exactly ABS_X and ABS_Y.
+ * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE.
+ * 4) Device is not on "Amiga" bus.
+ */
+
+ bitmap_zero(jd_scratch, EV_CNT);
+ __set_bit(EV_ABS, jd_scratch);
+ __set_bit(EV_KEY, jd_scratch);
+ __set_bit(EV_SYN, jd_scratch);
+ if (!bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
+ return false;
+
+ bitmap_zero(jd_scratch, ABS_CNT);
+ __set_bit(ABS_X, jd_scratch);
+ __set_bit(ABS_Y, jd_scratch);
+ if (!bitmap_equal(dev->absbit, jd_scratch, ABS_CNT))
+ return false;
+
+ bitmap_zero(jd_scratch, KEY_CNT);
+ __set_bit(BTN_LEFT, jd_scratch);
+ __set_bit(BTN_RIGHT, jd_scratch);
+ __set_bit(BTN_MIDDLE, jd_scratch);
+
+ if (!bitmap_equal(dev->keybit, jd_scratch, KEY_CNT))
+ return false;
+
+ /*
+ * Amiga joystick (amijoy) historically uses left/middle/right
+ * button events.
+ */
+ if (dev->id.bustype == BUS_AMIGA)
+ return false;
+
+ return true;
+}
static bool joydev_match(struct input_handler *handler, struct input_dev *dev)
{
@@ -758,6 +815,10 @@ static bool joydev_match(struct input_handler *handler, struct input_dev *dev)
if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit))
return false;
+ /* Avoid absolute mice */
+ if (joydev_dev_is_absolute_mouse(dev))
+ return false;
+
return true;
}
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 3aa2f3f3da5b..61c761156371 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -31,12 +31,14 @@
* - the iForce driver drivers/char/joystick/iforce.c
* - the skeleton-driver drivers/usb/usb-skeleton.c
* - Xbox 360 information http://www.free60.org/wiki/Gamepad
+ * - Xbox One information https://github.com/quantus/xbox-one-controller-protocol
*
* Thanks to:
* - ITO Takayuki for providing essential xpad information on his website
* - Vojtech Pavlik - iforce driver / input subsystem
* - Greg Kroah-Hartman - usb-skeleton driver
* - XBOX Linux project - extra USB id's
+ * - Pekka Pöyry (quantus) - Xbox One controller reverse engineering
*
* TODO:
* - fine tune axes (especially trigger axes)
@@ -828,6 +830,23 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+ case XTYPE_XBOXONE:
+ xpad->odata[0] = 0x09; /* activate rumble */
+ xpad->odata[1] = 0x08;
+ xpad->odata[2] = 0x00;
+ xpad->odata[3] = 0x08; /* continuous effect */
+ xpad->odata[4] = 0x00; /* simple rumble mode */
+ xpad->odata[5] = 0x03; /* L and R actuator only */
+ xpad->odata[6] = 0x00; /* TODO: LT actuator */
+ xpad->odata[7] = 0x00; /* TODO: RT actuator */
+ xpad->odata[8] = strong / 256; /* left actuator */
+ xpad->odata[9] = weak / 256; /* right actuator */
+ xpad->odata[10] = 0x80; /* length of pulse */
+ xpad->odata[11] = 0x00; /* stop period of pulse */
+ xpad->irq_out->transfer_buffer_length = 12;
+
+ return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+
default:
dev_dbg(&xpad->dev->dev,
"%s - rumble command sent to unsupported xpad type: %d\n",
@@ -841,7 +860,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
static int xpad_init_ff(struct usb_xpad *xpad)
{
- if (xpad->xtype == XTYPE_UNKNOWN || xpad->xtype == XTYPE_XBOXONE)
+ if (xpad->xtype == XTYPE_UNKNOWN)
return 0;
input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a89ba7cb96f1..4cd94fd6cbad 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -367,6 +367,7 @@ config KEYBOARD_MAPLE
config KEYBOARD_MAX7359
tristate "Maxim MAX7359 Key Switch Controller"
+ select INPUT_MATRIXKMAP
depends on I2C
help
If you say yes here you get support for the Maxim MAX7359 Key
@@ -588,6 +589,16 @@ config KEYBOARD_DAVINCI
To compile this driver as a module, choose M here: the
module will be called davinci_keyscan.
+config KEYBOARD_IPAQ_MICRO
+ tristate "Buttons on Micro SoC (iPaq h3100,h3600,h3700)"
+ depends on MFD_IPAQ_MICRO
+ help
+ Say Y to enable support for the buttons attached to
+ Micro peripheral controller on iPAQ h3100/h3600/h3700
+
+ To compile this driver as a module, choose M here: the
+ module will be called ipaq-micro-keys.
+
config KEYBOARD_OMAP
tristate "TI OMAP keypad support"
depends on ARCH_OMAP1
@@ -667,7 +678,7 @@ config KEYBOARD_W90P910
config KEYBOARD_CROS_EC
tristate "ChromeOS EC keyboard"
select INPUT_MATRIXKMAP
- depends on MFD_CROS_EC
+ depends on CROS_EC_PROTO
help
Say Y here to enable the matrix keyboard used by ChromeOS devices
and implemented on the ChromeOS EC. You must enable one bus option
@@ -686,4 +697,15 @@ config KEYBOARD_CAP11XX
To compile this driver as a module, choose M here: the
module will be called cap11xx.
+config KEYBOARD_BCM
+ tristate "Broadcom keypad driver"
+ depends on OF && HAVE_CLK
+ select INPUT_MATRIXKMAP
+ default ARCH_BCM_CYGNUS
+ help
+ Say Y here if you want to use Broadcom keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm-keypad.
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 470767884bd8..df28d5553c05 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
+obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
@@ -23,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o
obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
index a45267729dfc..6ed83cf8b74e 100644
--- a/drivers/input/keyboard/adp5589-keys.c
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -180,7 +180,7 @@
#define LOGIC2_STAT (1 << 7) /* ADP5589 only */
#define LOGIC1_STAT (1 << 6)
#define LOCK_STAT (1 << 5) /* ADP5589 only */
-#define KEC 0xF
+#define KEC 0x1F
/* PIN_CONFIG_D Register */
#define C4_EXTEND_CFG (1 << 6) /* RESET2 */
@@ -726,7 +726,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad)
pull_mask |= val << (2 * (i & 0x3));
- if (i == 3 || i == kpad->var->max_row_num) {
+ if (i % 4 == 3 || i == kpad->var->max_row_num) {
ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A)
+ (i >> 2), pull_mask);
pull_mask = 0;
@@ -746,7 +746,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad)
pull_mask |= val << (2 * (i & 0x3));
- if (i == 3 || i == kpad->var->max_col_num) {
+ if (i % 4 == 3 || i == kpad->var->max_col_num) {
ret |= adp5589_write(client,
reg(ADP5585_RPULL_CONFIG_C) +
(i >> 2), pull_mask);
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 387c51f4b4e4..ec876b5b1382 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -1653,6 +1653,12 @@ static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id)
return 1;
}
+/*
+ * NOTE: do not add any more "force release" quirks to this table. The
+ * task of adjusting list of keys that should be "released" automatically
+ * by the driver is now delegated to userspace tools, such as udev, so
+ * submit such quirks there.
+ */
static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
{
.matches = {
diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c
new file mode 100644
index 000000000000..86a8b723ae15
--- /dev/null
+++ b/drivers/input/keyboard/bcm-keypad.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/gfp.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+
+#define DEFAULT_CLK_HZ 31250
+#define MAX_ROWS 8
+#define MAX_COLS 8
+
+/* Register/field definitions */
+#define KPCR_OFFSET 0x00000080
+#define KPCR_MODE 0x00000002
+#define KPCR_MODE_SHIFT 1
+#define KPCR_MODE_MASK 1
+#define KPCR_ENABLE 0x00000001
+#define KPCR_STATUSFILTERENABLE 0x00008000
+#define KPCR_STATUSFILTERTYPE_SHIFT 12
+#define KPCR_COLFILTERENABLE 0x00000800
+#define KPCR_COLFILTERTYPE_SHIFT 8
+#define KPCR_ROWWIDTH_SHIFT 20
+#define KPCR_COLUMNWIDTH_SHIFT 16
+
+#define KPIOR_OFFSET 0x00000084
+#define KPIOR_ROWOCONTRL_SHIFT 24
+#define KPIOR_ROWOCONTRL_MASK 0xFF000000
+#define KPIOR_COLUMNOCONTRL_SHIFT 16
+#define KPIOR_COLUMNOCONTRL_MASK 0x00FF0000
+#define KPIOR_COLUMN_IO_DATA_SHIFT 0
+
+#define KPEMR0_OFFSET 0x00000090
+#define KPEMR1_OFFSET 0x00000094
+#define KPEMR2_OFFSET 0x00000098
+#define KPEMR3_OFFSET 0x0000009C
+#define KPEMR_EDGETYPE_BOTH 3
+
+#define KPSSR0_OFFSET 0x000000A0
+#define KPSSR1_OFFSET 0x000000A4
+#define KPSSRN_OFFSET(reg_n) (KPSSR0_OFFSET + 4 * (reg_n))
+#define KPIMR0_OFFSET 0x000000B0
+#define KPIMR1_OFFSET 0x000000B4
+#define KPICR0_OFFSET 0x000000B8
+#define KPICR1_OFFSET 0x000000BC
+#define KPICRN_OFFSET(reg_n) (KPICR0_OFFSET + 4 * (reg_n))
+#define KPISR0_OFFSET 0x000000C0
+#define KPISR1_OFFSET 0x000000C4
+
+#define KPCR_STATUSFILTERTYPE_MAX 7
+#define KPCR_COLFILTERTYPE_MAX 7
+
+/* Macros to determine the row/column from a bit that is set in SSR0/1. */
+#define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n))
+#define BIT_TO_COL(bit_nr) ((bit_nr) % 8)
+
+/* Structure representing various run-time entities */
+struct bcm_kp {
+ void __iomem *base;
+ int irq;
+ struct clk *clk;
+ struct input_dev *input_dev;
+ unsigned long last_state[2];
+ unsigned int n_rows;
+ unsigned int n_cols;
+ u32 kpcr;
+ u32 kpior;
+ u32 kpemr;
+ u32 imr0_val;
+ u32 imr1_val;
+};
+
+/*
+ * Returns the keycode from the input device keymap given the row and
+ * column.
+ */
+static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col)
+{
+ unsigned int row_shift = get_count_order(kp->n_cols);
+ unsigned short *keymap = kp->input_dev->keycode;
+
+ return keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
+}
+
+static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode)
+{
+ unsigned long state, change;
+ int bit_nr;
+ int key_press;
+ int row, col;
+ unsigned int keycode;
+
+ /* Clear interrupts */
+ writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num));
+
+ state = readl(kp->base + KPSSRN_OFFSET(reg_num));
+ change = kp->last_state[reg_num] ^ state;
+ kp->last_state[reg_num] = state;
+
+ for_each_set_bit(bit_nr, &change, BITS_PER_LONG) {
+ key_press = state & BIT(bit_nr);
+ /* The meaning of SSR register depends on pull mode. */
+ key_press = pull_mode ? !key_press : key_press;
+ row = BIT_TO_ROW_SSRN(bit_nr, reg_num);
+ col = BIT_TO_COL(bit_nr);
+ keycode = bcm_kp_get_keycode(kp, row, col);
+ input_report_key(kp->input_dev, keycode, key_press);
+ }
+}
+
+static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id)
+{
+ struct bcm_kp *kp = dev_id;
+ int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK;
+ int reg_num;
+
+ for (reg_num = 0; reg_num <= 1; reg_num++)
+ bcm_kp_report_keys(kp, reg_num, pull_mode);
+
+ input_sync(kp->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int bcm_kp_start(struct bcm_kp *kp)
+{
+ int error;
+
+ if (kp->clk) {
+ error = clk_prepare_enable(kp->clk);
+ if (error)
+ return error;
+ }
+
+ writel(kp->kpior, kp->base + KPIOR_OFFSET);
+
+ writel(kp->imr0_val, kp->base + KPIMR0_OFFSET);
+ writel(kp->imr1_val, kp->base + KPIMR1_OFFSET);
+
+ writel(kp->kpemr, kp->base + KPEMR0_OFFSET);
+ writel(kp->kpemr, kp->base + KPEMR1_OFFSET);
+ writel(kp->kpemr, kp->base + KPEMR2_OFFSET);
+ writel(kp->kpemr, kp->base + KPEMR3_OFFSET);
+
+ writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
+ writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
+
+ kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET);
+ kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET);
+
+ writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET);
+
+ return 0;
+}
+
+static void bcm_kp_stop(const struct bcm_kp *kp)
+{
+ u32 val;
+
+ val = readl(kp->base + KPCR_OFFSET);
+ val &= ~KPCR_ENABLE;
+ writel(0, kp->base + KPCR_OFFSET);
+ writel(0, kp->base + KPIMR0_OFFSET);
+ writel(0, kp->base + KPIMR1_OFFSET);
+ writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
+ writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
+
+ if (kp->clk)
+ clk_disable_unprepare(kp->clk);
+}
+
+static int bcm_kp_open(struct input_dev *dev)
+{
+ struct bcm_kp *kp = input_get_drvdata(dev);
+
+ return bcm_kp_start(kp);
+}
+
+static void bcm_kp_close(struct input_dev *dev)
+{
+ struct bcm_kp *kp = input_get_drvdata(dev);
+
+ bcm_kp_stop(kp);
+}
+
+static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp)
+{
+ struct device *dev = kp->input_dev->dev.parent;
+ struct device_node *np = dev->of_node;
+ int error;
+ unsigned int dt_val;
+ unsigned int i;
+ unsigned int num_rows, col_mask, rows_set;
+
+ /* Initialize the KPCR Keypad Configuration Register */
+ kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE;
+
+ error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols);
+ if (error) {
+ dev_err(dev, "failed to parse kp params\n");
+ return error;
+ }
+
+ /* Set row width for the ASIC block. */
+ kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT;
+
+ /* Set column width for the ASIC block. */
+ kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT;
+
+ /* Configure the IMR registers */
+
+ /*
+ * IMR registers contain interrupt enable bits for 8x8 matrix
+ * IMR0 register format: <row3> <row2> <row1> <row0>
+ * IMR1 register format: <row7> <row6> <row5> <row4>
+ */
+ col_mask = (1 << (kp->n_cols)) - 1;
+ num_rows = kp->n_rows;
+
+ /* Set column bits in rows 0 to 3 in IMR0 */
+ kp->imr0_val = col_mask;
+
+ rows_set = 1;
+ while (--num_rows && rows_set++ < 4)
+ kp->imr0_val |= kp->imr0_val << MAX_COLS;
+
+ /* Set column bits in rows 4 to 7 in IMR1 */
+ kp->imr1_val = 0;
+ if (num_rows) {
+ kp->imr1_val = col_mask;
+ while (--num_rows)
+ kp->imr1_val |= kp->imr1_val << MAX_COLS;
+ }
+
+ /* Initialize the KPEMR Keypress Edge Mode Registers */
+ /* Trigger on both edges */
+ kp->kpemr = 0;
+ for (i = 0; i <= 30; i += 2)
+ kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i);
+
+ /*
+ * Obtain the Status filter debounce value and verify against the
+ * possible values specified in the DT binding.
+ */
+ of_property_read_u32(np, "status-debounce-filter-period", &dt_val);
+
+ if (dt_val > KPCR_STATUSFILTERTYPE_MAX) {
+ dev_err(dev, "Invalid Status filter debounce value %d\n",
+ dt_val);
+ return -EINVAL;
+ }
+
+ kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT;
+
+ /*
+ * Obtain the Column filter debounce value and verify against the
+ * possible values specified in the DT binding.
+ */
+ of_property_read_u32(np, "col-debounce-filter-period", &dt_val);
+
+ if (dt_val > KPCR_COLFILTERTYPE_MAX) {
+ dev_err(dev, "Invalid Column filter debounce value %d\n",
+ dt_val);
+ return -EINVAL;
+ }
+
+ kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT;
+
+ /*
+ * Determine between the row and column,
+ * which should be configured as output.
+ */
+ if (of_property_read_bool(np, "row-output-enabled")) {
+ /*
+ * Set RowOContrl or ColumnOContrl in KPIOR
+ * to the number of pins to drive as outputs
+ */
+ kp->kpior = ((1 << kp->n_rows) - 1) <<
+ KPIOR_ROWOCONTRL_SHIFT;
+ } else {
+ kp->kpior = ((1 << kp->n_cols) - 1) <<
+ KPIOR_COLUMNOCONTRL_SHIFT;
+ }
+
+ /*
+ * Determine if the scan pull up needs to be enabled
+ */
+ if (of_property_read_bool(np, "pull-up-enabled"))
+ kp->kpcr |= KPCR_MODE;
+
+ dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n",
+ kp->n_rows, kp->n_cols,
+ kp->kpcr, kp->kpior, kp->kpemr);
+
+ return 0;
+}
+
+
+static int bcm_kp_probe(struct platform_device *pdev)
+{
+ struct bcm_kp *kp;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int error;
+
+ kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
+ if (!kp)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev) {
+ dev_err(&pdev->dev, "failed to allocate the input device\n");
+ return -ENOMEM;
+ }
+
+ __set_bit(EV_KEY, input_dev->evbit);
+
+ /* Enable auto repeat feature of Linux input subsystem */
+ if (of_property_read_bool(pdev->dev.of_node, "autorepeat"))
+ __set_bit(EV_REP, input_dev->evbit);
+
+ input_dev->name = pdev->name;
+ input_dev->phys = "keypad/input0";
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = bcm_kp_open;
+ input_dev->close = bcm_kp_close;
+
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ input_set_drvdata(input_dev, kp);
+
+ kp->input_dev = input_dev;
+
+ platform_set_drvdata(pdev, kp);
+
+ error = bcm_kp_matrix_key_parse_dt(kp);
+ if (error)
+ return error;
+
+ error = matrix_keypad_build_keymap(NULL, NULL,
+ kp->n_rows, kp->n_cols,
+ NULL, input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to build keymap\n");
+ return error;
+ }
+
+ /* Get the KEYPAD base address */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing keypad base address resource\n");
+ return -ENODEV;
+ }
+
+ kp->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(kp->base))
+ return PTR_ERR(kp->base);
+
+ /* Enable clock */
+ kp->clk = devm_clk_get(&pdev->dev, "peri_clk");
+ if (IS_ERR(kp->clk)) {
+ error = PTR_ERR(kp->clk);
+ if (error != -ENOENT) {
+ if (error != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get clock\n");
+ return error;
+ }
+ dev_dbg(&pdev->dev,
+ "No clock specified. Assuming it's enabled\n");
+ kp->clk = NULL;
+ } else {
+ unsigned int desired_rate;
+ long actual_rate;
+
+ error = of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &desired_rate);
+ if (error < 0)
+ desired_rate = DEFAULT_CLK_HZ;
+
+ actual_rate = clk_round_rate(kp->clk, desired_rate);
+ if (actual_rate <= 0)
+ return -EINVAL;
+
+ error = clk_set_rate(kp->clk, actual_rate);
+ if (error)
+ return error;
+
+ error = clk_prepare_enable(kp->clk);
+ if (error)
+ return error;
+ }
+
+ /* Put the kp into a known sane state */
+ bcm_kp_stop(kp);
+
+ kp->irq = platform_get_irq(pdev, 0);
+ if (kp->irq < 0) {
+ dev_err(&pdev->dev, "no IRQ specified\n");
+ return -EINVAL;
+ }
+
+ error = devm_request_threaded_irq(&pdev->dev, kp->irq,
+ NULL, bcm_kp_isr_thread,
+ IRQF_ONESHOT, pdev->name, kp);
+ if (error) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return error;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id bcm_kp_of_match[] = {
+ { .compatible = "brcm,bcm-keypad" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bcm_kp_of_match);
+
+static struct platform_driver bcm_kp_device_driver = {
+ .probe = bcm_kp_probe,
+ .driver = {
+ .name = "bcm-keypad",
+ .of_match_table = of_match_ptr(bcm_kp_of_match),
+ }
+};
+
+module_platform_driver(bcm_kp_device_driver);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("BCM Keypad Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c
index 27ef29f8fe6a..b637f1af842e 100644
--- a/drivers/input/keyboard/clps711x-keypad.c
+++ b/drivers/input/keyboard/clps711x-keypad.c
@@ -120,14 +120,9 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
for (i = 0; i < priv->row_count; i++) {
struct clps711x_gpio_data *data = &priv->gpio_data[i];
- data->desc = devm_gpiod_get_index(dev, "row", i);
- if (!data->desc)
- return -EINVAL;
-
+ data->desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN);
if (IS_ERR(data->desc))
return PTR_ERR(data->desc);
-
- gpiod_direction_input(data->desc);
}
err = of_property_read_u32(np, "poll-interval", &poll_interval);
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index ffa989f2c785..b01966dc7eb3 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -148,16 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
{
- struct cros_ec_command msg = {
- .version = 0,
- .command = EC_CMD_MKBP_STATE,
- .outdata = NULL,
- .outsize = 0,
- .indata = kb_state,
- .insize = ckdev->cols,
- };
-
- return cros_ec_cmd_xfer(ckdev->ec, &msg);
+ int ret = 0;
+ struct cros_ec_command *msg;
+
+ msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_MKBP_STATE;
+ msg->insize = ckdev->cols;
+ msg->outsize = 0;
+
+ ret = cros_ec_cmd_xfer(ckdev->ec, msg);
+ if (ret < 0) {
+ dev_err(ckdev->dev, "Error transferring EC message %d\n", ret);
+ goto exit;
+ }
+
+ memcpy(kb_state, msg->data, ckdev->cols);
+exit:
+ kfree(msg);
+ return ret;
}
static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
@@ -263,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
ckdev->dev = dev;
dev_set_drvdata(&pdev->dev, ckdev);
- idev->name = ec->ec_name;
+ idev->name = CROS_EC_DEV_NAME;
idev->phys = ec->phys_name;
__set_bit(EV_REP, idev->evbit);
@@ -338,7 +350,7 @@ static int cros_ec_keyb_resume(struct device *dev)
* wake source (e.g. the lid is open and the user might press a key to
* wake) then the key scan buffer should be preserved.
*/
- if (ckdev->ec->was_wake_device)
+ if (!ckdev->ec->was_wake_device)
cros_ec_keyb_clear_keyboard(ckdev);
return 0;
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index 90df4df58b07..097d7216d98e 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -125,7 +125,7 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
device_for_each_child_node(dev, child) {
struct gpio_desc *desc;
- desc = devm_get_gpiod_from_child(dev, child);
+ desc = devm_get_gpiod_from_child(dev, NULL, child);
if (IS_ERR(desc)) {
error = PTR_ERR(desc);
if (error != -EPROBE_DEFER)
diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c
new file mode 100644
index 000000000000..602900d1f937
--- /dev/null
+++ b/drivers/input/keyboard/ipaq-micro-keys.c
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ *
+ * h3600 atmel micro companion support, key subdevice
+ * based on previous kernel 2.4 version
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ipaq-micro.h>
+
+struct ipaq_micro_keys {
+ struct ipaq_micro *micro;
+ struct input_dev *input;
+ u16 *codes;
+};
+
+static const u16 micro_keycodes[] = {
+ KEY_RECORD, /* 1: Record button */
+ KEY_CALENDAR, /* 2: Calendar */
+ KEY_ADDRESSBOOK, /* 3: Contacts (looks like Outlook) */
+ KEY_MAIL, /* 4: Envelope (Q on older iPAQs) */
+ KEY_HOMEPAGE, /* 5: Start (looks like swoopy arrow) */
+ KEY_UP, /* 6: Up */
+ KEY_RIGHT, /* 7: Right */
+ KEY_LEFT, /* 8: Left */
+ KEY_DOWN, /* 9: Down */
+};
+
+static void micro_key_receive(void *data, int len, unsigned char *msg)
+{
+ struct ipaq_micro_keys *keys = data;
+ int key, down;
+
+ down = 0x80 & msg[0];
+ key = 0x7f & msg[0];
+
+ if (key < ARRAY_SIZE(micro_keycodes)) {
+ input_report_key(keys->input, keys->codes[key], down);
+ input_sync(keys->input);
+ }
+}
+
+static void micro_key_start(struct ipaq_micro_keys *keys)
+{
+ spin_lock(&keys->micro->lock);
+ keys->micro->key = micro_key_receive;
+ keys->micro->key_data = keys;
+ spin_unlock(&keys->micro->lock);
+}
+
+static void micro_key_stop(struct ipaq_micro_keys *keys)
+{
+ spin_lock(&keys->micro->lock);
+ keys->micro->key = NULL;
+ keys->micro->key_data = NULL;
+ spin_unlock(&keys->micro->lock);
+}
+
+static int micro_key_open(struct input_dev *input)
+{
+ struct ipaq_micro_keys *keys = input_get_drvdata(input);
+
+ micro_key_start(keys);
+
+ return 0;
+}
+
+static void micro_key_close(struct input_dev *input)
+{
+ struct ipaq_micro_keys *keys = input_get_drvdata(input);
+
+ micro_key_stop(keys);
+}
+
+static int micro_key_probe(struct platform_device *pdev)
+{
+ struct ipaq_micro_keys *keys;
+ int error;
+ int i;
+
+ keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
+ if (!keys)
+ return -ENOMEM;
+
+ keys->micro = dev_get_drvdata(pdev->dev.parent);
+
+ keys->input = devm_input_allocate_device(&pdev->dev);
+ if (!keys->input)
+ return -ENOMEM;
+
+ keys->input->keycodesize = sizeof(micro_keycodes[0]);
+ keys->input->keycodemax = ARRAY_SIZE(micro_keycodes);
+ keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes,
+ keys->input->keycodesize * keys->input->keycodemax,
+ GFP_KERNEL);
+ keys->input->keycode = keys->codes;
+
+ __set_bit(EV_KEY, keys->input->evbit);
+ for (i = 0; i < ARRAY_SIZE(micro_keycodes); i++)
+ __set_bit(micro_keycodes[i], keys->input->keybit);
+
+ keys->input->name = "h3600 micro keys";
+ keys->input->open = micro_key_open;
+ keys->input->close = micro_key_close;
+ input_set_drvdata(keys->input, keys);
+
+ error = input_register_device(keys->input);
+ if (error)
+ return error;
+
+ platform_set_drvdata(pdev, keys);
+ return 0;
+}
+
+static int __maybe_unused micro_key_suspend(struct device *dev)
+{
+ struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
+
+ micro_key_stop(keys);
+
+ return 0;
+}
+
+static int __maybe_unused micro_key_resume(struct device *dev)
+{
+ struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
+ struct input_dev *input = keys->input;
+
+ mutex_lock(&input->mutex);
+
+ if (input->users)
+ micro_key_start(keys);
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(micro_key_dev_pm_ops,
+ micro_key_suspend, micro_key_resume);
+
+static struct platform_driver micro_key_device_driver = {
+ .driver = {
+ .name = "ipaq-micro-keys",
+ .pm = &micro_key_dev_pm_ops,
+ },
+ .probe = micro_key_probe,
+};
+module_platform_driver(micro_key_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro keys");
+MODULE_ALIAS("platform:ipaq-micro-keys");
diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c
index 9081cbef11ea..0ad422b8a260 100644
--- a/drivers/input/keyboard/lm8333.c
+++ b/drivers/input/keyboard/lm8333.c
@@ -1,6 +1,6 @@
/*
* LM8333 keypad driver
- * Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
+ * Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -231,6 +231,6 @@ static struct i2c_driver lm8333_driver = {
};
module_i2c_driver(lm8333_driver);
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
MODULE_DESCRIPTION("LM8333 keyboard driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c
index faa6da53eba8..5091133b7b8e 100644
--- a/drivers/input/keyboard/max7359_keypad.c
+++ b/drivers/input/keyboard/max7359_keypad.c
@@ -84,26 +84,6 @@ static int max7359_read_reg(struct i2c_client *client, int reg)
return ret;
}
-static void max7359_build_keycode(struct max7359_keypad *keypad,
- const struct matrix_keymap_data *keymap_data)
-{
- struct input_dev *input_dev = keypad->input_dev;
- int i;
-
- for (i = 0; i < keymap_data->keymap_size; i++) {
- unsigned int key = keymap_data->keymap[i];
- unsigned int row = KEY_ROW(key);
- unsigned int col = KEY_COL(key);
- unsigned int scancode = MATRIX_SCAN_CODE(row, col,
- MAX7359_ROW_SHIFT);
- unsigned short keycode = KEY_VAL(key);
-
- keypad->keycodes[scancode] = keycode;
- __set_bit(keycode, input_dev->keybit);
- }
- __clear_bit(KEY_RESERVED, input_dev->keybit);
-}
-
/* runs in an IRQ thread -- can (and will!) sleep */
static irqreturn_t max7359_interrupt(int irq, void *dev_id)
{
@@ -166,7 +146,6 @@ static void max7359_close(struct input_dev *dev)
static void max7359_initialize(struct i2c_client *client)
{
max7359_write_reg(client, MAX7359_REG_CONFIG,
- MAX7359_CFG_INTERRUPT | /* Irq clears after host read */
MAX7359_CFG_KEY_RELEASE | /* Key release enable */
MAX7359_CFG_WAKEUP); /* Key press wakeup enable */
@@ -233,7 +212,15 @@ static int max7359_probe(struct i2c_client *client,
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
- max7359_build_keycode(keypad, keymap_data);
+ error = matrix_keypad_build_keymap(keymap_data, NULL,
+ MAX7359_MAX_KEY_ROWS,
+ MAX7359_MAX_KEY_COLS,
+ keypad->keycodes,
+ input_dev);
+ if (error) {
+ dev_err(&client->dev, "failed to build keymap\n");
+ return error;
+ }
error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
max7359_interrupt,
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c
index 6b9fdf6cf8e8..43e48dac7687 100644
--- a/drivers/input/keyboard/samsung-keypad.c
+++ b/drivers/input/keyboard/samsung-keypad.c
@@ -585,7 +585,7 @@ static const struct of_device_id samsung_keypad_dt_match[] = {
MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
#endif
-static struct platform_device_id samsung_keypad_driver_ids[] = {
+static const struct platform_device_id samsung_keypad_driver_ids[] = {
{
.name = "samsung-keypad",
.driver_data = KEYPAD_TYPE_SAMSUNG,
diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c
index f42a543db043..623d451767e3 100644
--- a/drivers/input/keyboard/spear-keyboard.c
+++ b/drivers/input/keyboard/spear-keyboard.c
@@ -3,7 +3,7 @@
* Based on omap-keypad driver
*
* Copyright (C) 2010 ST Microelectronics
- * Rajeev Kumar<rajeev-dlh.kumar@st.com>
+ * Rajeev Kumar <rajeevkumar.linux@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c
index 563932500ff1..31c606a4dd31 100644
--- a/drivers/input/keyboard/tc3589x-keypad.c
+++ b/drivers/input/keyboard/tc3589x-keypad.c
@@ -70,6 +70,28 @@
#define TC3589x_KBD_INT_CLR 0x1
/**
+ * struct tc35893_keypad_platform_data - platform specific keypad data
+ * @keymap_data: matrix scan code table for keycodes
+ * @krow: mask for available rows, value is 0xFF
+ * @kcol: mask for available columns, value is 0xFF
+ * @debounce_period: platform specific debounce time
+ * @settle_time: platform specific settle down time
+ * @irqtype: type of interrupt, falling or rising edge
+ * @enable_wakeup: specifies if keypad event can wake up system from sleep
+ * @no_autorepeat: flag for auto repetition
+ */
+struct tc3589x_keypad_platform_data {
+ const struct matrix_keymap_data *keymap_data;
+ u8 krow;
+ u8 kcol;
+ u8 debounce_period;
+ u8 settle_time;
+ unsigned long irqtype;
+ bool enable_wakeup;
+ bool no_autorepeat;
+};
+
+/**
* struct tc_keypad - data structure used by keypad driver
* @tc3589x: pointer to tc35893
* @input: pointer to input device object
@@ -296,7 +318,6 @@ static void tc3589x_keypad_close(struct input_dev *input)
tc3589x_keypad_disable(keypad);
}
-#ifdef CONFIG_OF
static const struct tc3589x_keypad_platform_data *
tc3589x_keypad_of_probe(struct device *dev)
{
@@ -346,14 +367,6 @@ tc3589x_keypad_of_probe(struct device *dev)
return plat;
}
-#else
-static inline const struct tc3589x_keypad_platform_data *
-tc3589x_keypad_of_probe(struct device *dev)
-{
- return ERR_PTR(-ENODEV);
-}
-#endif
-
static int tc3589x_keypad_probe(struct platform_device *pdev)
{
@@ -363,13 +376,10 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
const struct tc3589x_keypad_platform_data *plat;
int error, irq;
- plat = tc3589x->pdata->keypad;
- if (!plat) {
- plat = tc3589x_keypad_of_probe(&pdev->dev);
- if (IS_ERR(plat)) {
- dev_err(&pdev->dev, "invalid keypad platform data\n");
- return PTR_ERR(plat);
- }
+ plat = tc3589x_keypad_of_probe(&pdev->dev);
+ if (IS_ERR(plat)) {
+ dev_err(&pdev->dev, "invalid keypad platform data\n");
+ return PTR_ERR(plat);
}
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 6deb8dae3205..d4f0a817e858 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -115,6 +115,18 @@ config INPUT_PCSPKR
To compile this driver as a module, choose M here: the
module will be called pcspkr.
+config INPUT_PM8941_PWRKEY
+ tristate "Qualcomm PM8941 power key support"
+ depends on MFD_SPMI_PMIC
+ help
+ Say Y here if you want support for the power key usually found
+ on boards using a Qualcomm PM8941 compatible PMIC.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pm8941-pwrkey.
+
config INPUT_PM8XXX_VIBRATOR
tristate "Qualcomm PM8XXX vibrator support"
depends on MFD_PM8XXX
@@ -165,6 +177,18 @@ config INPUT_MAX77693_HAPTIC
To compile this driver as module, choose M here: the
module will be called max77693-haptic.
+config INPUT_MAX77843_HAPTIC
+ tristate "MAXIM MAX77843 haptic controller support"
+ depends on MFD_MAX77843 && REGULATOR
+ select INPUT_FF_MEMLESS
+ help
+ This option enables support for the haptic controller on
+ MAXIM MAX77843 chip. The driver supports ff-memless interface
+ from input framework.
+
+ To compile this driver as module, choose M here: the
+ module will be called max77843-haptic.
+
config INPUT_MAX8925_ONKEY
tristate "MAX8925 ONKEY support"
depends on MFD_MAX8925
@@ -586,6 +610,16 @@ config INPUT_DA9055_ONKEY
To compile this driver as a module, choose M here: the module
will be called da9055_onkey.
+config INPUT_DA9063_ONKEY
+ tristate "Dialog DA9063 OnKey"
+ depends on MFD_DA9063
+ help
+ Support the ONKEY of Dialog DA9063 Power Management IC as an
+ input device reporting power button statue.
+
+ To compile this driver as a module, choose M here: the module
+ will be called da9063_onkey.
+
config INPUT_DM355EVM
tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
depends on MFD_DM355EVM_MSP
@@ -751,6 +785,17 @@ config INPUT_DRV260X_HAPTICS
To compile this driver as a module, choose M here: the
module will be called drv260x-haptics.
+config INPUT_DRV2665_HAPTICS
+ tristate "TI DRV2665 haptics support"
+ depends on INPUT && I2C
+ select INPUT_FF_MEMLESS
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the TI DRV2665 haptics driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called drv2665-haptics.
+
config INPUT_DRV2667_HAPTICS
tristate "TI DRV2667 haptics support"
depends on INPUT && I2C
@@ -760,6 +805,6 @@ config INPUT_DRV2667_HAPTICS
Say Y to enable support for the TI DRV2667 haptics driver.
To compile this driver as a module, choose M here: the
- module will be called drv260x-haptics.
+ module will be called drv2667-haptics.
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 403a1a54a76c..53df07dcc23c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -25,9 +25,11 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
+obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
+obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o
obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
@@ -39,6 +41,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o
+obj-$(CONFIG_INPUT_MAX77843_HAPTIC) += max77843-haptic.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
@@ -49,6 +52,7 @@ obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
+obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o
obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
index 470bfd6f0830..bdb5d03b296e 100644
--- a/drivers/input/misc/adxl34x-i2c.c
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -10,6 +10,7 @@
#include <linux/input.h> /* BUS_I2C */
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/types.h>
#include <linux/pm.h>
#include "adxl34x.h"
@@ -135,11 +136,31 @@ static const struct i2c_device_id adxl34x_id[] = {
MODULE_DEVICE_TABLE(i2c, adxl34x_id);
+#ifdef CONFIG_OF
+static const struct of_device_id adxl34x_of_id[] = {
+ /*
+ * The ADXL346 is backward-compatible with the ADXL345. Differences are
+ * handled by runtime detection of the device model, there's thus no
+ * need for listing the "adi,adxl346" compatible value explicitly.
+ */
+ { .compatible = "adi,adxl345", },
+ /*
+ * Deprecated, DT nodes should use one or more of the device-specific
+ * compatible values "adi,adxl345" and "adi,adxl346".
+ */
+ { .compatible = "adi,adxl34x", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, adxl34x_of_id);
+#endif
+
static struct i2c_driver adxl34x_driver = {
.driver = {
.name = "adxl34x",
.owner = THIS_MODULE,
.pm = &adxl34x_i2c_pm,
+ .of_match_table = of_match_ptr(adxl34x_of_id),
},
.probe = adxl34x_i2c_probe,
.remove = adxl34x_i2c_remove,
diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c
new file mode 100644
index 000000000000..f577585ef999
--- /dev/null
+++ b/drivers/input/misc/da9063_onkey.c
@@ -0,0 +1,226 @@
+/*
+ * OnKey device driver for DA9063
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+#include <linux/mfd/da9063/registers.h>
+
+struct da9063_onkey {
+ struct da9063 *hw;
+ struct delayed_work work;
+ struct input_dev *input;
+ struct device *dev;
+ bool key_power;
+};
+
+static void da9063_poll_on(struct work_struct *work)
+{
+ struct da9063_onkey *onkey = container_of(work, struct da9063_onkey,
+ work.work);
+ unsigned int val;
+ int fault_log = 0;
+ bool poll = true;
+ int error;
+
+ /* Poll to see when the pin is released */
+ error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
+ if (error) {
+ dev_err(onkey->dev,
+ "Failed to read ON status: %d\n", error);
+ goto err_poll;
+ }
+
+ if (!(val & DA9063_NONKEY)) {
+ error = regmap_update_bits(onkey->hw->regmap,
+ DA9063_REG_CONTROL_B,
+ DA9063_NONKEY_LOCK, 0);
+ if (error) {
+ dev_err(onkey->dev,
+ "Failed to reset the Key Delay %d\n", error);
+ goto err_poll;
+ }
+
+ input_report_key(onkey->input, KEY_POWER, 0);
+ input_sync(onkey->input);
+
+ poll = false;
+ }
+
+ /*
+ * If the fault log KEY_RESET is detected, then clear it
+ * and shut down the system.
+ */
+ error = regmap_read(onkey->hw->regmap,
+ DA9063_REG_FAULT_LOG, &fault_log);
+ if (error) {
+ dev_warn(&onkey->input->dev,
+ "Cannot read FAULT_LOG: %d\n", error);
+ } else if (fault_log & DA9063_KEY_RESET) {
+ error = regmap_write(onkey->hw->regmap,
+ DA9063_REG_FAULT_LOG,
+ DA9063_KEY_RESET);
+ if (error) {
+ dev_warn(&onkey->input->dev,
+ "Cannot reset KEY_RESET fault log: %d\n",
+ error);
+ } else {
+ /* at this point we do any S/W housekeeping
+ * and then send shutdown command
+ */
+ dev_dbg(&onkey->input->dev,
+ "Sending SHUTDOWN to DA9063 ...\n");
+ error = regmap_write(onkey->hw->regmap,
+ DA9063_REG_CONTROL_F,
+ DA9063_SHUTDOWN);
+ if (error)
+ dev_err(&onkey->input->dev,
+ "Cannot SHUTDOWN DA9063: %d\n",
+ error);
+ }
+ }
+
+err_poll:
+ if (poll)
+ schedule_delayed_work(&onkey->work, msecs_to_jiffies(50));
+}
+
+static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
+{
+ struct da9063_onkey *onkey = data;
+ unsigned int val;
+ int error;
+
+ error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val);
+ if (onkey->key_power && !error && (val & DA9063_NONKEY)) {
+ input_report_key(onkey->input, KEY_POWER, 1);
+ input_sync(onkey->input);
+ schedule_delayed_work(&onkey->work, 0);
+ dev_dbg(onkey->dev, "KEY_POWER pressed.\n");
+ } else {
+ input_report_key(onkey->input, KEY_SLEEP, 1);
+ input_sync(onkey->input);
+ input_report_key(onkey->input, KEY_SLEEP, 0);
+ input_sync(onkey->input);
+ dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void da9063_cancel_poll(void *data)
+{
+ struct da9063_onkey *onkey = data;
+
+ cancel_delayed_work_sync(&onkey->work);
+}
+
+static int da9063_onkey_probe(struct platform_device *pdev)
+{
+ struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
+ struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
+ struct da9063_onkey *onkey;
+ int irq;
+ int error;
+
+ onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey),
+ GFP_KERNEL);
+ if (!onkey) {
+ dev_err(&pdev->dev, "Failed to allocate memory.\n");
+ return -ENOMEM;
+ }
+
+ onkey->dev = &pdev->dev;
+ onkey->hw = da9063;
+
+ if (pdata)
+ onkey->key_power = pdata->key_power;
+ else
+ onkey->key_power =
+ !of_property_read_bool(pdev->dev.of_node,
+ "dlg,disable-key-power");
+
+ onkey->input = devm_input_allocate_device(&pdev->dev);
+ if (!onkey->input) {
+ dev_err(&pdev->dev, "Failed to allocated input device.\n");
+ return -ENOMEM;
+ }
+
+ onkey->input->name = DA9063_DRVNAME_ONKEY;
+ onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0";
+ onkey->input->dev.parent = &pdev->dev;
+
+ if (onkey->key_power)
+ input_set_capability(onkey->input, EV_KEY, KEY_POWER);
+
+ input_set_capability(onkey->input, EV_KEY, KEY_SLEEP);
+
+ INIT_DELAYED_WORK(&onkey->work, da9063_poll_on);
+
+ error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Failed to add cancel poll action: %d\n",
+ error);
+ return error;
+ }
+
+ irq = platform_get_irq_byname(pdev, "ONKEY");
+ if (irq < 0) {
+ error = irq;
+ dev_err(&pdev->dev, "Failed to get platform IRQ: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, da9063_onkey_irq_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "ONKEY", onkey);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Failed to request IRQ %d: %d\n", irq, error);
+ return error;
+ }
+
+ error = input_register_device(onkey->input);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Failed to register input device: %d\n", error);
+ return error;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+ return 0;
+}
+
+static struct platform_driver da9063_onkey_driver = {
+ .probe = da9063_onkey_probe,
+ .driver = {
+ .name = DA9063_DRVNAME_ONKEY,
+ },
+};
+module_platform_driver(da9063_onkey_driver);
+
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY);
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 599578042ea0..e5d60ecd29a4 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -580,15 +580,10 @@ static int drv260x_probe(struct i2c_client *client,
return error;
}
- haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable");
- if (IS_ERR(haptics->enable_gpio)) {
- error = PTR_ERR(haptics->enable_gpio);
- if (error != -ENOENT && error != -ENOSYS)
- return error;
- haptics->enable_gpio = NULL;
- } else {
- gpiod_direction_output(haptics->enable_gpio, 1);
- }
+ haptics->enable_gpio = devm_gpiod_get_optional(&client->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);
if (!haptics->input_dev) {
diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c
new file mode 100644
index 000000000000..0afaa33de07d
--- /dev/null
+++ b/drivers/input/misc/drv2665.c
@@ -0,0 +1,322 @@
+/*
+ * DRV2665 haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright: (C) 2015 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+/* Contol registers */
+#define DRV2665_STATUS 0x00
+#define DRV2665_CTRL_1 0x01
+#define DRV2665_CTRL_2 0x02
+#define DRV2665_FIFO 0x0b
+
+/* Status Register */
+#define DRV2665_FIFO_FULL BIT(0)
+#define DRV2665_FIFO_EMPTY BIT(1)
+
+/* Control 1 Register */
+#define DRV2665_25_VPP_GAIN 0x00
+#define DRV2665_50_VPP_GAIN 0x01
+#define DRV2665_75_VPP_GAIN 0x02
+#define DRV2665_100_VPP_GAIN 0x03
+#define DRV2665_DIGITAL_IN 0xfc
+#define DRV2665_ANALOG_IN BIT(2)
+
+/* Control 2 Register */
+#define DRV2665_BOOST_EN BIT(1)
+#define DRV2665_STANDBY BIT(6)
+#define DRV2665_DEV_RST BIT(7)
+#define DRV2665_5_MS_IDLE_TOUT 0x00
+#define DRV2665_10_MS_IDLE_TOUT 0x04
+#define DRV2665_15_MS_IDLE_TOUT 0x08
+#define DRV2665_20_MS_IDLE_TOUT 0x0c
+
+/**
+ * struct drv2665_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @regulator - Pointer to the regulator for the IC
+ */
+struct drv2665_data {
+ struct input_dev *input_dev;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct work_struct work;
+ struct regulator *regulator;
+};
+
+/* 8kHz Sine wave to stream to the FIFO */
+static const u8 drv2665_sine_wave_form[] = {
+ 0x00, 0x10, 0x20, 0x2e, 0x3c, 0x48, 0x53, 0x5b, 0x61, 0x65, 0x66,
+ 0x65, 0x61, 0x5b, 0x53, 0x48, 0x3c, 0x2e, 0x20, 0x10,
+ 0x00, 0xf0, 0xe0, 0xd2, 0xc4, 0xb8, 0xad, 0xa5, 0x9f, 0x9b, 0x9a,
+ 0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00,
+};
+
+static struct reg_default drv2665_reg_defs[] = {
+ { DRV2665_STATUS, 0x02 },
+ { DRV2665_CTRL_1, 0x28 },
+ { DRV2665_CTRL_2, 0x40 },
+ { DRV2665_FIFO, 0x00 },
+};
+
+static void drv2665_worker(struct work_struct *work)
+{
+ struct drv2665_data *haptics =
+ container_of(work, struct drv2665_data, work);
+ unsigned int read_buf;
+ int error;
+
+ error = regmap_read(haptics->regmap, DRV2665_STATUS, &read_buf);
+ if (error) {
+ dev_err(&haptics->client->dev,
+ "Failed to read status: %d\n", error);
+ return;
+ }
+
+ if (read_buf & DRV2665_FIFO_EMPTY) {
+ error = regmap_bulk_write(haptics->regmap,
+ DRV2665_FIFO,
+ drv2665_sine_wave_form,
+ ARRAY_SIZE(drv2665_sine_wave_form));
+ if (error) {
+ dev_err(&haptics->client->dev,
+ "Failed to write FIFO: %d\n", error);
+ return;
+ }
+ }
+}
+
+static int drv2665_haptics_play(struct input_dev *input, void *data,
+ struct ff_effect *effect)
+{
+ struct drv2665_data *haptics = input_get_drvdata(input);
+
+ schedule_work(&haptics->work);
+
+ return 0;
+}
+
+static void drv2665_close(struct input_dev *input)
+{
+ struct drv2665_data *haptics = input_get_drvdata(input);
+ int error;
+
+ cancel_work_sync(&haptics->work);
+
+ error = regmap_update_bits(haptics->regmap,
+ DRV2665_CTRL_2, DRV2665_STANDBY, 1);
+ if (error)
+ dev_err(&haptics->client->dev,
+ "Failed to enter standby mode: %d\n", error);
+}
+
+static const struct reg_default drv2665_init_regs[] = {
+ { DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT },
+ { DRV2665_CTRL_1, DRV2665_25_VPP_GAIN },
+};
+
+static int drv2665_init(struct drv2665_data *haptics)
+{
+ int error;
+
+ error = regmap_register_patch(haptics->regmap,
+ drv2665_init_regs,
+ ARRAY_SIZE(drv2665_init_regs));
+ if (error) {
+ dev_err(&haptics->client->dev,
+ "Failed to write init registers: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct regmap_config drv2665_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DRV2665_FIFO,
+ .reg_defaults = drv2665_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(drv2665_reg_defs),
+ .cache_type = REGCACHE_NONE,
+};
+
+static int drv2665_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct drv2665_data *haptics;
+ int error;
+
+ haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+ if (!haptics)
+ return -ENOMEM;
+
+ haptics->regulator = devm_regulator_get(&client->dev, "vbat");
+ if (IS_ERR(haptics->regulator)) {
+ error = PTR_ERR(haptics->regulator);
+ dev_err(&client->dev,
+ "unable to get regulator, error: %d\n", error);
+ return error;
+ }
+
+ haptics->input_dev = devm_input_allocate_device(&client->dev);
+ if (!haptics->input_dev) {
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ haptics->input_dev->name = "drv2665:haptics";
+ haptics->input_dev->dev.parent = client->dev.parent;
+ haptics->input_dev->close = drv2665_close;
+ input_set_drvdata(haptics->input_dev, haptics);
+ input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+ error = input_ff_create_memless(haptics->input_dev, NULL,
+ drv2665_haptics_play);
+ if (error) {
+ dev_err(&client->dev, "input_ff_create() failed: %d\n",
+ error);
+ return error;
+ }
+
+ INIT_WORK(&haptics->work, drv2665_worker);
+
+ haptics->client = client;
+ i2c_set_clientdata(client, haptics);
+
+ haptics->regmap = devm_regmap_init_i2c(client, &drv2665_regmap_config);
+ if (IS_ERR(haptics->regmap)) {
+ error = PTR_ERR(haptics->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ error = drv2665_init(haptics);
+ if (error) {
+ dev_err(&client->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);
+ return error;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused drv2665_suspend(struct device *dev)
+{
+ struct drv2665_data *haptics = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&haptics->input_dev->mutex);
+
+ if (haptics->input_dev->users) {
+ ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
+ DRV2665_STANDBY, 1);
+ if (ret) {
+ dev_err(dev, "Failed to set standby mode\n");
+ regulator_disable(haptics->regulator);
+ goto out;
+ }
+
+ ret = regulator_disable(haptics->regulator);
+ if (ret) {
+ dev_err(dev, "Failed to disable regulator\n");
+ regmap_update_bits(haptics->regmap,
+ DRV2665_CTRL_2,
+ DRV2665_STANDBY, 0);
+ }
+ }
+out:
+ mutex_unlock(&haptics->input_dev->mutex);
+ return ret;
+}
+
+static int __maybe_unused drv2665_resume(struct device *dev)
+{
+ struct drv2665_data *haptics = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&haptics->input_dev->mutex);
+
+ if (haptics->input_dev->users) {
+ ret = regulator_enable(haptics->regulator);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator\n");
+ goto out;
+ }
+
+ ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
+ DRV2665_STANDBY, 0);
+ if (ret) {
+ dev_err(dev, "Failed to unset standby mode\n");
+ regulator_disable(haptics->regulator);
+ goto out;
+ }
+
+ }
+
+out:
+ mutex_unlock(&haptics->input_dev->mutex);
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume);
+
+static const struct i2c_device_id drv2665_id[] = {
+ { "drv2665", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, drv2665_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv2665_of_match[] = {
+ { .compatible = "ti,drv2665", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, drv2665_of_match);
+#endif
+
+static struct i2c_driver drv2665_driver = {
+ .probe = drv2665_probe,
+ .driver = {
+ .name = "drv2665-haptics",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(drv2665_of_match),
+ .pm = &drv2665_pm_ops,
+ },
+ .id_table = drv2665_id,
+};
+module_i2c_driver(drv2665_driver);
+
+MODULE_DESCRIPTION("TI DRV2665 haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/drivers/input/misc/gpio-beeper.c b/drivers/input/misc/gpio-beeper.c
index 4817c5f0c3e4..16272fffeb7e 100644
--- a/drivers/input/misc/gpio-beeper.c
+++ b/drivers/input/misc/gpio-beeper.c
@@ -66,13 +66,12 @@ static int gpio_beeper_probe(struct platform_device *pdev)
{
struct gpio_beeper *beep;
struct input_dev *input;
- int err;
beep = devm_kzalloc(&pdev->dev, sizeof(*beep), GFP_KERNEL);
if (!beep)
return -ENOMEM;
- beep->desc = devm_gpiod_get(&pdev->dev, NULL);
+ beep->desc = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
if (IS_ERR(beep->desc))
return PTR_ERR(beep->desc);
@@ -92,10 +91,6 @@ static int gpio_beeper_probe(struct platform_device *pdev)
input_set_capability(input, EV_SND, SND_BELL);
- err = gpiod_direction_output(beep->desc, 0);
- if (err)
- return err;
-
input_set_drvdata(input, beep);
return input_register_device(input);
diff --git a/drivers/input/misc/max77843-haptic.c b/drivers/input/misc/max77843-haptic.c
new file mode 100644
index 000000000000..dccbb465a055
--- /dev/null
+++ b/drivers/input/misc/max77843-haptic.c
@@ -0,0 +1,358 @@
+/*
+ * MAXIM MAX77693 Haptic device driver
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ * Author: Jaewon Kim <jaewon02.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/mfd/max77843-private.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define MAX_MAGNITUDE_SHIFT 16
+
+enum max77843_haptic_motor_type {
+ MAX77843_HAPTIC_ERM = 0,
+ MAX77843_HAPTIC_LRA,
+};
+
+enum max77843_haptic_pwm_divisor {
+ MAX77843_HAPTIC_PWM_DIVISOR_32 = 0,
+ MAX77843_HAPTIC_PWM_DIVISOR_64,
+ MAX77843_HAPTIC_PWM_DIVISOR_128,
+ MAX77843_HAPTIC_PWM_DIVISOR_256,
+};
+
+struct max77843_haptic {
+ struct regmap *regmap_haptic;
+ struct device *dev;
+ struct input_dev *input_dev;
+ struct pwm_device *pwm_dev;
+ struct regulator *motor_reg;
+ struct work_struct work;
+ struct mutex mutex;
+
+ unsigned int magnitude;
+ unsigned int pwm_duty;
+
+ bool active;
+ bool suspended;
+
+ enum max77843_haptic_motor_type type;
+ enum max77843_haptic_pwm_divisor pwm_divisor;
+};
+
+static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic)
+{
+ int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
+ int error;
+
+ error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
+ if (error) {
+ dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on)
+{
+ int error;
+
+ error = regmap_update_bits(haptic->regmap_haptic,
+ MAX77843_SYS_REG_MAINCTRL1,
+ MAX77843_MAINCTRL1_BIASEN_MASK,
+ on << MAINCTRL1_BIASEN_SHIFT);
+ if (error) {
+ dev_err(haptic->dev, "failed to %s bias: %d\n",
+ on ? "enable" : "disable", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable)
+{
+ unsigned int value;
+ int error;
+
+ value = (haptic->type << MCONFIG_MODE_SHIFT) |
+ (enable << MCONFIG_MEN_SHIFT) |
+ (haptic->pwm_divisor << MCONFIG_PDIV_SHIFT);
+
+ error = regmap_write(haptic->regmap_haptic,
+ MAX77843_HAP_REG_MCONFIG, value);
+ if (error) {
+ dev_err(haptic->dev,
+ "failed to update haptic config: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int max77843_haptic_enable(struct max77843_haptic *haptic)
+{
+ int error;
+
+ if (haptic->active)
+ return 0;
+
+ error = pwm_enable(haptic->pwm_dev);
+ if (error) {
+ dev_err(haptic->dev,
+ "failed to enable pwm device: %d\n", error);
+ return error;
+ }
+
+ error = max77843_haptic_config(haptic, true);
+ if (error)
+ goto err_config;
+
+ haptic->active = true;
+
+ return 0;
+
+err_config:
+ pwm_disable(haptic->pwm_dev);
+
+ return error;
+}
+
+static int max77843_haptic_disable(struct max77843_haptic *haptic)
+{
+ int error;
+
+ if (!haptic->active)
+ return 0;
+
+ error = max77843_haptic_config(haptic, false);
+ if (error)
+ return error;
+
+ pwm_disable(haptic->pwm_dev);
+
+ haptic->active = false;
+
+ return 0;
+}
+
+static void max77843_haptic_play_work(struct work_struct *work)
+{
+ struct max77843_haptic *haptic =
+ container_of(work, struct max77843_haptic, work);
+ int error;
+
+ mutex_lock(&haptic->mutex);
+
+ if (haptic->suspended)
+ goto out_unlock;
+
+ if (haptic->magnitude) {
+ error = max77843_haptic_set_duty_cycle(haptic);
+ if (error) {
+ dev_err(haptic->dev,
+ "failed to set duty cycle: %d\n", error);
+ goto out_unlock;
+ }
+
+ error = max77843_haptic_enable(haptic);
+ if (error)
+ dev_err(haptic->dev,
+ "cannot enable haptic: %d\n", error);
+ } else {
+ error = max77843_haptic_disable(haptic);
+ if (error)
+ dev_err(haptic->dev,
+ "cannot disable haptic: %d\n", error);
+ }
+
+out_unlock:
+ mutex_unlock(&haptic->mutex);
+}
+
+static int max77843_haptic_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct max77843_haptic *haptic = input_get_drvdata(dev);
+ u64 period_mag_multi;
+
+ haptic->magnitude = effect->u.rumble.strong_magnitude;
+ if (!haptic->magnitude)
+ haptic->magnitude = effect->u.rumble.weak_magnitude;
+
+ period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
+ haptic->pwm_duty = (unsigned int)(period_mag_multi >>
+ MAX_MAGNITUDE_SHIFT);
+
+ schedule_work(&haptic->work);
+
+ return 0;
+}
+
+static int max77843_haptic_open(struct input_dev *dev)
+{
+ struct max77843_haptic *haptic = input_get_drvdata(dev);
+ int error;
+
+ error = max77843_haptic_bias(haptic, true);
+ if (error)
+ return error;
+
+ error = regulator_enable(haptic->motor_reg);
+ if (error) {
+ dev_err(haptic->dev,
+ "failed to enable regulator: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void max77843_haptic_close(struct input_dev *dev)
+{
+ struct max77843_haptic *haptic = input_get_drvdata(dev);
+ int error;
+
+ cancel_work_sync(&haptic->work);
+ max77843_haptic_disable(haptic);
+
+ error = regulator_disable(haptic->motor_reg);
+ if (error)
+ dev_err(haptic->dev,
+ "failed to disable regulator: %d\n", error);
+
+ max77843_haptic_bias(haptic, false);
+}
+
+static int max77843_haptic_probe(struct platform_device *pdev)
+{
+ struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent);
+ struct max77843_haptic *haptic;
+ int error;
+
+ haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
+ if (!haptic)
+ return -ENOMEM;
+
+ haptic->regmap_haptic = max77843->regmap;
+ haptic->dev = &pdev->dev;
+ haptic->type = MAX77843_HAPTIC_LRA;
+ haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128;
+
+ INIT_WORK(&haptic->work, max77843_haptic_play_work);
+ mutex_init(&haptic->mutex);
+
+ haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL);
+ if (IS_ERR(haptic->pwm_dev)) {
+ dev_err(&pdev->dev, "failed to get pwm device\n");
+ return PTR_ERR(haptic->pwm_dev);
+ }
+
+ haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic");
+ if (IS_ERR(haptic->motor_reg)) {
+ dev_err(&pdev->dev, "failed to get regulator\n");
+ return PTR_ERR(haptic->motor_reg);
+ }
+
+ haptic->input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!haptic->input_dev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ haptic->input_dev->name = "max77843-haptic";
+ haptic->input_dev->id.version = 1;
+ haptic->input_dev->dev.parent = &pdev->dev;
+ haptic->input_dev->open = max77843_haptic_open;
+ haptic->input_dev->close = max77843_haptic_close;
+ input_set_drvdata(haptic->input_dev, haptic);
+ input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
+
+ error = input_ff_create_memless(haptic->input_dev, NULL,
+ max77843_haptic_play_effect);
+ if (error) {
+ dev_err(&pdev->dev, "failed to create force-feedback\n");
+ return error;
+ }
+
+ error = input_register_device(haptic->input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ return error;
+ }
+
+ platform_set_drvdata(pdev, haptic);
+
+ return 0;
+}
+
+static int __maybe_unused max77843_haptic_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max77843_haptic *haptic = platform_get_drvdata(pdev);
+ int error;
+
+ error = mutex_lock_interruptible(&haptic->mutex);
+ if (error)
+ return error;
+
+ max77843_haptic_disable(haptic);
+
+ haptic->suspended = true;
+
+ mutex_unlock(&haptic->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused max77843_haptic_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max77843_haptic *haptic = platform_get_drvdata(pdev);
+ unsigned int magnitude;
+
+ mutex_lock(&haptic->mutex);
+
+ haptic->suspended = false;
+
+ magnitude = ACCESS_ONCE(haptic->magnitude);
+ if (magnitude)
+ max77843_haptic_enable(haptic);
+
+ mutex_unlock(&haptic->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops,
+ max77843_haptic_suspend, max77843_haptic_resume);
+
+static struct platform_driver max77843_haptic_driver = {
+ .driver = {
+ .name = "max77843-haptic",
+ .pm = &max77843_haptic_pm_ops,
+ },
+ .probe = max77843_haptic_probe,
+};
+module_platform_driver(max77843_haptic_driver);
+
+MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
+MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
index 98228773a111..19c73574458e 100644
--- a/drivers/input/misc/mma8450.c
+++ b/drivers/input/misc/mma8450.c
@@ -174,12 +174,13 @@ static int mma8450_probe(struct i2c_client *c,
struct mma8450 *m;
int err;
- m = kzalloc(sizeof(struct mma8450), GFP_KERNEL);
- idev = input_allocate_polled_device();
- if (!m || !idev) {
- err = -ENOMEM;
- goto err_free_mem;
- }
+ m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ idev = devm_input_allocate_polled_device(&c->dev);
+ if (!idev)
+ return -ENOMEM;
m->client = c;
m->idev = idev;
@@ -187,7 +188,6 @@ static int mma8450_probe(struct i2c_client *c,
idev->private = m;
idev->input->name = MMA8450_DRV_NAME;
idev->input->id.bustype = BUS_I2C;
- idev->input->dev.parent = &c->dev;
idev->poll = mma8450_poll;
idev->poll_interval = POLL_INTERVAL;
idev->poll_interval_max = POLL_INTERVAL_MAX;
@@ -202,29 +202,12 @@ static int mma8450_probe(struct i2c_client *c,
err = input_register_polled_device(idev);
if (err) {
dev_err(&c->dev, "failed to register polled input device\n");
- goto err_free_mem;
+ return err;
}
i2c_set_clientdata(c, m);
return 0;
-
-err_free_mem:
- input_free_polled_device(idev);
- kfree(m);
- return err;
-}
-
-static int mma8450_remove(struct i2c_client *c)
-{
- struct mma8450 *m = i2c_get_clientdata(c);
- struct input_polled_dev *idev = m->idev;
-
- input_unregister_polled_device(idev);
- input_free_polled_device(idev);
- kfree(m);
-
- return 0;
}
static const struct i2c_device_id mma8450_id[] = {
@@ -242,11 +225,9 @@ MODULE_DEVICE_TABLE(of, mma8450_dt_ids);
static struct i2c_driver mma8450_driver = {
.driver = {
.name = MMA8450_DRV_NAME,
- .owner = THIS_MODULE,
.of_match_table = mma8450_dt_ids,
},
.probe = mma8450_probe,
- .remove = mma8450_remove,
.id_table = mma8450_id,
};
diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c
index 1f9b5ee92746..1e1baed63929 100644
--- a/drivers/input/misc/palmas-pwrbutton.c
+++ b/drivers/input/misc/palmas-pwrbutton.c
@@ -304,7 +304,7 @@ static SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
palmas_pwron_suspend, palmas_pwron_resume);
#ifdef CONFIG_OF
-static struct of_device_id of_palmas_pwr_match[] = {
+static const struct of_device_id of_palmas_pwr_match[] = {
{ .compatible = "ti,palmas-pwrbutton" },
{ },
};
diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
new file mode 100644
index 000000000000..867db8a91372
--- /dev/null
+++ b/drivers/input/misc/pm8941-pwrkey.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2014, Sony Mobile Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+
+#define PON_REV2 0x01
+
+#define PON_RT_STS 0x10
+#define PON_KPDPWR_N_SET BIT(0)
+
+#define PON_PS_HOLD_RST_CTL 0x5a
+#define PON_PS_HOLD_RST_CTL2 0x5b
+#define PON_PS_HOLD_ENABLE BIT(7)
+#define PON_PS_HOLD_TYPE_MASK 0x0f
+#define PON_PS_HOLD_TYPE_SHUTDOWN 4
+#define PON_PS_HOLD_TYPE_HARD_RESET 7
+
+#define PON_PULL_CTL 0x70
+#define PON_KPDPWR_PULL_UP BIT(1)
+
+#define PON_DBC_CTL 0x71
+#define PON_DBC_DELAY_MASK 0x7
+
+
+struct pm8941_pwrkey {
+ struct device *dev;
+ int irq;
+ u32 baseaddr;
+ struct regmap *regmap;
+ struct input_dev *input;
+
+ unsigned int revision;
+ struct notifier_block reboot_notifier;
+};
+
+static int pm8941_reboot_notify(struct notifier_block *nb,
+ unsigned long code, void *unused)
+{
+ struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey,
+ reboot_notifier);
+ unsigned int enable_reg;
+ unsigned int reset_type;
+ int error;
+
+ /* PMICs with revision 0 have the enable bit in same register as ctrl */
+ if (pwrkey->revision == 0)
+ enable_reg = PON_PS_HOLD_RST_CTL;
+ else
+ enable_reg = PON_PS_HOLD_RST_CTL2;
+
+ error = regmap_update_bits(pwrkey->regmap,
+ pwrkey->baseaddr + enable_reg,
+ PON_PS_HOLD_ENABLE,
+ 0);
+ if (error)
+ dev_err(pwrkey->dev,
+ "unable to clear ps hold reset enable: %d\n",
+ error);
+
+ /*
+ * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between
+ * writes.
+ */
+ usleep_range(100, 1000);
+
+ switch (code) {
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ reset_type = PON_PS_HOLD_TYPE_SHUTDOWN;
+ break;
+ case SYS_RESTART:
+ default:
+ reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
+ break;
+ };
+
+ error = regmap_update_bits(pwrkey->regmap,
+ pwrkey->baseaddr + PON_PS_HOLD_RST_CTL,
+ PON_PS_HOLD_TYPE_MASK,
+ reset_type);
+ if (error)
+ dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n",
+ error);
+
+ error = regmap_update_bits(pwrkey->regmap,
+ pwrkey->baseaddr + enable_reg,
+ PON_PS_HOLD_ENABLE,
+ PON_PS_HOLD_ENABLE);
+ if (error)
+ dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error);
+
+ return NOTIFY_DONE;
+}
+
+static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
+{
+ struct pm8941_pwrkey *pwrkey = _data;
+ unsigned int sts;
+ int error;
+
+ error = regmap_read(pwrkey->regmap,
+ pwrkey->baseaddr + PON_RT_STS, &sts);
+ if (error)
+ return IRQ_HANDLED;
+
+ input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET));
+ input_sync(pwrkey->input);
+
+ return IRQ_HANDLED;
+}
+
+static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
+{
+ struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(pwrkey->irq);
+
+ return 0;
+}
+
+static int __maybe_unused pm8941_pwrkey_resume(struct device *dev)
+{
+ struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(pwrkey->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
+ pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
+
+static int pm8941_pwrkey_probe(struct platform_device *pdev)
+{
+ struct pm8941_pwrkey *pwrkey;
+ bool pull_up;
+ u32 req_delay;
+ int error;
+
+ if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
+ req_delay = 15625;
+
+ if (req_delay > 2000000 || req_delay == 0) {
+ dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay);
+ return -EINVAL;
+ }
+
+ pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up");
+
+ pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
+ if (!pwrkey)
+ return -ENOMEM;
+
+ pwrkey->dev = &pdev->dev;
+
+ pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pwrkey->regmap) {
+ dev_err(&pdev->dev, "failed to locate regmap\n");
+ return -ENODEV;
+ }
+
+ pwrkey->irq = platform_get_irq(pdev, 0);
+ if (pwrkey->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return pwrkey->irq;
+ }
+
+ error = of_property_read_u32(pdev->dev.of_node, "reg",
+ &pwrkey->baseaddr);
+ if (error)
+ return error;
+
+ error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
+ &pwrkey->revision);
+ if (error) {
+ dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+ return error;
+ }
+
+ pwrkey->input = devm_input_allocate_device(&pdev->dev);
+ if (!pwrkey->input) {
+ dev_dbg(&pdev->dev, "unable to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_set_capability(pwrkey->input, EV_KEY, KEY_POWER);
+
+ pwrkey->input->name = "pm8941_pwrkey";
+ pwrkey->input->phys = "pm8941_pwrkey/input0";
+
+ req_delay = (req_delay << 6) / USEC_PER_SEC;
+ req_delay = ilog2(req_delay);
+
+ error = regmap_update_bits(pwrkey->regmap,
+ pwrkey->baseaddr + PON_DBC_CTL,
+ PON_DBC_DELAY_MASK,
+ req_delay);
+ if (error) {
+ dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+ return error;
+ }
+
+ error = regmap_update_bits(pwrkey->regmap,
+ pwrkey->baseaddr + PON_PULL_CTL,
+ PON_KPDPWR_PULL_UP,
+ pull_up ? PON_KPDPWR_PULL_UP : 0);
+ if (error) {
+ dev_err(&pdev->dev, "failed to set pull: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
+ NULL, pm8941_pwrkey_irq,
+ IRQF_ONESHOT,
+ "pm8941_pwrkey", pwrkey);
+ if (error) {
+ dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
+ return error;
+ }
+
+ error = input_register_device(pwrkey->input);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device: %d\n",
+ error);
+ return error;
+ }
+
+ pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify,
+ error = register_reboot_notifier(&pwrkey->reboot_notifier);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
+ error);
+ return error;
+ }
+
+ platform_set_drvdata(pdev, pwrkey);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+}
+
+static int pm8941_pwrkey_remove(struct platform_device *pdev)
+{
+ struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+ unregister_reboot_notifier(&pwrkey->reboot_notifier);
+
+ return 0;
+}
+
+static const struct of_device_id pm8941_pwr_key_id_table[] = {
+ { .compatible = "qcom,pm8941-pwrkey" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
+
+static struct platform_driver pm8941_pwrkey_driver = {
+ .probe = pm8941_pwrkey_probe,
+ .remove = pm8941_pwrkey_remove,
+ .driver = {
+ .name = "pm8941-pwrkey",
+ .pm = &pm8941_pwr_key_pm_ops,
+ .of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
+ },
+};
+module_platform_driver(pm8941_pwrkey_driver);
+
+MODULE_DESCRIPTION("PM8941 Power Key driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index a28ee70ff158..e82edf810d1f 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -50,7 +50,6 @@ static int pwm_beeper_event(struct input_dev *input,
}
if (value == 0) {
- pwm_config(beeper->pwm, 0, 0);
pwm_disable(beeper->pwm);
} else {
period = HZ_TO_NANOSECONDS(value);
@@ -169,12 +168,6 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
pwm_beeper_suspend, pwm_beeper_resume);
-#ifdef CONFIG_PM_SLEEP
-#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
-#else
-#define PWM_BEEPER_PM_OPS NULL
-#endif
-
#ifdef CONFIG_OF
static const struct of_device_id pwm_beeper_match[] = {
{ .compatible = "pwm-beeper", },
@@ -187,7 +180,7 @@ static struct platform_driver pwm_beeper_driver = {
.remove = pwm_beeper_remove,
.driver = {
.name = "pwm-beeper",
- .pm = PWM_BEEPER_PM_OPS,
+ .pm = &pwm_beeper_pm_ops,
.of_match_table = of_match_ptr(pwm_beeper_match),
},
};
diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c
index 132eb914ea3e..6bf3f1082f71 100644
--- a/drivers/input/misc/regulator-haptic.c
+++ b/drivers/input/misc/regulator-haptic.c
@@ -245,7 +245,7 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
regulator_haptic_suspend, regulator_haptic_resume);
-static struct of_device_id regulator_haptic_dt_match[] = {
+static const struct of_device_id regulator_haptic_dt_match[] = {
{ .compatible = "regulator-haptic" },
{ /* sentinel */ },
};
diff --git a/drivers/input/misc/retu-pwrbutton.c b/drivers/input/misc/retu-pwrbutton.c
index 0c8ac60e2639..30b459b6b344 100644
--- a/drivers/input/misc/retu-pwrbutton.c
+++ b/drivers/input/misc/retu-pwrbutton.c
@@ -63,7 +63,8 @@ static int retu_pwrbutton_probe(struct platform_device *pdev)
input_set_drvdata(idev, rdev);
error = devm_request_threaded_irq(&pdev->dev, irq,
- NULL, retu_pwrbutton_irq, 0,
+ NULL, retu_pwrbutton_irq,
+ IRQF_ONESHOT,
"retu-pwrbutton", idev);
if (error)
return error;
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index e8e010a85484..c14b82709b0f 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -18,7 +18,6 @@
#include <linux/gpio/consumer.h>
#include <linux/gpio_keys.h>
#include <linux/platform_device.h>
-#include <linux/acpi.h>
/*
* Definition of buttons on the tablet. The ACPI index of each button
diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c
index 54508dec4eb3..a39b62651a4b 100644
--- a/drivers/input/misc/tps65218-pwrbutton.c
+++ b/drivers/input/misc/tps65218-pwrbutton.c
@@ -106,7 +106,7 @@ static int tps65218_pwron_probe(struct platform_device *pdev)
return 0;
}
-static struct of_device_id of_tps65218_pwr_match[] = {
+static const struct of_device_id of_tps65218_pwr_match[] = {
{ .compatible = "ti,tps65218-pwrbutton" },
{ },
};
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index e98cc81a84c6..603fc2fadf05 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -71,7 +71,8 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev)
pwr->dev.parent = &pdev->dev;
err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
"twl4030_pwrbutton", pwr);
if (err < 0) {
dev_err(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
index 0e0d094df2e6..ea63fad48de6 100644
--- a/drivers/input/misc/twl6040-vibra.c
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -308,7 +308,8 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
mutex_init(&info->mutex);
error = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
- twl6040_vib_irq_handler, 0,
+ twl6040_vib_irq_handler,
+ IRQF_ONESHOT,
"twl6040_irq_vib", info);
if (error) {
dev_err(info->dev, "VIB IRQ request failed: %d\n", error);
diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c
index 59d4f7bcb4a3..1b44de265a0a 100644
--- a/drivers/input/misc/wm831x-on.c
+++ b/drivers/input/misc/wm831x-on.c
@@ -99,7 +99,8 @@ static int wm831x_on_probe(struct platform_device *pdev)
wm831x_on->dev->dev.parent = &pdev->dev;
ret = request_threaded_irq(irq, NULL, wm831x_on_irq,
- IRQF_TRIGGER_RISING, "wm831x_on",
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "wm831x_on",
wm831x_on);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret);
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 4658b5d41dd7..d7820d1152d2 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -149,6 +149,18 @@ config MOUSE_PS2_FOCALTECH
If unsure, say Y.
+config MOUSE_PS2_VMMOUSE
+ bool "Virtual mouse (vmmouse)"
+ depends on MOUSE_PS2 && X86 && HYPERVISOR_GUEST
+ help
+ Say Y here if you are running under control of VMware hypervisor
+ (ESXi, Workstation or Fusion). Also make sure that when you enable
+ this option, you remove the xf86-input-vmmouse user-space driver
+ or upgrade it to at least xf86-input-vmmouse 13.1.0, which doesn't
+ load in the presence of an in-kernel vmmouse driver.
+
+ If unsure, say N.
+
config MOUSE_SERIAL
tristate "Serial mouse"
select SERIO
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 8a9c98e76d9c..793300bfbddd 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -36,6 +36,7 @@ psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE) += vmmouse.o
elan_i2c-objs := elan_i2c_core.o
elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C) += elan_i2c_i2c.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 27bcdbc950c9..113d6f1516a5 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -153,10 +153,18 @@ static const struct alps_protocol_info alps_v7_protocol_data = {
ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT
};
+static const struct alps_protocol_info alps_v8_protocol_data = {
+ ALPS_PROTO_V8, 0x18, 0x18, 0
+};
+
static void alps_set_abs_params_st(struct alps_data *priv,
struct input_dev *dev1);
-static void alps_set_abs_params_mt(struct alps_data *priv,
+static void alps_set_abs_params_semi_mt(struct alps_data *priv,
+ struct input_dev *dev1);
+static void alps_set_abs_params_v7(struct alps_data *priv,
struct input_dev *dev1);
+static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
+ struct input_dev *dev1);
/* Packet formats are described in Documentation/input/alps.txt */
@@ -243,6 +251,14 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
return;
}
+ /* Non interleaved V2 dualpoint has separate stick button bits */
+ if (priv->proto_version == ALPS_PROTO_V2 &&
+ priv->flags == (ALPS_PASS | ALPS_DUALPOINT)) {
+ left |= packet[0] & 1;
+ right |= packet[0] & 2;
+ middle |= packet[0] & 4;
+ }
+
alps_report_buttons(dev, dev2, left, right, middle);
/* Convert hardware tap to a reasonable Z value */
@@ -294,53 +310,6 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
input_sync(dev);
}
-/*
- * Process bitmap data for V5 protocols. Return value is null.
- *
- * The bitmaps don't have enough data to track fingers, so this function
- * only generates points representing a bounding box of at most two contacts.
- * These two points are returned in fields->mt.
- */
-static void alps_process_bitmap_dolphin(struct alps_data *priv,
- struct alps_fields *fields)
-{
- int box_middle_x, box_middle_y;
- unsigned int x_map, y_map;
- unsigned char start_bit, end_bit;
- unsigned char x_msb, x_lsb, y_msb, y_lsb;
-
- x_map = fields->x_map;
- y_map = fields->y_map;
-
- if (!x_map || !y_map)
- return;
-
- /* Get Most-significant and Least-significant bit */
- x_msb = fls(x_map);
- x_lsb = ffs(x_map);
- y_msb = fls(y_map);
- y_lsb = ffs(y_map);
-
- /* Most-significant bit should never exceed max sensor line number */
- if (x_msb > priv->x_bits || y_msb > priv->y_bits)
- return;
-
- if (fields->fingers > 1) {
- start_bit = priv->x_bits - x_msb;
- end_bit = priv->x_bits - x_lsb;
- box_middle_x = (priv->x_max * (start_bit + end_bit)) /
- (2 * (priv->x_bits - 1));
-
- start_bit = y_lsb - 1;
- end_bit = y_msb - 1;
- box_middle_y = (priv->y_max * (start_bit + end_bit)) /
- (2 * (priv->y_bits - 1));
- fields->mt[0] = fields->st;
- fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x;
- fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y;
- }
-}
-
static void alps_get_bitmap_points(unsigned int map,
struct alps_bitmap_point *low,
struct alps_bitmap_point *high,
@@ -368,7 +337,7 @@ static void alps_get_bitmap_points(unsigned int map,
}
/*
- * Process bitmap data from v3 and v4 protocols. Returns the number of
+ * Process bitmap data from semi-mt protocols. Returns the number of
* fingers detected. A return value of 0 means at least one of the
* bitmaps was empty.
*
@@ -380,9 +349,10 @@ static void alps_get_bitmap_points(unsigned int map,
static int alps_process_bitmap(struct alps_data *priv,
struct alps_fields *fields)
{
- int i, fingers_x = 0, fingers_y = 0, fingers;
+ int i, fingers_x = 0, fingers_y = 0, fingers, closest;
struct alps_bitmap_point x_low = {0,}, x_high = {0,};
struct alps_bitmap_point y_low = {0,}, y_high = {0,};
+ struct input_mt_pos corner[4];
if (!fields->x_map || !fields->y_map)
return 0;
@@ -413,26 +383,76 @@ static int alps_process_bitmap(struct alps_data *priv,
y_high.num_bits = max(i, 1);
}
- fields->mt[0].x =
+ /* top-left corner */
+ corner[0].x =
(priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
(2 * (priv->x_bits - 1));
- fields->mt[0].y =
+ corner[0].y =
(priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
(2 * (priv->y_bits - 1));
- fields->mt[1].x =
+ /* top-right corner */
+ corner[1].x =
(priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
(2 * (priv->x_bits - 1));
- fields->mt[1].y =
+ corner[1].y =
+ (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
+ (2 * (priv->y_bits - 1));
+
+ /* bottom-right corner */
+ corner[2].x =
+ (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
+ (2 * (priv->x_bits - 1));
+ corner[2].y =
+ (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
+ (2 * (priv->y_bits - 1));
+
+ /* bottom-left corner */
+ corner[3].x =
+ (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
+ (2 * (priv->x_bits - 1));
+ corner[3].y =
(priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
(2 * (priv->y_bits - 1));
- /* y-bitmap order is reversed, except on rushmore */
- if (priv->proto_version != ALPS_PROTO_V3_RUSHMORE) {
- fields->mt[0].y = priv->y_max - fields->mt[0].y;
- fields->mt[1].y = priv->y_max - fields->mt[1].y;
+ /* x-bitmap order is reversed on v5 touchpads */
+ if (priv->proto_version == ALPS_PROTO_V5) {
+ for (i = 0; i < 4; i++)
+ corner[i].x = priv->x_max - corner[i].x;
}
+ /* y-bitmap order is reversed on v3 and v4 touchpads */
+ if (priv->proto_version == ALPS_PROTO_V3 ||
+ priv->proto_version == ALPS_PROTO_V4) {
+ for (i = 0; i < 4; i++)
+ corner[i].y = priv->y_max - corner[i].y;
+ }
+
+ /*
+ * We only select a corner for the second touch once per 2 finger
+ * touch sequence to avoid the chosen corner (and thus the coordinates)
+ * jumping around when the first touch is in the middle.
+ */
+ if (priv->second_touch == -1) {
+ /* Find corner closest to our st coordinates */
+ closest = 0x7fffffff;
+ for (i = 0; i < 4; i++) {
+ int dx = fields->st.x - corner[i].x;
+ int dy = fields->st.y - corner[i].y;
+ int distance = dx * dx + dy * dy;
+
+ if (distance < closest) {
+ priv->second_touch = i;
+ closest = distance;
+ }
+ }
+ /* And select the opposite corner to use for the 2nd touch */
+ priv->second_touch = (priv->second_touch + 2) % 4;
+ }
+
+ fields->mt[0] = fields->st;
+ fields->mt[1] = corner[priv->second_touch];
+
return fingers;
}
@@ -469,9 +489,14 @@ static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers)
f->mt[0].x = f->st.x;
f->mt[0].y = f->st.y;
fingers = f->pressure > 0 ? 1 : 0;
+ priv->second_touch = -1;
}
- alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2);
+ if (fingers >= 1)
+ alps_set_slot(dev, 0, f->mt[0].x, f->mt[0].y);
+ if (fingers >= 2)
+ alps_set_slot(dev, 1, f->mt[1].x, f->mt[1].y);
+ input_mt_sync_frame(dev);
input_mt_report_finger_count(dev, fingers);
@@ -568,20 +593,22 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
f->first_mp = !!(p[4] & 0x40);
f->is_mp = !!(p[0] & 0x40);
- f->fingers = (p[5] & 0x3) + 1;
- f->x_map = ((p[4] & 0x7e) << 8) |
- ((p[1] & 0x7f) << 2) |
- ((p[0] & 0x30) >> 4);
- f->y_map = ((p[3] & 0x70) << 4) |
- ((p[2] & 0x7f) << 1) |
- (p[4] & 0x01);
-
- f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
- ((p[0] & 0x30) >> 4);
- f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
- f->pressure = p[5] & 0x7f;
+ if (f->is_mp) {
+ f->fingers = (p[5] & 0x3) + 1;
+ f->x_map = ((p[4] & 0x7e) << 8) |
+ ((p[1] & 0x7f) << 2) |
+ ((p[0] & 0x30) >> 4);
+ f->y_map = ((p[3] & 0x70) << 4) |
+ ((p[2] & 0x7f) << 1) |
+ (p[4] & 0x01);
+ } else {
+ f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+ ((p[0] & 0x30) >> 4);
+ f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+ f->pressure = p[5] & 0x7f;
- alps_decode_buttons_v3(f, p);
+ alps_decode_buttons_v3(f, p);
+ }
return 0;
}
@@ -589,13 +616,27 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
struct psmouse *psmouse)
{
- alps_decode_pinnacle(f, p, psmouse);
-
- /* Rushmore's packet decode has a bit difference with Pinnacle's */
+ f->first_mp = !!(p[4] & 0x40);
f->is_mp = !!(p[5] & 0x40);
- f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
- f->x_map |= (p[5] & 0x10) << 11;
- f->y_map |= (p[5] & 0x20) << 6;
+
+ if (f->is_mp) {
+ f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
+ f->x_map = ((p[5] & 0x10) << 11) |
+ ((p[4] & 0x7e) << 8) |
+ ((p[1] & 0x7f) << 2) |
+ ((p[0] & 0x30) >> 4);
+ f->y_map = ((p[5] & 0x20) << 6) |
+ ((p[3] & 0x70) << 4) |
+ ((p[2] & 0x7f) << 1) |
+ (p[4] & 0x01);
+ } else {
+ f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+ ((p[0] & 0x30) >> 4);
+ f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+ f->pressure = p[5] & 0x7f;
+
+ alps_decode_buttons_v3(f, p);
+ }
return 0;
}
@@ -664,30 +705,13 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
*/
if (f->is_mp) {
fingers = f->fingers;
- if (priv->proto_version == ALPS_PROTO_V3 ||
- priv->proto_version == ALPS_PROTO_V3_RUSHMORE) {
- if (alps_process_bitmap(priv, f) == 0)
- fingers = 0; /* Use st data */
-
- /* Now process position packet */
- priv->decode_fields(f, priv->multi_data,
- psmouse);
- } else {
- /*
- * Because Dolphin uses position packet's
- * coordinate data as Pt1 and uses it to
- * calculate Pt2, so we need to do position
- * packet decode first.
- */
- priv->decode_fields(f, priv->multi_data,
- psmouse);
-
- /*
- * Since Dolphin's finger number is reliable,
- * there is no need to compare with bmap_fn.
- */
- alps_process_bitmap_dolphin(priv, f);
- }
+ /*
+ * Bitmap processing uses position packet's coordinate
+ * data, so we need to do decode it first.
+ */
+ priv->decode_fields(f, priv->multi_data, psmouse);
+ if (alps_process_bitmap(priv, f) == 0)
+ fingers = 0; /* Use st data */
} else {
priv->multi_packet = 0;
}
@@ -849,6 +873,14 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
priv->multi_data[offset] = packet[6];
priv->multi_data[offset + 1] = packet[7];
+ f->left = !!(packet[4] & 0x01);
+ f->right = !!(packet[4] & 0x02);
+
+ f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
+ ((packet[0] & 0x30) >> 4);
+ f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
+ f->pressure = packet[5] & 0x7f;
+
if (++priv->multi_packet > 2) {
priv->multi_packet = 0;
@@ -863,14 +895,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
f->fingers = alps_process_bitmap(priv, f);
}
- f->left = !!(packet[4] & 0x01);
- f->right = !!(packet[4] & 0x02);
-
- f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
- ((packet[0] & 0x30) >> 4);
- f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
- f->pressure = packet[5] & 0x7f;
-
alps_report_semi_mt_data(psmouse, f->fingers);
}
@@ -925,6 +949,11 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
case V7_PACKET_ID_TWO:
mt[1].x &= ~0x000F;
mt[1].y |= 0x000F;
+ /* Detect false-postive touches where x & y report max value */
+ if (mt[1].y == 0x7ff && mt[1].x == 0xff0) {
+ mt[1].x = 0;
+ /* y gets set to 0 at the end of this function */
+ }
break;
case V7_PACKET_ID_MULTI:
@@ -1042,9 +1071,8 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
right = (packet[1] & 0x02) >> 1;
middle = (packet[1] & 0x04) >> 2;
- /* Divide 2 since trackpoint's speed is too fast */
- input_report_rel(dev2, REL_X, (char)x / 2);
- input_report_rel(dev2, REL_Y, -((char)y / 2));
+ input_report_rel(dev2, REL_X, (char)x);
+ input_report_rel(dev2, REL_Y, -((char)y));
input_report_key(dev2, BTN_LEFT, left);
input_report_key(dev2, BTN_RIGHT, right);
@@ -1085,6 +1113,176 @@ 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)
+{
+ unsigned char pkt_id = SS4_PACKET_ID_IDLE;
+
+ if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
+ (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) {
+ pkt_id = SS4_PACKET_ID_IDLE;
+ } else if (!(byte[3] & 0x10)) {
+ pkt_id = SS4_PACKET_ID_ONE;
+ } else if (!(byte[3] & 0x20)) {
+ pkt_id = SS4_PACKET_ID_TWO;
+ } else {
+ pkt_id = SS4_PACKET_ID_MULTI;
+ }
+
+ return pkt_id;
+}
+
+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;
+ unsigned int no_data_x, no_data_y;
+
+ pkt_id = alps_get_pkt_id_ss4_v2(p);
+
+ /* Current packet is 1Finger coordinate packet */
+ switch (pkt_id) {
+ case SS4_PACKET_ID_ONE:
+ f->mt[0].x = SS4_1F_X_V2(p);
+ f->mt[0].y = SS4_1F_Y_V2(p);
+ f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;
+ f->fingers = 1;
+ f->first_mp = 0;
+ f->is_mp = 0;
+ break;
+
+ case SS4_PACKET_ID_TWO:
+ if (priv->flags & ALPS_BUTTONPAD) {
+ f->mt[0].x = SS4_BTL_MF_X_V2(p, 0);
+ f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0);
+ f->mt[1].x = SS4_BTL_MF_X_V2(p, 1);
+ f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1);
+ } else {
+ f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
+ f->mt[0].y = SS4_STD_MF_Y_V2(p, 0);
+ f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
+ f->mt[1].y = SS4_STD_MF_Y_V2(p, 1);
+ }
+ f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0;
+
+ if (SS4_IS_MF_CONTINUE(p)) {
+ f->first_mp = 1;
+ } else {
+ f->fingers = 2;
+ f->first_mp = 0;
+ }
+ f->is_mp = 0;
+
+ break;
+
+ case SS4_PACKET_ID_MULTI:
+ if (priv->flags & ALPS_BUTTONPAD) {
+ f->mt[2].x = SS4_BTL_MF_X_V2(p, 0);
+ f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0);
+ f->mt[3].x = SS4_BTL_MF_X_V2(p, 1);
+ f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1);
+ no_data_x = SS4_MFPACKET_NO_AX_BL;
+ no_data_y = SS4_MFPACKET_NO_AY_BL;
+ } else {
+ f->mt[2].x = SS4_STD_MF_X_V2(p, 0);
+ f->mt[2].y = SS4_STD_MF_Y_V2(p, 0);
+ f->mt[3].x = SS4_STD_MF_X_V2(p, 1);
+ f->mt[3].y = SS4_STD_MF_Y_V2(p, 1);
+ no_data_x = SS4_MFPACKET_NO_AX;
+ no_data_y = SS4_MFPACKET_NO_AY;
+ }
+
+ f->first_mp = 0;
+ f->is_mp = 1;
+
+ if (SS4_IS_5F_DETECTED(p)) {
+ f->fingers = 5;
+ } else if (f->mt[3].x == no_data_x &&
+ f->mt[3].y == no_data_y) {
+ f->mt[3].x = 0;
+ f->mt[3].y = 0;
+ f->fingers = 3;
+ } else {
+ f->fingers = 4;
+ }
+ break;
+
+ case SS4_PACKET_ID_IDLE:
+ default:
+ memset(f, 0, sizeof(struct alps_fields));
+ break;
+ }
+
+ f->left = !!(SS4_BTN_V2(p) & 0x01);
+ if (!(priv->flags & ALPS_BUTTONPAD)) {
+ f->right = !!(SS4_BTN_V2(p) & 0x02);
+ f->middle = !!(SS4_BTN_V2(p) & 0x04);
+ }
+
+ return 0;
+}
+
+static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ struct input_dev *dev = psmouse->dev;
+ struct alps_fields *f = &priv->f;
+
+ memset(f, 0, sizeof(struct alps_fields));
+ priv->decode_fields(f, packet, psmouse);
+ if (priv->multi_packet) {
+ /*
+ * Sometimes the first packet will indicate a multi-packet
+ * sequence, but sometimes the next multi-packet would not
+ * come. Check for this, and when it happens process the
+ * position packet as usual.
+ */
+ if (f->is_mp) {
+ /* Now process the 1st packet */
+ priv->decode_fields(f, priv->multi_data, psmouse);
+ } else {
+ priv->multi_packet = 0;
+ }
+ }
+
+ /*
+ * "f.is_mp" would always be '0' after merging the 1st and 2nd packet.
+ * When it is set, it means 2nd packet comes without 1st packet come.
+ */
+ if (f->is_mp)
+ return;
+
+ /* Save the first packet */
+ if (!priv->multi_packet && f->first_mp) {
+ priv->multi_packet = 1;
+ memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
+ return;
+ }
+
+ priv->multi_packet = 0;
+
+ alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4);
+
+ input_mt_report_finger_count(dev, f->fingers);
+
+ input_report_key(dev, BTN_LEFT, f->left);
+ input_report_key(dev, BTN_RIGHT, f->right);
+ input_report_key(dev, BTN_MIDDLE, f->middle);
+
+ input_report_abs(dev, ABS_PRESSURE, f->pressure);
+ input_sync(dev);
+}
+
+static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
+{
+ if (psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08))
+ return false;
+ if (psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0))
+ return false;
+ return true;
+}
+
static DEFINE_MUTEX(alps_mutex);
static void alps_register_bare_ps2_mouse(struct work_struct *work)
@@ -1159,13 +1357,14 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
bool report_buttons)
{
struct alps_data *priv = psmouse->private;
- struct input_dev *dev;
+ struct input_dev *dev, *dev2 = NULL;
/* Figure out which device to use to report the bare packet */
if (priv->proto_version == ALPS_PROTO_V2 &&
(priv->flags & ALPS_DUALPOINT)) {
/* On V2 devices the DualPoint Stick reports bare packets */
dev = priv->dev2;
+ dev2 = psmouse->dev;
} else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) {
/* Register dev3 mouse if we received PS/2 packet first time */
if (!IS_ERR(priv->dev3))
@@ -1177,7 +1376,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
}
if (report_buttons)
- alps_report_buttons(dev, NULL,
+ alps_report_buttons(dev, dev2,
packet[0] & 1, packet[0] & 2, packet[0] & 4);
input_report_rel(dev, REL_X,
@@ -1305,8 +1504,12 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
* a device connected to the external PS/2 port. Because bare PS/2
* protocol does not have enough constant bits to self-synchronize
* properly we only do this if the device is fully synchronized.
+ * Can not distinguish V8's first byte from PS/2 packet's
*/
- if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
+ if (priv->proto_version != ALPS_PROTO_V8 &&
+ !psmouse->out_of_sync_cnt &&
+ (psmouse->packet[0] & 0xc8) == 0x08) {
+
if (psmouse->pktcnt == 3) {
alps_report_bare_ps2_packet(psmouse, psmouse->packet,
true);
@@ -1354,8 +1557,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
return PSMOUSE_BAD_DATA;
}
- if (priv->proto_version == ALPS_PROTO_V7 &&
- !alps_is_valid_package_v7(psmouse)) {
+ if ((priv->proto_version == ALPS_PROTO_V7 &&
+ !alps_is_valid_package_v7(psmouse)) ||
+ (priv->proto_version == ALPS_PROTO_V8 &&
+ !alps_is_valid_package_ss4_v2(psmouse))) {
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
psmouse->pktcnt - 1,
psmouse->packet[psmouse->pktcnt - 1]);
@@ -2130,6 +2335,88 @@ error:
return -1;
}
+static int alps_get_otp_values_ss4_v2(struct psmouse *psmouse,
+ unsigned char index, unsigned char otp[])
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+ switch (index) {
+ case 0:
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+ ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
+ return -1;
+
+ break;
+
+ case 1:
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
+ ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
+ return -1;
+
+ break;
+ }
+
+ return 0;
+}
+
+static int alps_update_device_area_ss4_v2(unsigned char otp[][4],
+ struct alps_data *priv)
+{
+ int num_x_electrode;
+ int num_y_electrode;
+ int x_pitch, y_pitch, x_phys, y_phys;
+
+ num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F);
+ num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x0F);
+
+ priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+ priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+
+ x_pitch = ((otp[1][2] >> 2) & 0x07) + SS4_MIN_PITCH_MM;
+ y_pitch = ((otp[1][2] >> 5) & 0x07) + SS4_MIN_PITCH_MM;
+
+ x_phys = x_pitch * (num_x_electrode - 1); /* In 0.1 mm units */
+ y_phys = y_pitch * (num_y_electrode - 1); /* In 0.1 mm units */
+
+ priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
+ priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
+
+ return 0;
+}
+
+static int alps_update_btn_info_ss4_v2(unsigned char otp[][4],
+ struct alps_data *priv)
+{
+ unsigned char is_btnless;
+
+ is_btnless = (otp[1][1] >> 3) & 0x01;
+
+ if (is_btnless)
+ priv->flags |= ALPS_BUTTONPAD;
+
+ return 0;
+}
+
+static int alps_set_defaults_ss4_v2(struct psmouse *psmouse,
+ struct alps_data *priv)
+{
+ unsigned char otp[2][4];
+
+ memset(otp, 0, sizeof(otp));
+
+ if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) ||
+ alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0]))
+ return -1;
+
+ alps_update_device_area_ss4_v2(otp, priv);
+
+ alps_update_btn_info_ss4_v2(otp, priv);
+
+ return 0;
+}
+
static int alps_dolphin_get_device_area(struct psmouse *psmouse,
struct alps_data *priv)
{
@@ -2222,6 +2509,35 @@ error:
return ret;
}
+static int alps_hw_init_ss4_v2(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ char param[2] = {0x64, 0x28};
+ int ret = -1;
+
+ /* enter absolute mode */
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+ ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+ ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE)) {
+ goto error;
+ }
+
+ /* T.B.D. Decread noise packet number, delete in the future */
+ if (alps_exit_command_mode(psmouse) ||
+ alps_enter_command_mode(psmouse) ||
+ alps_command_mode_write_reg(psmouse, 0x001D, 0x20)) {
+ goto error;
+ }
+ alps_exit_command_mode(psmouse);
+
+ return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+ alps_exit_command_mode(psmouse);
+ return ret;
+}
+
static int alps_set_protocol(struct psmouse *psmouse,
struct alps_data *priv,
const struct alps_protocol_info *protocol)
@@ -2253,7 +2569,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
case ALPS_PROTO_V3:
priv->hw_init = alps_hw_init_v3;
priv->process_packet = alps_process_packet_v3;
- priv->set_abs_params = alps_set_abs_params_mt;
+ priv->set_abs_params = alps_set_abs_params_semi_mt;
priv->decode_fields = alps_decode_pinnacle;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
@@ -2262,7 +2578,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
case ALPS_PROTO_V3_RUSHMORE:
priv->hw_init = alps_hw_init_rushmore_v3;
priv->process_packet = alps_process_packet_v3;
- priv->set_abs_params = alps_set_abs_params_mt;
+ priv->set_abs_params = alps_set_abs_params_semi_mt;
priv->decode_fields = alps_decode_rushmore;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
@@ -2278,7 +2594,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
case ALPS_PROTO_V4:
priv->hw_init = alps_hw_init_v4;
priv->process_packet = alps_process_packet_v4;
- priv->set_abs_params = alps_set_abs_params_mt;
+ priv->set_abs_params = alps_set_abs_params_semi_mt;
priv->nibble_commands = alps_v4_nibble_commands;
priv->addr_command = PSMOUSE_CMD_DISABLE;
break;
@@ -2287,7 +2603,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
priv->hw_init = alps_hw_init_dolphin_v1;
priv->process_packet = alps_process_touchpad_packet_v3_v5;
priv->decode_fields = alps_decode_dolphin;
- priv->set_abs_params = alps_set_abs_params_mt;
+ priv->set_abs_params = alps_set_abs_params_semi_mt;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
priv->x_bits = 23;
@@ -2311,7 +2627,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
priv->hw_init = alps_hw_init_v7;
priv->process_packet = alps_process_packet_v7;
priv->decode_fields = alps_decode_packet_v7;
- priv->set_abs_params = alps_set_abs_params_mt;
+ priv->set_abs_params = alps_set_abs_params_v7;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
priv->x_max = 0xfff;
@@ -2321,6 +2637,19 @@ static int alps_set_protocol(struct psmouse *psmouse,
priv->flags |= ALPS_BUTTONPAD;
break;
+
+ case ALPS_PROTO_V8:
+ priv->hw_init = alps_hw_init_ss4_v2;
+ priv->process_packet = alps_process_packet_ss4_v2;
+ priv->decode_fields = alps_decode_ss4_v2;
+ priv->set_abs_params = alps_set_abs_params_ss4_v2;
+ priv->nibble_commands = alps_v3_nibble_commands;
+ priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+
+ if (alps_set_defaults_ss4_v2(psmouse, priv))
+ return -EIO;
+
+ break;
}
return 0;
@@ -2389,6 +2718,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
} else if (ec[0] == 0x88 && ec[1] == 0x07 &&
ec[2] >= 0x90 && ec[2] <= 0x9d) {
protocol = &alps_v3_protocol_data;
+ } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
+ e7[2] == 0x14 && ec[1] == 0x02) {
+ protocol = &alps_v8_protocol_data;
} else {
psmouse_dbg(psmouse,
"Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
@@ -2437,10 +2769,11 @@ static void alps_set_abs_params_st(struct alps_data *priv,
{
input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
+ input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
}
-static void alps_set_abs_params_mt(struct alps_data *priv,
- struct input_dev *dev1)
+static void alps_set_abs_params_mt_common(struct alps_data *priv,
+ struct input_dev *dev1)
{
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
@@ -2448,15 +2781,44 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
- input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER |
- INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
-
set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
+}
+
+static void alps_set_abs_params_semi_mt(struct alps_data *priv,
+ struct input_dev *dev1)
+{
+ alps_set_abs_params_mt_common(priv, dev1);
+ input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+
+ input_mt_init_slots(dev1, MAX_TOUCHES,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+ INPUT_MT_SEMI_MT);
+}
+
+static void alps_set_abs_params_v7(struct alps_data *priv,
+ struct input_dev *dev1)
+{
+ alps_set_abs_params_mt_common(priv, dev1);
+ set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
+
+ input_mt_init_slots(dev1, MAX_TOUCHES,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+ INPUT_MT_TRACK);
+
+ set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
+}
+
+static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
+ struct input_dev *dev1)
+{
+ alps_set_abs_params_mt_common(priv, dev1);
+ input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+ set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
- /* V7 is real multi-touch */
- if (priv->proto_version == ALPS_PROTO_V7)
- clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
+ input_mt_init_slots(dev1, MAX_TOUCHES,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+ INPUT_MT_TRACK);
}
int alps_init(struct psmouse *psmouse)
@@ -2489,9 +2851,6 @@ int alps_init(struct psmouse *psmouse)
dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
priv->set_abs_params(priv, dev1);
- /* No pressure on V7 */
- if (priv->proto_version != ALPS_PROTO_V7)
- input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
if (priv->flags & ALPS_WHEEL) {
dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 02513c0502fc..d37f814dc447 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -22,14 +22,90 @@
#define ALPS_PROTO_V5 0x500
#define ALPS_PROTO_V6 0x600
#define ALPS_PROTO_V7 0x700 /* t3btl t4s */
+#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */
-#define MAX_TOUCHES 2
+#define MAX_TOUCHES 4
#define DOLPHIN_COUNT_PER_ELECTRODE 64
#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */
#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */
/*
+ * enum SS4_PACKET_ID - defines the packet type for V8
+ * SS4_PACKET_ID_IDLE: There's no finger and no button activity.
+ * SS4_PACKET_ID_ONE: There's one finger on touchpad
+ * or there's button activities.
+ * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
+ * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
+*/
+enum SS4_PACKET_ID {
+ SS4_PACKET_ID_IDLE = 0,
+ SS4_PACKET_ID_ONE,
+ SS4_PACKET_ID_TWO,
+ SS4_PACKET_ID_MULTI,
+};
+
+#define SS4_COUNT_PER_ELECTRODE 256
+#define SS4_NUMSENSOR_XOFFSET 7
+#define SS4_NUMSENSOR_YOFFSET 7
+#define SS4_MIN_PITCH_MM 50
+
+#define SS4_MASK_NORMAL_BUTTONS 0x07
+
+#define SS4_1F_X_V2(_b) ((_b[0] & 0x0007) | \
+ ((_b[1] << 3) & 0x0078) | \
+ ((_b[1] << 2) & 0x0380) | \
+ ((_b[2] << 5) & 0x1C00) \
+ )
+
+#define SS4_1F_Y_V2(_b) (((_b[2]) & 0x000F) | \
+ ((_b[3] >> 2) & 0x0030) | \
+ ((_b[4] << 6) & 0x03C0) | \
+ ((_b[4] << 5) & 0x0C00) \
+ )
+
+#define SS4_1F_Z_V2(_b) (((_b[5]) & 0x0F) | \
+ ((_b[5] >> 1) & 0x70) | \
+ ((_b[4]) & 0x80) \
+ )
+
+#define SS4_1F_LFB_V2(_b) (((_b[2] >> 4) & 0x01) == 0x01)
+
+#define SS4_MF_LF_V2(_b, _i) ((_b[1 + (_i) * 3] & 0x0004) == 0x0004)
+
+#define SS4_BTN_V2(_b) ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS)
+
+#define SS4_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 5) & 0x00E0) | \
+ ((_b[1 + _i * 3] << 5) & 0x1F00) \
+ )
+
+#define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \
+ ((_b[2 + (_i) * 3] << 5) & 0x01E0) | \
+ ((_b[2 + (_i) * 3] << 4) & 0x0E00) \
+ )
+
+#define SS4_BTL_MF_X_V2(_b, _i) (SS4_STD_MF_X_V2(_b, _i) | \
+ ((_b[0 + (_i) * 3] >> 3) & 0x0010) \
+ )
+
+#define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \
+ ((_b[0 + (_i) * 3] >> 3) & 0x0008) \
+ )
+
+#define SS4_MF_Z_V2(_b, _i) (((_b[1 + (_i) * 3]) & 0x0001) | \
+ ((_b[1 + (_i) * 3] >> 1) & 0x0002) \
+ )
+
+#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10)
+#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10)
+
+
+#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */
+#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */
+#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coordinate value */
+#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coordinate value */
+
+/*
* enum V7_PACKET_ID - defines the packet type for V7
* V7_PACKET_ID_IDLE: There's no finger and no button activity.
* V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
@@ -202,6 +278,7 @@ struct alps_data {
int prev_fin;
int multi_packet;
+ int second_touch;
unsigned char multi_data[6];
struct alps_fields f;
u8 quirks;
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index 58f4f6fa4857..efe148474e7f 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -723,7 +723,7 @@ static ssize_t cyapa_update_suspend_scanrate(struct device *dev,
} else if (sysfs_streq(buf, OFF_MODE_NAME)) {
cyapa->suspend_power_mode = PWR_MODE_OFF;
} else if (!kstrtou16(buf, 10, &sleep_time)) {
- cyapa->suspend_sleep_time = max_t(u16, sleep_time, 1000);
+ cyapa->suspend_sleep_time = min_t(u16, sleep_time, 1000);
cyapa->suspend_power_mode =
cyapa_sleep_time_to_pwr_cmd(cyapa->suspend_sleep_time);
} else {
@@ -840,7 +840,7 @@ static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev,
if (error)
return error;
- cyapa->runtime_suspend_sleep_time = max_t(u16, time, 1000);
+ cyapa->runtime_suspend_sleep_time = min_t(u16, time, 1000);
cyapa->runtime_suspend_power_mode =
cyapa_sleep_time_to_pwr_cmd(cyapa->runtime_suspend_sleep_time);
diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c
index 1e2291c378fe..3faf01c1b191 100644
--- a/drivers/input/mouse/cyapa_gen3.c
+++ b/drivers/input/mouse/cyapa_gen3.c
@@ -950,14 +950,13 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode)
* Device power mode can only be set when device is in operational mode.
*/
static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
- u16 always_unused)
+ u16 always_unused)
{
int ret;
u8 power;
int tries;
u16 sleep_time;
- always_unused = 0;
if (cyapa->state != CYAPA_STATE_OP)
return 0;
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index 5b611dd71e79..afc39e799da2 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -352,7 +352,7 @@ struct gen5_app_cmd_head {
u8 parameter_data[0]; /* Parameter data variable based on cmd_code */
} __packed;
-/* Applicaton get/set parameter command data structure */
+/* Application get/set parameter command data structure */
struct gen5_app_set_parameter_data {
u8 parameter_id;
u8 parameter_size;
@@ -832,7 +832,7 @@ static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data)
int ret;
/* 0x20 0x00 0xF7 is Gen5 Application HID Description Header;
- * 0x20 0x00 0xFF is Gen5 Booloader HID Description Header.
+ * 0x20 0x00 0xFF is Gen5 Bootloader HID Description Header.
*
* Must read HID Description content through out,
* otherwise Gen5 trackpad cannot response next command
@@ -1654,8 +1654,8 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
* that trackpad unable to report signal to wake system up
* in the special situation that system is in suspending, and
* at the same time, user touch trackpad to wake system up.
- * This function can avoid the data to be buffured when system
- * is suspending which may cause interrput line unable to be
+ * This function can avoid the data to be buffered when system
+ * is suspending which may cause interrupt line unable to be
* asserted again.
*/
cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
@@ -2546,16 +2546,11 @@ static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa)
gen5_pip->resp_sort_func(cyapa,
gen5_pip->irq_cmd_buf, length))) {
/*
- * Cover the Gen5 V1 firmware issue.
- * The issue is there is no interrut will be
- * asserted to notityf host to read a command
- * data out when always has finger touch on
- * trackpad during the command is issued to
- * trackad device.
- * This issue has the scenario is that,
- * user always has his fingers touched on
- * trackpad device when booting/rebooting
- * their chrome book.
+ * Work around the Gen5 V1 firmware
+ * that does not assert interrupt signalling
+ * that command response is ready if user
+ * keeps touching the trackpad while command
+ * is sent to the device.
*/
length = 0;
if (gen5_pip->resp_len)
diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h
index e100c1b31597..73670f2aebfd 100644
--- a/drivers/input/mouse/elan_i2c.h
+++ b/drivers/input/mouse/elan_i2c.h
@@ -17,7 +17,7 @@
*/
#ifndef _ELAN_I2C_H
-#define _ELAN_i2C_H
+#define _ELAN_I2C_H
#include <linux/types.h>
@@ -25,16 +25,16 @@
#define ETP_ENABLE_CALIBRATE 0x0002
#define ETP_DISABLE_CALIBRATE 0x0000
#define ETP_DISABLE_POWER 0x0001
+#define ETP_PRESSURE_OFFSET 25
/* IAP Firmware handling */
-#define ETP_FW_NAME "elan_i2c.bin"
+#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0"
+#define ETP_FW_NAME "elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin"
#define ETP_IAP_START_ADDR 0x0083
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
#define ETP_FW_IAP_INTF_ERR (1 << 4)
#define ETP_FW_PAGE_SIZE 64
-#define ETP_FW_VAILDPAGE_COUNT 768
#define ETP_FW_SIGNATURE_SIZE 6
-#define ETP_FW_SIGNATURE_ADDRESS 0xBFFA
struct i2c_client;
struct completion;
@@ -57,7 +57,8 @@ struct elan_transport_ops {
bool max_baseliune, u8 *value);
int (*get_version)(struct i2c_client *client, bool iap, u8 *version);
- int (*get_sm_version)(struct i2c_client *client, u8 *version);
+ int (*get_sm_version)(struct i2c_client *client,
+ u8* ic_type, u8 *version);
int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
int (*get_product_id)(struct i2c_client *client, u8 *id);
@@ -79,6 +80,8 @@ struct elan_transport_ops {
struct completion *reset_done);
int (*get_report)(struct i2c_client *client, u8 *report);
+ int (*get_pressure_adjustment)(struct i2c_client *client,
+ int *adjustment);
};
extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 7ce8bfe22d7e..62641f2adaf7 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -4,7 +4,7 @@
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
- * Version: 1.5.6
+ * Version: 1.5.9
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
@@ -40,8 +40,7 @@
#include "elan_i2c.h"
#define DRIVER_NAME "elan_i2c"
-#define ELAN_DRIVER_VERSION "1.5.6"
-#define ETP_PRESSURE_OFFSET 25
+#define ELAN_DRIVER_VERSION "1.5.9"
#define ETP_MAX_PRESSURE 255
#define ETP_FWIDTH_REDUCE 90
#define ETP_FINGER_WIDTH 15
@@ -53,6 +52,7 @@
#define ETP_REPORT_ID_OFFSET 2
#define ETP_TOUCH_INFO_OFFSET 3
#define ETP_FINGER_DATA_OFFSET 4
+#define ETP_HOVER_INFO_OFFSET 30
#define ETP_MAX_REPORT_LEN 34
/* The main device structure */
@@ -81,8 +81,11 @@ struct elan_tp_data {
u8 sm_version;
u8 iap_version;
u16 fw_checksum;
-
+ int pressure_adjustment;
u8 mode;
+ u8 ic_type;
+ u16 fw_vaildpage_count;
+ u16 fw_signature_address;
bool irq_wake;
@@ -91,6 +94,29 @@ struct elan_tp_data {
bool baseline_ready;
};
+static int elan_get_fwinfo(u8 ic_type, u16 *vaildpage_count,
+ u16 *signature_address)
+{
+ switch(ic_type) {
+ case 0x09:
+ *vaildpage_count = 768;
+ break;
+ case 0x0D:
+ *vaildpage_count = 896;
+ break;
+ default:
+ /* unknown ic type clear value */
+ *vaildpage_count = 0;
+ *signature_address = 0;
+ return -ENXIO;
+ }
+
+ *signature_address =
+ (*vaildpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
+
+ return 0;
+}
+
static int elan_enable_power(struct elan_tp_data *data)
{
int repeat = ETP_RETRY_COUNT;
@@ -99,7 +125,7 @@ static int elan_enable_power(struct elan_tp_data *data)
error = regulator_enable(data->vcc);
if (error) {
dev_err(&data->client->dev,
- "Failed to enable regulator: %d\n", error);
+ "failed to enable regulator: %d\n", error);
return error;
}
@@ -111,6 +137,7 @@ static int elan_enable_power(struct elan_tp_data *data)
msleep(30);
} while (--repeat > 0);
+ dev_err(&data->client->dev, "failed to enable power: %d\n", error);
return error;
}
@@ -125,7 +152,7 @@ static int elan_disable_power(struct elan_tp_data *data)
error = regulator_disable(data->vcc);
if (error) {
dev_err(&data->client->dev,
- "Failed to disable regulator: %d\n",
+ "failed to disable regulator: %d\n",
error);
/* Attempt to power the chip back up */
data->ops->power_control(data->client, true);
@@ -138,6 +165,7 @@ static int elan_disable_power(struct elan_tp_data *data)
msleep(30);
} while (--repeat > 0);
+ dev_err(&data->client->dev, "failed to disable power: %d\n", error);
return error;
}
@@ -196,7 +224,6 @@ static int elan_initialize(struct elan_tp_data *data)
if (!error)
return 0;
- repeat--;
msleep(30);
} while (--repeat > 0);
@@ -220,7 +247,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
if (error)
return error;
- error = data->ops->get_sm_version(data->client, &data->sm_version);
+ error = data->ops->get_sm_version(data->client, &data->ic_type,
+ &data->sm_version);
if (error)
return error;
@@ -228,6 +256,19 @@ static int elan_query_device_info(struct elan_tp_data *data)
if (error)
return error;
+ error = data->ops->get_pressure_adjustment(data->client,
+ &data->pressure_adjustment);
+ if (error)
+ return error;
+
+ error = elan_get_fwinfo(data->ic_type, &data->fw_vaildpage_count,
+ &data->fw_signature_address);
+ if (error) {
+ dev_err(&data->client->dev,
+ "unknown ic type %d\n", data->ic_type);
+ return error;
+ }
+
return 0;
}
@@ -312,7 +353,7 @@ static int __elan_update_firmware(struct elan_tp_data *data,
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
- for (i = boot_page_count; i < ETP_FW_VAILDPAGE_COUNT; i++) {
+ for (i = boot_page_count; i < data->fw_vaildpage_count; i++) {
u16 checksum = 0;
const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
@@ -397,7 +438,8 @@ static ssize_t elan_sysfs_read_product_id(struct device *dev,
struct i2c_client *client = to_i2c_client(dev);
struct elan_tp_data *data = i2c_get_clientdata(client);
- return sprintf(buf, "%d.0\n", data->product_id);
+ return sprintf(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n",
+ data->product_id);
}
static ssize_t elan_sysfs_read_fw_ver(struct device *dev,
@@ -436,19 +478,28 @@ static ssize_t elan_sysfs_update_fw(struct device *dev,
{
struct elan_tp_data *data = dev_get_drvdata(dev);
const struct firmware *fw;
+ char *fw_name;
int error;
const u8 *fw_signature;
static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
- error = request_firmware(&fw, ETP_FW_NAME, dev);
+ /* Look for a firmware with the product id appended. */
+ fw_name = kasprintf(GFP_KERNEL, ETP_FW_NAME, data->product_id);
+ if (!fw_name) {
+ dev_err(dev, "failed to allocate memory for firmware name\n");
+ return -ENOMEM;
+ }
+
+ dev_info(dev, "requesting fw '%s'\n", fw_name);
+ error = request_firmware(&fw, fw_name, dev);
+ kfree(fw_name);
if (error) {
- dev_err(dev, "cannot load firmware %s: %d\n",
- ETP_FW_NAME, error);
+ dev_err(dev, "failed to request firmware: %d\n", error);
return error;
}
/* Firmware file must match signature data */
- fw_signature = &fw->data[ETP_FW_SIGNATURE_ADDRESS];
+ fw_signature = &fw->data[data->fw_signature_address];
if (memcmp(fw_signature, signature, sizeof(signature)) != 0) {
dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n",
(int)sizeof(signature), signature,
@@ -720,13 +771,13 @@ static const struct attribute_group *elan_sysfs_groups[] = {
*/
static void elan_report_contact(struct elan_tp_data *data,
int contact_num, bool contact_valid,
- u8 *finger_data)
+ bool hover_event, u8 *finger_data)
{
struct input_dev *input = data->input;
unsigned int pos_x, pos_y;
unsigned int pressure, mk_x, mk_y;
- unsigned int area_x, area_y, major, minor, new_pressure;
-
+ unsigned int area_x, area_y, major, minor;
+ unsigned int scaled_pressure;
if (contact_valid) {
pos_x = ((finger_data[0] & 0xf0) << 4) |
@@ -755,15 +806,18 @@ static void elan_report_contact(struct elan_tp_data *data,
major = max(area_x, area_y);
minor = min(area_x, area_y);
- new_pressure = pressure + ETP_PRESSURE_OFFSET;
- if (new_pressure > ETP_MAX_PRESSURE)
- new_pressure = ETP_MAX_PRESSURE;
+ scaled_pressure = pressure + data->pressure_adjustment;
+
+ if (scaled_pressure > ETP_MAX_PRESSURE)
+ scaled_pressure = ETP_MAX_PRESSURE;
input_mt_slot(input, contact_num);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
input_report_abs(input, ABS_MT_POSITION_X, pos_x);
input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y);
- input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
+ input_report_abs(input, ABS_MT_DISTANCE, hover_event);
+ input_report_abs(input, ABS_MT_PRESSURE,
+ hover_event ? 0 : scaled_pressure);
input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
@@ -779,11 +833,14 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET];
int i;
u8 tp_info = packet[ETP_TOUCH_INFO_OFFSET];
- bool contact_valid;
+ u8 hover_info = packet[ETP_HOVER_INFO_OFFSET];
+ bool contact_valid, hover_event;
+ hover_event = hover_info & 0x40;
for (i = 0; i < ETP_MAX_FINGERS; i++) {
contact_valid = tp_info & (1U << (3 + i));
- elan_report_contact(data, i, contact_valid, finger_data);
+ elan_report_contact(data, i, contact_valid, hover_event,
+ finger_data);
if (contact_valid)
finger_data += ETP_FINGER_DATA_LEN;
@@ -877,6 +934,7 @@ static int elan_setup_input_device(struct elan_tp_data *data)
ETP_FINGER_WIDTH * max_width, 0, 0);
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
ETP_FINGER_WIDTH * min_width, 0, 0);
+ input_set_abs_params(input, ABS_MT_DISTANCE, 0, 1, 0, 0);
data->input = input;
@@ -1084,16 +1142,18 @@ static int __maybe_unused elan_resume(struct device *dev)
}
error = elan_enable_power(data);
- if (error)
+ if (error) {
dev_err(dev, "power up when resuming failed: %d\n", error);
+ goto err;
+ }
error = elan_initialize(data);
if (error)
dev_err(dev, "initialize when resuming failed: %d\n", error);
+err:
enable_irq(data->client->irq);
-
- return 0;
+ return error;
}
static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c
index 029941f861af..683c840c9dd7 100644
--- a/drivers/input/mouse/elan_i2c_i2c.c
+++ b/drivers/input/mouse/elan_i2c_i2c.c
@@ -41,6 +41,7 @@
#define ETP_I2C_MAX_X_AXIS_CMD 0x0106
#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107
#define ETP_I2C_RESOLUTION_CMD 0x0108
+#define ETP_I2C_PRESSURE_CMD 0x010A
#define ETP_I2C_IAP_VERSION_CMD 0x0110
#define ETP_I2C_SET_CMD 0x0300
#define ETP_I2C_POWER_CMD 0x0307
@@ -117,7 +118,15 @@ static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
int ret;
ret = i2c_transfer(client->adapter, &msg, 1);
- return ret == 1 ? 0 : (ret < 0 ? ret : -EIO);
+ if (ret != 1) {
+ if (ret >= 0)
+ ret = -EIO;
+ dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
}
static int elan_i2c_initialize(struct i2c_client *client)
@@ -250,7 +259,8 @@ static int elan_i2c_get_version(struct i2c_client *client,
return 0;
}
-static int elan_i2c_get_sm_version(struct i2c_client *client, u8 *version)
+static int elan_i2c_get_sm_version(struct i2c_client *client,
+ u8 *ic_type, u8 *version)
{
int error;
u8 val[3];
@@ -262,6 +272,7 @@ static int elan_i2c_get_sm_version(struct i2c_client *client, u8 *version)
}
*version = val[0];
+ *ic_type = val[1];
return 0;
}
@@ -356,8 +367,29 @@ static int elan_i2c_get_num_traces(struct i2c_client *client,
return error;
}
- *x_traces = val[0] - 1;
- *y_traces = val[1] - 1;
+ *x_traces = val[0];
+ *y_traces = val[1];
+
+ return 0;
+}
+
+static int elan_i2c_get_pressure_adjustment(struct i2c_client *client,
+ int *adjustment)
+{
+ int error;
+ u8 val[3];
+
+ error = elan_i2c_read_cmd(client, ETP_I2C_PRESSURE_CMD, val);
+ if (error) {
+ dev_err(&client->dev, "failed to get pressure format: %d\n",
+ error);
+ return error;
+ }
+
+ if ((val[0] >> 4) & 0x1)
+ *adjustment = 0;
+ else
+ *adjustment = ETP_PRESSURE_OFFSET;
return 0;
}
@@ -594,6 +626,7 @@ const struct elan_transport_ops elan_i2c_ops = {
.get_sm_version = elan_i2c_get_sm_version,
.get_product_id = elan_i2c_get_product_id,
.get_checksum = elan_i2c_get_checksum,
+ .get_pressure_adjustment = elan_i2c_get_pressure_adjustment,
.get_max = elan_i2c_get_max,
.get_resolution = elan_i2c_get_resolution,
diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c
index 06a2bcd1cda2..ff36a366b2aa 100644
--- a/drivers/input/mouse/elan_i2c_smbus.c
+++ b/drivers/input/mouse/elan_i2c_smbus.c
@@ -165,7 +165,8 @@ static int elan_smbus_get_version(struct i2c_client *client,
return 0;
}
-static int elan_smbus_get_sm_version(struct i2c_client *client, u8 *version)
+static int elan_smbus_get_sm_version(struct i2c_client *client,
+ u8 *ic_type, u8 *version)
{
int error;
u8 val[3];
@@ -177,7 +178,8 @@ static int elan_smbus_get_sm_version(struct i2c_client *client, u8 *version)
return error;
}
- *version = val[0]; /* XXX Why 0 and not 2 as in IAP/FW versions? */
+ *version = val[0];
+ *ic_type = val[1];
return 0;
}
@@ -268,12 +270,19 @@ static int elan_smbus_get_num_traces(struct i2c_client *client,
return error;
}
- *x_traces = val[1] - 1;
- *y_traces = val[2] - 1;
+ *x_traces = val[1];
+ *y_traces = val[2];
return 0;
}
+static int elan_smbus_get_pressure_adjustment(struct i2c_client *client,
+ int *adjustment)
+{
+ *adjustment = ETP_PRESSURE_OFFSET;
+ return 0;
+}
+
static int elan_smbus_iap_get_mode(struct i2c_client *client,
enum tp_mode *mode)
{
@@ -497,6 +506,7 @@ const struct elan_transport_ops elan_smbus_ops = {
.get_sm_version = elan_smbus_get_sm_version,
.get_product_id = elan_smbus_get_product_id,
.get_checksum = elan_smbus_get_checksum,
+ .get_pressure_adjustment = elan_smbus_get_pressure_adjustment,
.get_max = elan_smbus_get_max,
.get_resolution = elan_smbus_get_resolution,
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 6e22682c8255..ce3d40004458 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -315,7 +315,7 @@ static void elantech_report_semi_mt_data(struct input_dev *dev,
unsigned int x2, unsigned int y2)
{
elantech_set_slot(dev, 0, num_fingers != 0, x1, y1);
- elantech_set_slot(dev, 1, num_fingers == 2, x2, y2);
+ elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2);
}
/*
@@ -893,6 +893,21 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
}
/*
+ * This writes the reg_07 value again to the hardware at the end of every
+ * set_rate call because the register loses its value. reg_07 allows setting
+ * absolute mode on v4 hardware
+ */
+static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse,
+ unsigned int rate)
+{
+ struct elantech_data *etd = psmouse->private;
+
+ etd->original_set_rate(psmouse, rate);
+ if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
+ psmouse_err(psmouse, "restoring reg_07 failed\n");
+}
+
+/*
* Put the touchpad into absolute mode
*/
static int elantech_set_absolute_mode(struct psmouse *psmouse)
@@ -1094,6 +1109,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons
* Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons
* Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons
+ * Asus TP500LN 0x381f17 10, 14, 0e clickpad
+ * Asus X750JN 0x381f17 10, 14, 0e clickpad
* Asus UX31 0x361f00 20, 15, 0e clickpad
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
* Avatar AVIU-145A2 0x361f00 ? clickpad
@@ -1359,10 +1376,11 @@ static bool elantech_is_signature_valid(const unsigned char *param)
return true;
/*
- * Some models have a revision higher then 20. Meaning param[2] may
- * be 10 or 20, skip the rates check for these.
+ * Some hw_version >= 4 models have a revision higher then 20. Meaning
+ * that param[2] may be 10 or 20, skip the rates check for these.
*/
- if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40)
+ if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f &&
+ param[2] < 40)
return true;
for (i = 0; i < ARRAY_SIZE(rates); i++)
@@ -1538,6 +1556,7 @@ static int elantech_set_properties(struct elantech_data *etd)
case 9:
case 10:
case 13:
+ case 14:
etd->hw_version = 4;
break;
default:
@@ -1635,6 +1654,11 @@ int elantech_init(struct psmouse *psmouse)
goto init_fail;
}
+ if (etd->fw_version == 0x381f17) {
+ etd->original_set_rate = psmouse->set_rate;
+ psmouse->set_rate = elantech_set_rate_restore_reg_07;
+ }
+
if (elantech_set_input_params(psmouse)) {
psmouse_err(psmouse, "failed to query touchpad range.\n");
goto init_fail;
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index 6f3afec02f03..f965d1569cc3 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -142,6 +142,7 @@ struct elantech_data {
struct finger_pos mt[ETP_MAX_FINGERS];
unsigned char parity[256];
int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
+ void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
};
#ifdef CONFIG_MOUSE_PS2_ELANTECH
diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c
index 23d259416f2f..4d5576de81be 100644
--- a/drivers/input/mouse/focaltech.c
+++ b/drivers/input/mouse/focaltech.c
@@ -103,6 +103,16 @@ struct focaltech_hw_state {
*/
struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
+ /*
+ * Finger width 0-7 and 15 for a very big contact area.
+ * 15 value stays until the finger is released.
+ * Width is reported only in absolute packets.
+ * Since hardware reports width only for last touching finger,
+ * there is no need to store width for every specific finger,
+ * so we keep only last value reported.
+ */
+ unsigned int width;
+
/* True if the clickpad has been pressed. */
bool pressed;
};
@@ -137,6 +147,7 @@ static void focaltech_report_state(struct psmouse *psmouse)
input_report_abs(dev, ABS_MT_POSITION_X, clamped_x);
input_report_abs(dev, ABS_MT_POSITION_Y,
priv->y_max - clamped_y);
+ input_report_abs(dev, ABS_TOOL_WIDTH, state->width);
}
}
input_mt_report_pointer_emulation(dev, true);
@@ -187,6 +198,7 @@ static void focaltech_process_abs_packet(struct psmouse *psmouse,
state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
state->fingers[finger].y = (packet[3] << 8) | packet[4];
+ state->width = packet[5] >> 4;
state->fingers[finger].valid = true;
}
@@ -331,6 +343,7 @@ static void focaltech_set_input_params(struct psmouse *psmouse)
__set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
+ input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
}
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index 23222dd5a66f..e5ed216824e9 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -256,8 +256,8 @@ static void lifebook_disconnect(struct psmouse *psmouse)
int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
- if (!lifebook_present)
- return -1;
+ if (!lifebook_present)
+ return -1;
if (desired_serio_phys &&
strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
@@ -268,7 +268,7 @@ int lifebook_detect(struct psmouse *psmouse, bool set_properties)
psmouse->name = "Lifebook TouchScreen";
}
- return 0;
+ return 0;
}
static int lifebook_create_relative_device(struct psmouse *psmouse)
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 8bc61237bc1b..7c4ba43d253e 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -36,6 +36,7 @@
#include "sentelic.h"
#include "cypress_ps2.h"
#include "focaltech.h"
+#include "vmmouse.h"
#define DRIVER_DESC "PS/2 mouse driver"
@@ -62,7 +63,7 @@ static unsigned int psmouse_rate = 100;
module_param_named(rate, psmouse_rate, uint, 0644);
MODULE_PARM_DESC(rate, "Report rate, in reports per second.");
-static bool psmouse_smartscroll = 1;
+static bool psmouse_smartscroll = true;
module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
@@ -474,19 +475,45 @@ static int psmouse_poll(struct psmouse *psmouse)
PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
}
+static bool psmouse_check_pnp_id(const char *id, const char * const ids[])
+{
+ int i;
+
+ for (i = 0; ids[i]; i++)
+ if (!strcasecmp(id, ids[i]))
+ return true;
+
+ return false;
+}
+
/*
* psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids.
*/
bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
{
- int i;
-
- if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4))
- for (i = 0; ids[i]; i++)
- if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i]))
- return true;
+ struct serio *serio = psmouse->ps2dev.serio;
+ char *p, *fw_id_copy, *save_ptr;
+ bool found = false;
+
+ if (strncmp(serio->firmware_id, "PNP: ", 5))
+ return false;
+
+ fw_id_copy = kstrndup(&serio->firmware_id[5],
+ sizeof(serio->firmware_id) - 5,
+ GFP_KERNEL);
+ if (!fw_id_copy)
+ return false;
+
+ save_ptr = fw_id_copy;
+ while ((p = strsep(&fw_id_copy, " ")) != NULL) {
+ if (psmouse_check_pnp_id(p, ids)) {
+ found = true;
+ break;
+ }
+ }
- return false;
+ kfree(save_ptr);
+ return found;
}
/*
@@ -764,6 +791,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
}
}
+ if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) {
+ if (max_proto > PSMOUSE_IMEX) {
+ if (!set_properties || vmmouse_init(psmouse) == 0)
+ return PSMOUSE_VMMOUSE;
+ }
+ }
+
/*
* Try Kensington ThinkingMouse (we try first, because synaptics probe
* upsets the thinkingmouse).
@@ -1087,6 +1121,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.init = focaltech_init,
},
#endif
+#ifdef CONFIG_MOUSE_PS2_VMMOUSE
+ {
+ .type = PSMOUSE_VMMOUSE,
+ .name = VMMOUSE_PSNAME,
+ .alias = "vmmouse",
+ .detect = vmmouse_detect,
+ .init = vmmouse_init,
+ },
+#endif
{
.type = PSMOUSE_AUTO,
.name = "auto",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index d02e1bdc9ae4..ad5a5a1ea872 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -103,6 +103,7 @@ enum psmouse_type {
PSMOUSE_SYNAPTICS_RELATIVE,
PSMOUSE_CYPRESS,
PSMOUSE_FOCALTECH,
+ PSMOUSE_VMMOUSE,
PSMOUSE_AUTO /* This one should always be last */
};
diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h
index aa697ece405b..42df9e3beae8 100644
--- a/drivers/input/mouse/sentelic.h
+++ b/drivers/input/mouse/sentelic.h
@@ -123,11 +123,11 @@ struct fsp_data {
extern int fsp_detect(struct psmouse *psmouse, bool set_properties);
extern int fsp_init(struct psmouse *psmouse);
#else
-inline int fsp_detect(struct psmouse *psmouse, bool set_properties)
+static inline int fsp_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
-inline int fsp_init(struct psmouse *psmouse)
+static inline int fsp_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 3b06c8a360b6..35c8d0ceabee 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -67,6 +67,9 @@
#define X_MAX_POSITIVE 8176
#define Y_MAX_POSITIVE 8176
+/* maximum ABS_MT_POSITION displacement (in mm) */
+#define DMAX 10
+
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
****************************************************************************/
@@ -148,6 +151,11 @@ static const struct min_max_quirk min_max_pnpid_table[] = {
1024, 5112, 2024, 4832
},
{
+ (const char * const []){"LEN2000", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5113, 2021, 4832
+ },
+ {
(const char * const []){"LEN2001", NULL},
{ANY_BOARD_ID, ANY_BOARD_ID},
1024, 5022, 2508, 4832
@@ -188,7 +196,7 @@ static const char * const topbuttonpad_pnp_ids[] = {
"LEN0045",
"LEN0047",
"LEN0049",
- "LEN2000",
+ "LEN2000", /* S540 */
"LEN2001", /* Edge E431 */
"LEN2002", /* Edge E531 */
"LEN2003",
@@ -203,6 +211,13 @@ static const char * const topbuttonpad_pnp_ids[] = {
NULL
};
+/* This list has been kindly provided by Synaptics. */
+static const char * const forcepad_pnp_ids[] = {
+ "SYN300D",
+ "SYN3014",
+ NULL
+};
+
/*****************************************************************************
* Synaptics communications functions
****************************************************************************/
@@ -687,8 +702,6 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[],
hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits;
}
-static bool is_forcepad;
-
static int synaptics_parse_hw_state(const unsigned char buf[],
struct synaptics_data *priv,
struct synaptics_hw_state *hw)
@@ -718,7 +731,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
- if (is_forcepad) {
+ if (priv->is_forcepad) {
/*
* ForcePads, like Clickpads, use middle button
* bits to report primary button clicks.
@@ -917,7 +930,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
pos[i].y = synaptics_invert_y(hw[i]->y);
}
- input_mt_assign_slots(dev, slot, pos, nsemi, 0);
+ input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
for (i = 0; i < nsemi; i++) {
input_mt_slot(dev, slot[i]);
@@ -1186,7 +1199,7 @@ static void set_input_params(struct psmouse *psmouse,
ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
- input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
+ input_mt_init_slots(dev, 3, INPUT_MT_POINTER | INPUT_MT_TRACK);
/* Image sensors can signal 4 and 5 finger clicks */
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
@@ -1418,29 +1431,11 @@ static const struct dmi_system_id __initconst cr48_dmi_table[] = {
{ }
};
-static const struct dmi_system_id forcepad_dmi_table[] __initconst = {
-#if defined(CONFIG_DMI) && defined(CONFIG_X86)
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook Folio 1040 G1"),
- },
- },
-#endif
- { }
-};
-
void __init synaptics_module_init(void)
{
impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
broken_olpc_ec = dmi_check_system(olpc_dmi_table);
cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
-
- /*
- * Unfortunately ForcePad capability is not exported over PS/2,
- * so we have to resort to checking DMI.
- */
- is_forcepad = dmi_check_system(forcepad_dmi_table);
}
static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
@@ -1475,6 +1470,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
priv->disable_gesture = true;
+ /*
+ * Unfortunately ForcePad capability is not exported over PS/2,
+ * so we have to resort to checking PNP IDs.
+ */
+ priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
+
if (synaptics_set_mode(psmouse)) {
psmouse_err(psmouse, "Unable to initialize device.\n");
goto init_fail;
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index ee4bd0d12b26..56faa7ec4434 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -196,6 +196,7 @@ struct synaptics_data {
unsigned long press_start;
bool press;
bool report_press;
+ bool is_forcepad;
};
void synaptics_module_init(void);
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index 878f18498f3b..ffceedcaf3c8 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -185,7 +185,7 @@
#define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4)
/* Control touchpad's No Deceleration option */
-static bool no_decel = 1;
+static bool no_decel = true;
module_param(no_decel, bool, 0644);
MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)");
@@ -340,9 +340,9 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
s32 data;
s8 x_delta, y_delta;
- /* Deal with spontanious resets and errors */
+ /* Deal with spontaneous resets and errors */
if (synaptics_i2c_check_error(touch->client))
- return 0;
+ return false;
/* Get Gesture Bit */
data = synaptics_i2c_reg_get(touch->client, DATA_REG0);
diff --git a/drivers/input/mouse/vmmouse.c b/drivers/input/mouse/vmmouse.c
new file mode 100644
index 000000000000..e272f06258ce
--- /dev/null
+++ b/drivers/input/mouse/vmmouse.c
@@ -0,0 +1,508 @@
+/*
+ * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
+ *
+ * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
+ *
+ * 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.
+ *
+ * Twin device code is hugely inspired by the ALPS driver.
+ * Authors:
+ * Dmitry Torokhov <dmitry.torokhov@gmail.com>
+ * Thomas Hellstrom <thellstrom@vmware.com>
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/hypervisor.h>
+
+#include "psmouse.h"
+#include "vmmouse.h"
+
+#define VMMOUSE_PROTO_MAGIC 0x564D5868U
+#define VMMOUSE_PROTO_PORT 0x5658
+
+/*
+ * Main commands supported by the vmmouse hypervisor port.
+ */
+#define VMMOUSE_PROTO_CMD_GETVERSION 10
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_DATA 39
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS 40
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND 41
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT 86
+
+/*
+ * Subcommands for VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND
+ */
+#define VMMOUSE_CMD_ENABLE 0x45414552U
+#define VMMOUSE_CMD_DISABLE 0x000000f5U
+#define VMMOUSE_CMD_REQUEST_RELATIVE 0x4c455252U
+#define VMMOUSE_CMD_REQUEST_ABSOLUTE 0x53424152U
+
+#define VMMOUSE_ERROR 0xffff0000U
+
+#define VMMOUSE_VERSION_ID 0x3442554aU
+
+#define VMMOUSE_RELATIVE_PACKET 0x00010000U
+
+#define VMMOUSE_LEFT_BUTTON 0x20
+#define VMMOUSE_RIGHT_BUTTON 0x10
+#define VMMOUSE_MIDDLE_BUTTON 0x08
+
+/*
+ * VMMouse Restrict command
+ */
+#define VMMOUSE_RESTRICT_ANY 0x00
+#define VMMOUSE_RESTRICT_CPL0 0x01
+#define VMMOUSE_RESTRICT_IOPL 0x02
+
+#define VMMOUSE_MAX_X 0xFFFF
+#define VMMOUSE_MAX_Y 0xFFFF
+
+#define VMMOUSE_VENDOR "VMware"
+#define VMMOUSE_NAME "VMMouse"
+
+/**
+ * struct vmmouse_data - private data structure for the vmmouse driver
+ *
+ * @abs_dev: "Absolute" device used to report absolute mouse movement.
+ * @phys: Physical path for the absolute device.
+ * @dev_name: Name attribute name for the absolute device.
+ */
+struct vmmouse_data {
+ struct input_dev *abs_dev;
+ char phys[32];
+ char dev_name[128];
+};
+
+/**
+ * Hypervisor-specific bi-directional communication channel
+ * implementing the vmmouse protocol. Should never execute on
+ * bare metal hardware.
+ */
+#define VMMOUSE_CMD(cmd, in1, out1, out2, out3, out4) \
+({ \
+ unsigned long __dummy1, __dummy2; \
+ __asm__ __volatile__ ("inl %%dx" : \
+ "=a"(out1), \
+ "=b"(out2), \
+ "=c"(out3), \
+ "=d"(out4), \
+ "=S"(__dummy1), \
+ "=D"(__dummy2) : \
+ "a"(VMMOUSE_PROTO_MAGIC), \
+ "b"(in1), \
+ "c"(VMMOUSE_PROTO_CMD_##cmd), \
+ "d"(VMMOUSE_PROTO_PORT) : \
+ "memory"); \
+})
+
+/**
+ * vmmouse_report_button - report button state on the correct input device
+ *
+ * @psmouse: Pointer to the psmouse struct
+ * @abs_dev: The absolute input device
+ * @rel_dev: The relative input device
+ * @pref_dev: The preferred device for reporting
+ * @code: Button code
+ * @value: Button value
+ *
+ * Report @value and @code on @pref_dev, unless the button is already
+ * pressed on the other device, in which case the state is reported on that
+ * device.
+ */
+static void vmmouse_report_button(struct psmouse *psmouse,
+ struct input_dev *abs_dev,
+ struct input_dev *rel_dev,
+ struct input_dev *pref_dev,
+ unsigned int code, int value)
+{
+ if (test_bit(code, abs_dev->key))
+ pref_dev = abs_dev;
+ else if (test_bit(code, rel_dev->key))
+ pref_dev = rel_dev;
+
+ input_report_key(pref_dev, code, value);
+}
+
+/**
+ * vmmouse_report_events - process events on the vmmouse communications channel
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * This function pulls events from the vmmouse communications channel and
+ * reports them on the correct (absolute or relative) input device. When the
+ * communications channel is drained, or if we've processed more than 255
+ * psmouse commands, the function returns PSMOUSE_FULL_PACKET. If there is a
+ * host- or synchronization error, the function returns PSMOUSE_BAD_DATA in
+ * the hope that the caller will reset the communications channel.
+ */
+static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
+{
+ struct input_dev *rel_dev = psmouse->dev;
+ struct vmmouse_data *priv = psmouse->private;
+ struct input_dev *abs_dev = priv->abs_dev;
+ struct input_dev *pref_dev;
+ u32 status, x, y, z;
+ u32 dummy1, dummy2, dummy3;
+ unsigned int queue_length;
+ unsigned int count = 255;
+
+ while (count--) {
+ /* See if we have motion data. */
+ VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
+ status, dummy1, dummy2, dummy3);
+ if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
+ psmouse_err(psmouse, "failed to fetch status data\n");
+ /*
+ * After a few attempts this will result in
+ * reconnect.
+ */
+ return PSMOUSE_BAD_DATA;
+ }
+
+ queue_length = status & 0xffff;
+ if (queue_length == 0)
+ break;
+
+ if (queue_length % 4) {
+ psmouse_err(psmouse, "invalid queue length\n");
+ return PSMOUSE_BAD_DATA;
+ }
+
+ /* Now get it */
+ VMMOUSE_CMD(ABSPOINTER_DATA, 4, status, x, y, z);
+
+ /*
+ * And report what we've got. Prefer to report button
+ * events on the same device where we report motion events.
+ * This doesn't work well with the mouse wheel, though. See
+ * below. Ideally we would want to report that on the
+ * preferred device as well.
+ */
+ if (status & VMMOUSE_RELATIVE_PACKET) {
+ pref_dev = rel_dev;
+ input_report_rel(rel_dev, REL_X, (s32)x);
+ input_report_rel(rel_dev, REL_Y, -(s32)y);
+ } else {
+ pref_dev = abs_dev;
+ input_report_abs(abs_dev, ABS_X, x);
+ input_report_abs(abs_dev, ABS_Y, y);
+ }
+
+ /* Xorg seems to ignore wheel events on absolute devices */
+ input_report_rel(rel_dev, REL_WHEEL, -(s8)((u8) z));
+
+ vmmouse_report_button(psmouse, abs_dev, rel_dev,
+ pref_dev, BTN_LEFT,
+ status & VMMOUSE_LEFT_BUTTON);
+ vmmouse_report_button(psmouse, abs_dev, rel_dev,
+ pref_dev, BTN_RIGHT,
+ status & VMMOUSE_RIGHT_BUTTON);
+ vmmouse_report_button(psmouse, abs_dev, rel_dev,
+ pref_dev, BTN_MIDDLE,
+ status & VMMOUSE_MIDDLE_BUTTON);
+ input_sync(abs_dev);
+ input_sync(rel_dev);
+ }
+
+ return PSMOUSE_FULL_PACKET;
+}
+
+/**
+ * vmmouse_process_byte - process data on the ps/2 channel
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * When the ps/2 channel indicates that there is vmmouse data available,
+ * call vmmouse channel processing. Otherwise, continue to accept bytes. If
+ * there is a synchronization or communication data error, return
+ * PSMOUSE_BAD_DATA in the hope that the caller will reset the mouse.
+ */
+static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse)
+{
+ unsigned char *packet = psmouse->packet;
+
+ switch (psmouse->pktcnt) {
+ case 1:
+ return (packet[0] & 0x8) == 0x8 ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+
+ case 2:
+ return PSMOUSE_GOOD_DATA;
+
+ default:
+ return vmmouse_report_events(psmouse);
+ }
+}
+
+/**
+ * vmmouse_disable - Disable vmmouse
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to disable vmmouse mode.
+ */
+static void vmmouse_disable(struct psmouse *psmouse)
+{
+ u32 status;
+ u32 dummy1, dummy2, dummy3, dummy4;
+
+ VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE,
+ dummy1, dummy2, dummy3, dummy4);
+
+ VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
+ status, dummy1, dummy2, dummy3);
+
+ if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR)
+ psmouse_warn(psmouse, "failed to disable vmmouse device\n");
+}
+
+/**
+ * vmmouse_enable - Enable vmmouse and request absolute mode.
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to enable vmmouse mode. Performs basic checks and requests
+ * absolute vmmouse mode.
+ * Returns 0 on success, -ENODEV on failure.
+ */
+static int vmmouse_enable(struct psmouse *psmouse)
+{
+ u32 status, version;
+ u32 dummy1, dummy2, dummy3, dummy4;
+
+ /*
+ * Try enabling the device. If successful, we should be able to
+ * read valid version ID back from it.
+ */
+ VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE,
+ dummy1, dummy2, dummy3, dummy4);
+
+ /*
+ * See if version ID can be retrieved.
+ */
+ VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status, dummy1, dummy2, dummy3);
+ if ((status & 0x0000ffff) == 0) {
+ psmouse_dbg(psmouse, "empty flags - assuming no device\n");
+ return -ENXIO;
+ }
+
+ VMMOUSE_CMD(ABSPOINTER_DATA, 1 /* single item */,
+ version, dummy1, dummy2, dummy3);
+ if (version != VMMOUSE_VERSION_ID) {
+ psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n",
+ (unsigned) version, VMMOUSE_VERSION_ID);
+ vmmouse_disable(psmouse);
+ return -ENXIO;
+ }
+
+ /*
+ * Restrict ioport access, if possible.
+ */
+ VMMOUSE_CMD(ABSPOINTER_RESTRICT, VMMOUSE_RESTRICT_CPL0,
+ dummy1, dummy2, dummy3, dummy4);
+
+ VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_REQUEST_ABSOLUTE,
+ dummy1, dummy2, dummy3, dummy4);
+
+ return 0;
+}
+
+/*
+ * Array of supported hypervisors.
+ */
+static const struct hypervisor_x86 *vmmouse_supported_hypervisors[] = {
+ &x86_hyper_vmware,
+#ifdef CONFIG_KVM_GUEST
+ &x86_hyper_kvm,
+#endif
+};
+
+/**
+ * vmmouse_check_hypervisor - Check if we're running on a supported hypervisor
+ */
+static bool vmmouse_check_hypervisor(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vmmouse_supported_hypervisors); i++)
+ if (vmmouse_supported_hypervisors[i] == x86_hyper)
+ return true;
+
+ return false;
+}
+
+/**
+ * vmmouse_detect - Probe whether vmmouse is available
+ *
+ * @psmouse: Pointer to the psmouse struct
+ * @set_properties: Whether to set psmouse name and vendor
+ *
+ * Returns 0 if vmmouse channel is available. Negative error code if not.
+ */
+int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
+{
+ u32 response, version, dummy1, dummy2;
+
+ if (!vmmouse_check_hypervisor()) {
+ psmouse_dbg(psmouse,
+ "VMMouse not running on supported hypervisor.\n");
+ return -ENXIO;
+ }
+
+ if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) {
+ psmouse_dbg(psmouse, "VMMouse port in use.\n");
+ return -EBUSY;
+ }
+
+ /* Check if the device is present */
+ response = ~VMMOUSE_PROTO_MAGIC;
+ VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2);
+ if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU) {
+ release_region(VMMOUSE_PROTO_PORT, 4);
+ return -ENXIO;
+ }
+
+ if (set_properties) {
+ psmouse->vendor = VMMOUSE_VENDOR;
+ psmouse->name = VMMOUSE_NAME;
+ psmouse->model = version;
+ }
+
+ release_region(VMMOUSE_PROTO_PORT, 4);
+
+ return 0;
+}
+
+/**
+ * vmmouse_disconnect - Take down vmmouse driver
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Takes down vmmouse driver and frees resources set up in vmmouse_init().
+ */
+static void vmmouse_disconnect(struct psmouse *psmouse)
+{
+ struct vmmouse_data *priv = psmouse->private;
+
+ vmmouse_disable(psmouse);
+ psmouse_reset(psmouse);
+ input_unregister_device(priv->abs_dev);
+ kfree(priv);
+ release_region(VMMOUSE_PROTO_PORT, 4);
+}
+
+/**
+ * vmmouse_reconnect - Reset the ps/2 - and vmmouse connections
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Attempts to reset the mouse connections. Returns 0 on success and
+ * -1 on failure.
+ */
+static int vmmouse_reconnect(struct psmouse *psmouse)
+{
+ int error;
+
+ psmouse_reset(psmouse);
+ vmmouse_disable(psmouse);
+ error = vmmouse_enable(psmouse);
+ if (error) {
+ psmouse_err(psmouse,
+ "Unable to re-enable mouse when reconnecting, err: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+/**
+ * vmmouse_init - Initialize the vmmouse driver
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Requests the device and tries to enable vmmouse mode.
+ * If successful, sets up the input device for relative movement events.
+ * It also allocates another input device and sets it up for absolute motion
+ * events. Returns 0 on success and -1 on failure.
+ */
+int vmmouse_init(struct psmouse *psmouse)
+{
+ struct vmmouse_data *priv;
+ struct input_dev *rel_dev = psmouse->dev, *abs_dev;
+ int error;
+
+ if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) {
+ psmouse_dbg(psmouse, "VMMouse port in use.\n");
+ return -EBUSY;
+ }
+
+ psmouse_reset(psmouse);
+ error = vmmouse_enable(psmouse);
+ if (error)
+ goto release_region;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ abs_dev = input_allocate_device();
+ if (!priv || !abs_dev) {
+ error = -ENOMEM;
+ goto init_fail;
+ }
+
+ priv->abs_dev = abs_dev;
+ psmouse->private = priv;
+
+ input_set_capability(rel_dev, EV_REL, REL_WHEEL);
+
+ /* Set up and register absolute device */
+ snprintf(priv->phys, sizeof(priv->phys), "%s/input1",
+ psmouse->ps2dev.serio->phys);
+
+ /* Mimic name setup for relative device in psmouse-base.c */
+ snprintf(priv->dev_name, sizeof(priv->dev_name), "%s %s %s",
+ VMMOUSE_PSNAME, VMMOUSE_VENDOR, VMMOUSE_NAME);
+ abs_dev->phys = priv->phys;
+ abs_dev->name = priv->dev_name;
+ abs_dev->id.bustype = BUS_I8042;
+ abs_dev->id.vendor = 0x0002;
+ abs_dev->id.product = PSMOUSE_VMMOUSE;
+ abs_dev->id.version = psmouse->model;
+ abs_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+
+ error = input_register_device(priv->abs_dev);
+ if (error)
+ goto init_fail;
+
+ /* Set absolute device capabilities */
+ input_set_capability(abs_dev, EV_KEY, BTN_LEFT);
+ input_set_capability(abs_dev, EV_KEY, BTN_RIGHT);
+ input_set_capability(abs_dev, EV_KEY, BTN_MIDDLE);
+ input_set_capability(abs_dev, EV_ABS, ABS_X);
+ input_set_capability(abs_dev, EV_ABS, ABS_Y);
+ input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0);
+ input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0);
+
+ psmouse->protocol_handler = vmmouse_process_byte;
+ psmouse->disconnect = vmmouse_disconnect;
+ psmouse->reconnect = vmmouse_reconnect;
+
+ return 0;
+
+init_fail:
+ vmmouse_disable(psmouse);
+ psmouse_reset(psmouse);
+ input_free_device(abs_dev);
+ kfree(priv);
+ psmouse->private = NULL;
+
+release_region:
+ release_region(VMMOUSE_PROTO_PORT, 4);
+
+ return error;
+}
diff --git a/drivers/input/mouse/vmmouse.h b/drivers/input/mouse/vmmouse.h
new file mode 100644
index 000000000000..6f126017a24c
--- /dev/null
+++ b/drivers/input/mouse/vmmouse.h
@@ -0,0 +1,30 @@
+/*
+ * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
+ *
+ * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
+ *
+ * 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 _VMMOUSE_H
+#define _VMMOUSE_H
+
+#ifdef CONFIG_MOUSE_PS2_VMMOUSE
+#define VMMOUSE_PSNAME "VirtualPS/2"
+
+int vmmouse_detect(struct psmouse *psmouse, bool set_properties);
+int vmmouse_init(struct psmouse *psmouse);
+#else
+static inline int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
+{
+ return -ENOSYS;
+}
+static inline int vmmouse_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index 94ab494a6ade..ecba666afadb 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -31,7 +31,6 @@
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/ioport.h>
-#include <linux/pci_ids.h>
#include <asm/irq.h>
#include <asm/io.h>
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 986a71c614b0..cb5ece77fd7d 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -1162,13 +1162,32 @@ static int i8042_controller_resume(bool force_reset)
static int i8042_pm_suspend(struct device *dev)
{
+ int i;
+
i8042_controller_reset(true);
+ /* Set up serio interrupts for system wakeup. */
+ for (i = 0; i < I8042_NUM_PORTS; i++) {
+ struct serio *serio = i8042_ports[i].serio;
+
+ if (serio && device_may_wakeup(&serio->dev))
+ enable_irq_wake(i8042_ports[i].irq);
+ }
+
return 0;
}
static int i8042_pm_resume(struct device *dev)
{
+ int i;
+
+ for (i = 0; i < I8042_NUM_PORTS; i++) {
+ struct serio *serio = i8042_ports[i].serio;
+
+ if (serio && device_may_wakeup(&serio->dev))
+ disable_irq_wake(i8042_ports[i].irq);
+ }
+
/*
* On resume from S2R we always try to reset the controller
* to bring it in a sane state. (In case of S2D we expect
@@ -1300,13 +1319,16 @@ static void __init i8042_register_ports(void)
int i;
for (i = 0; i < I8042_NUM_PORTS; i++) {
- if (i8042_ports[i].serio) {
+ struct serio *serio = i8042_ports[i].serio;
+
+ if (serio) {
printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
- i8042_ports[i].serio->name,
+ serio->name,
(unsigned long) I8042_DATA_REG,
(unsigned long) I8042_COMMAND_REG,
i8042_ports[i].irq);
- serio_register_port(i8042_ports[i].serio);
+ serio_register_port(serio);
+ device_set_wakeup_capable(&serio->dev, true);
}
}
}
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 6261fd6d7c3c..7afa6a2b776c 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -140,6 +140,19 @@ config TOUCHSCREEN_BU21013
To compile this driver as a module, choose M here: the
module will be called bu21013_ts.
+config TOUCHSCREEN_CHIPONE_ICN8318
+ tristate "chipone icn8318 touchscreen controller"
+ depends on GPIOLIB
+ depends on I2C
+ depends on OF
+ help
+ Say Y here if you have a ChipOne icn8318 based I2C touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called chipone_icn8318.
+
config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen"
depends on I2C
@@ -297,11 +310,12 @@ config TOUCHSCREEN_FUJITSU
config TOUCHSCREEN_GOODIX
tristate "Goodix I2C touchscreen"
- depends on I2C && ACPI
+ depends on I2C
help
Say Y here if you have the Goodix touchscreen (such as one
installed in Onda v975w tablets) connected to your
- system.
+ system. It also supports 5-finger chip models, which can be
+ found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
If unsure, say N.
@@ -323,6 +337,18 @@ config TOUCHSCREEN_ILI210X
To compile this driver as a module, choose M here: the
module will be called ili210x.
+config TOUCHSCREEN_IPROC
+ tristate "IPROC touch panel driver support"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ help
+ Say Y here if you want to add support for the IPROC touch
+ controller to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm_iproc_tsc.
+
config TOUCHSCREEN_S3C2410
tristate "Samsung S3C2410/generic touchscreen input driver"
depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
@@ -932,6 +958,7 @@ config TOUCHSCREEN_ST1232
config TOUCHSCREEN_STMPE
tristate "STMicroelectronics STMPE touchscreens"
depends on MFD_STMPE
+ depends on (OF || COMPILE_TEST)
help
Say Y here if you want support for STMicroelectronics
STMPE touchscreen controllers.
@@ -954,7 +981,9 @@ config TOUCHSCREEN_SUN4I
config TOUCHSCREEN_SUR40
tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
depends on USB
+ depends on MEDIA_USB_SUPPORT
select INPUT_POLLDEV
+ select VIDEOBUF2_DMA_SG
help
Say Y here if you want support for the Samsung SUR40 touchscreen
(also known as Microsoft Surface 2.0 or Microsoft PixelSense).
@@ -962,6 +991,17 @@ config TOUCHSCREEN_SUR40
To compile this driver as a module, choose M here: the
module will be called sur40.
+config TOUCHSCREEN_SX8654
+ tristate "Semtech SX8654 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a Semtech SX8654 touchscreen controller.
+
+ If unsure, say N
+
+ To compile this driver as a module, choose M here: the
+ module will be called sx8654.
+
config TOUCHSCREEN_TPS6507X
tristate "TPS6507x based touchscreens"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 0242fea2102a..44deea743d02 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
@@ -79,5 +81,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
index ba30578e296e..f0b954d46a25 100644
--- a/drivers/input/touchscreen/ar1021_i2c.c
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -157,7 +157,7 @@ static const struct i2c_device_id ar1021_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
-static struct of_device_id ar1021_i2c_of_match[] = {
+static const struct of_device_id ar1021_i2c_of_match[] = {
{ .compatible = "microchip,ar1021-i2c", },
{ }
};
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 95ee92a91bd2..dfc7309e3d38 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -14,6 +14,8 @@
*
*/
+#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/completion.h>
@@ -25,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
/* Version */
#define MXT_VER_20 20
@@ -79,6 +82,7 @@
#define MXT_SPT_DIGITIZER_T43 43
#define MXT_SPT_MESSAGECOUNT_T44 44
#define MXT_SPT_CTECONFIG_T46 46
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
/* MXT_GEN_MESSAGE_T5 object */
#define MXT_RPTID_NOMSG 0xff
@@ -185,6 +189,36 @@ struct t9_range {
#define MXT_RESET_VALUE 0x01
#define MXT_BACKUP_VALUE 0x55
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL 0
+#define MXT_T100_CFG1 1
+#define MXT_T100_TCHAUX 3
+#define MXT_T100_XRANGE 13
+#define MXT_T100_YRANGE 24
+
+#define MXT_T100_CFG_SWITCHXY BIT(5)
+
+#define MXT_T100_TCHAUX_VECT BIT(0)
+#define MXT_T100_TCHAUX_AMPL BIT(1)
+#define MXT_T100_TCHAUX_AREA BIT(2)
+
+#define MXT_T100_DETECT BIT(7)
+#define MXT_T100_TYPE_MASK 0x70
+
+enum t100_type {
+ MXT_T100_TYPE_FINGER = 1,
+ MXT_T100_TYPE_PASSIVE_STYLUS = 2,
+ MXT_T100_TYPE_HOVERING_FINGER = 4,
+ MXT_T100_TYPE_GLOVE = 5,
+ MXT_T100_TYPE_LARGE_TOUCH = 6,
+};
+
+#define MXT_DISTANCE_ACTIVE_TOUCH 0
+#define MXT_DISTANCE_HOVERING 1
+
+#define MXT_TOUCH_MAJOR_DEFAULT 1
+#define MXT_PRESSURE_DEFAULT 1
+
/* Delay times */
#define MXT_BACKUP_TIME 50 /* msec */
#define MXT_RESET_TIME 200 /* msec */
@@ -244,6 +278,9 @@ struct mxt_data {
unsigned int max_y;
bool in_bootloader;
u16 mem_size;
+ u8 t100_aux_ampl;
+ u8 t100_aux_area;
+ u8 t100_aux_vect;
u8 max_reportid;
u32 config_crc;
u32 info_crc;
@@ -253,6 +290,7 @@ struct mxt_data {
bool update_input;
u8 last_message_count;
u8 num_touchids;
+ u8 multitouch;
/* Cached parameters from object table */
u16 T5_address;
@@ -264,6 +302,8 @@ struct mxt_data {
u8 T9_reportid_max;
u8 T19_reportid;
u16 T44_address;
+ u8 T100_reportid_min;
+ u8 T100_reportid_max;
/* for fw update in bootloader */
struct completion bl_completion;
@@ -686,15 +726,15 @@ static void mxt_input_button(struct mxt_data *data, u8 *message)
{
struct input_dev *input = data->input_dev;
const struct mxt_platform_data *pdata = data->pdata;
- bool button;
int i;
- /* Active-low switch */
for (i = 0; i < pdata->t19_num_keys; i++) {
if (pdata->t19_keymap[i] == KEY_RESERVED)
continue;
- button = !(message[1] & (1 << i));
- input_report_key(input, pdata->t19_keymap[i], button);
+
+ /* Active-low switch */
+ input_report_key(input, pdata->t19_keymap[i],
+ !(message[1] & BIT(i)));
}
}
@@ -771,6 +811,114 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
data->update_input = true;
}
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
+ u8 type = 0;
+ u16 x;
+ u16 y;
+ int distance = 0;
+ int tool = 0;
+ u8 major = 0;
+ u8 pressure = 0;
+ u8 orientation = 0;
+
+ id = message[0] - data->T100_reportid_min - 2;
+
+ /* ignore SCRSTATUS events */
+ if (id < 0)
+ return;
+
+ status = message[1];
+ x = get_unaligned_le16(&message[2]);
+ y = get_unaligned_le16(&message[4]);
+
+ if (status & MXT_T100_DETECT) {
+ type = (status & MXT_T100_TYPE_MASK) >> 4;
+
+ switch (type) {
+ case MXT_T100_TYPE_HOVERING_FINGER:
+ tool = MT_TOOL_FINGER;
+ distance = MXT_DISTANCE_HOVERING;
+
+ if (data->t100_aux_vect)
+ orientation = message[data->t100_aux_vect];
+
+ break;
+
+ case MXT_T100_TYPE_FINGER:
+ case MXT_T100_TYPE_GLOVE:
+ tool = MT_TOOL_FINGER;
+ distance = MXT_DISTANCE_ACTIVE_TOUCH;
+
+ if (data->t100_aux_area)
+ major = message[data->t100_aux_area];
+
+ if (data->t100_aux_ampl)
+ pressure = message[data->t100_aux_ampl];
+
+ if (data->t100_aux_vect)
+ orientation = message[data->t100_aux_vect];
+
+ break;
+
+ case MXT_T100_TYPE_PASSIVE_STYLUS:
+ tool = MT_TOOL_PEN;
+
+ /*
+ * Passive stylus is reported with size zero so
+ * hardcode.
+ */
+ major = MXT_TOUCH_MAJOR_DEFAULT;
+
+ if (data->t100_aux_ampl)
+ pressure = message[data->t100_aux_ampl];
+
+ break;
+
+ case MXT_T100_TYPE_LARGE_TOUCH:
+ /* Ignore suppressed touch */
+ break;
+
+ default:
+ dev_dbg(dev, "Unexpected T100 type\n");
+ return;
+ }
+ }
+
+ /*
+ * Values reported should be non-zero if tool is touching the
+ * device
+ */
+ if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
+ pressure = MXT_PRESSURE_DEFAULT;
+
+ input_mt_slot(input_dev, id);
+
+ if (status & MXT_T100_DETECT) {
+ dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
+ id, type, x, y, major, pressure, orientation);
+
+ input_mt_report_slot_state(input_dev, tool, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+ input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
+ input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
+ } else {
+ dev_dbg(dev, "[%u] release\n", id);
+
+ /* close out slot */
+ input_mt_report_slot_state(input_dev, 0, 0);
+ }
+
+ data->update_input = true;
+}
+
static int mxt_proc_message(struct mxt_data *data, u8 *message)
{
u8 report_id = message[0];
@@ -786,9 +934,12 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message)
* is not yet registered.
*/
mxt_dump_message(data, message);
- } else if (report_id >= data->T9_reportid_min
- && report_id <= data->T9_reportid_max) {
+ } else if (report_id >= data->T9_reportid_min &&
+ report_id <= data->T9_reportid_max) {
mxt_proc_t9_message(data, message);
+ } else if (report_id >= data->T100_reportid_min &&
+ report_id <= data->T100_reportid_max) {
+ mxt_proc_t100_message(data, message);
} else if (report_id == data->T19_reportid) {
mxt_input_button(data, message);
data->update_input = true;
@@ -1411,6 +1562,8 @@ static void mxt_free_object_table(struct mxt_data *data)
data->T9_reportid_max = 0;
data->T19_reportid = 0;
data->T44_address = 0;
+ data->T100_reportid_min = 0;
+ data->T100_reportid_max = 0;
data->max_reportid = 0;
}
@@ -1487,6 +1640,7 @@ static int mxt_get_object_table(struct mxt_data *data)
data->T7_address = object->start_address;
break;
case MXT_TOUCH_MULTI_T9:
+ data->multitouch = MXT_TOUCH_MULTI_T9;
data->T9_reportid_min = min_id;
data->T9_reportid_max = max_id;
data->num_touchids = object->num_report_ids
@@ -1498,6 +1652,13 @@ static int mxt_get_object_table(struct mxt_data *data)
case MXT_SPT_GPIOPWM_T19:
data->T19_reportid = min_id;
break;
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
+ data->T100_reportid_min = min_id;
+ data->T100_reportid_max = max_id;
+ /* first two report IDs reserved */
+ data->num_touchids = object->num_report_ids - 2;
+ break;
}
end_address = object->start_address
@@ -1582,22 +1743,138 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
return 0;
}
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ struct mxt_object *object;
+ u16 range_x, range_y;
+ u8 cfg, tchaux;
+ u8 aux;
+
+ object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_XRANGE,
+ sizeof(range_x), &range_x);
+ if (error)
+ return error;
+
+ le16_to_cpus(&range_x);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_YRANGE,
+ sizeof(range_y), &range_y);
+ if (error)
+ return error;
+
+ le16_to_cpus(&range_y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_CFG1,
+ 1, &cfg);
+ if (error)
+ return error;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_TCHAUX,
+ 1, &tchaux);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range_x == 0)
+ range_x = 1023;
+
+ if (range_y == 0)
+ range_y = 1023;
+
+ if (cfg & MXT_T100_CFG_SWITCHXY) {
+ data->max_x = range_y;
+ data->max_y = range_x;
+ } else {
+ data->max_x = range_x;
+ data->max_y = range_y;
+ }
+
+ /* allocate aux bytes */
+ aux = 6;
+
+ if (tchaux & MXT_T100_TCHAUX_VECT)
+ data->t100_aux_vect = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AMPL)
+ data->t100_aux_ampl = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AREA)
+ data->t100_aux_area = aux++;
+
+ dev_dbg(&client->dev,
+ "T100 aux mappings vect:%u ampl:%u area:%u\n",
+ data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
+
+ dev_info(&client->dev,
+ "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
static int mxt_input_open(struct input_dev *dev);
static void mxt_input_close(struct input_dev *dev);
-static int mxt_initialize_t9_input_device(struct mxt_data *data)
+static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
+ struct mxt_data *data)
{
- struct device *dev = &data->client->dev;
const struct mxt_platform_data *pdata = data->pdata;
+ int i;
+
+ input_dev->name = "Atmel maXTouch Touchpad";
+
+ __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+ input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ MXT_PIXELS_PER_MM);
+
+ for (i = 0; i < pdata->t19_num_keys; i++)
+ if (pdata->t19_keymap[i] != KEY_RESERVED)
+ input_set_capability(input_dev, EV_KEY,
+ pdata->t19_keymap[i]);
+}
+
+static int mxt_initialize_input_device(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct device *dev = &data->client->dev;
struct input_dev *input_dev;
int error;
unsigned int num_mt_slots;
unsigned int mt_flags = 0;
- int i;
- error = mxt_read_t9_resolution(data);
- if (error)
- dev_warn(dev, "Failed to initialize T9 resolution\n");
+ switch (data->multitouch) {
+ case MXT_TOUCH_MULTI_T9:
+ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+ error = mxt_read_t9_resolution(data);
+ if (error)
+ dev_warn(dev, "Failed to initialize T9 resolution\n");
+ break;
+
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ num_mt_slots = data->num_touchids;
+ error = mxt_read_t100_config(data);
+ if (error)
+ dev_warn(dev, "Failed to read T100 config\n");
+ break;
+
+ default:
+ dev_err(dev, "Invalid multitouch object\n");
+ return -EINVAL;
+ }
input_dev = input_allocate_device();
if (!input_dev) {
@@ -1612,54 +1889,76 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)
input_dev->open = mxt_input_open;
input_dev->close = mxt_input_close;
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
- if (pdata->t19_num_keys) {
- __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0);
- for (i = 0; i < pdata->t19_num_keys; i++)
- if (pdata->t19_keymap[i] != KEY_RESERVED)
- input_set_capability(input_dev, EV_KEY,
- pdata->t19_keymap[i]);
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl)) {
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+ }
+ /* If device has buttons we assume it is a touchpad */
+ if (pdata->t19_num_keys) {
+ mxt_set_up_as_touchpad(input_dev, data);
mt_flags |= INPUT_MT_POINTER;
-
- input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_MT_POSITION_X,
- MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
- MXT_PIXELS_PER_MM);
-
- input_dev->name = "Atmel maXTouch Touchpad";
}
- /* For single touch */
- input_set_abs_params(input_dev, ABS_X,
- 0, data->max_x, 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- 0, data->max_y, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE,
- 0, 255, 0, 0);
-
/* For multi touch */
- num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
if (error) {
dev_err(dev, "Error %d initialising slots\n", error);
goto err_free_mem;
}
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, MXT_MAX_AREA, 0, 0);
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_DISTANCE,
+ MXT_DISTANCE_ACTIVE_TOUCH,
+ MXT_DISTANCE_HOVERING,
+ 0, 0);
+ }
+
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, data->max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, data->max_y, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_PRESSURE,
- 0, 255, 0, 0);
+
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_area)) {
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl)) {
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_vect) {
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl) {
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_vect) {
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+ }
input_set_drvdata(input_dev, data);
@@ -1765,9 +2064,13 @@ static int mxt_configure_objects(struct mxt_data *data,
dev_warn(dev, "Error %d updating config\n", error);
}
- error = mxt_initialize_t9_input_device(data);
- if (error)
- return error;
+ if (data->multitouch) {
+ error = mxt_initialize_input_device(data);
+ if (error)
+ return error;
+ } else {
+ dev_warn(dev, "No touch object detected\n");
+ }
dev_info(dev,
"Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
@@ -2044,15 +2347,13 @@ static const struct attribute_group mxt_attr_group = {
static void mxt_start(struct mxt_data *data)
{
/* Touch enable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
+ mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83);
}
static void mxt_stop(struct mxt_data *data)
{
/* Touch disable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
+ mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0);
}
static int mxt_input_open(struct input_dev *dev)
@@ -2072,7 +2373,7 @@ static void mxt_input_close(struct input_dev *dev)
}
#ifdef CONFIG_OF
-static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
{
struct mxt_platform_data *pdata;
u32 *keymap;
@@ -2080,7 +2381,7 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
int proplen, i, ret;
if (!client->dev.of_node)
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-ENOENT);
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -2111,25 +2412,132 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
return pdata;
}
#else
-static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
{
- dev_dbg(&client->dev, "No platform data specified\n");
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(-ENOENT);
+}
+#endif
+
+#ifdef CONFIG_ACPI
+
+struct mxt_acpi_platform_data {
+ const char *hid;
+ struct mxt_platform_data pdata;
+};
+
+static unsigned int samus_touchpad_buttons[] = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ BTN_LEFT
+};
+
+static struct mxt_acpi_platform_data samus_platform_data[] = {
+ {
+ /* Touchpad */
+ .hid = "ATML0000",
+ .pdata = {
+ .t19_num_keys = ARRAY_SIZE(samus_touchpad_buttons),
+ .t19_keymap = samus_touchpad_buttons,
+ },
+ },
+ {
+ /* Touchscreen */
+ .hid = "ATML0001",
+ },
+ { }
+};
+
+static const struct dmi_system_id mxt_dmi_table[] = {
+ {
+ /* 2015 Google Pixel */
+ .ident = "Chromebook Pixel 2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
+ },
+ .driver_data = samus_platform_data,
+ },
+ { }
+};
+
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+ struct acpi_device *adev;
+ const struct dmi_system_id *system_id;
+ const struct mxt_acpi_platform_data *acpi_pdata;
+
+ /*
+ * Ignore ACPI devices representing bootloader mode.
+ *
+ * This is a bit of a hack: Google Chromebook BIOS creates ACPI
+ * devices for both application and bootloader modes, but we are
+ * interested in application mode only (if device is in bootloader
+ * mode we'll end up switching into application anyway). So far
+ * application mode addresses were all above 0x40, so we'll use it
+ * as a threshold.
+ */
+ if (client->addr < 0x40)
+ return ERR_PTR(-ENXIO);
+
+ adev = ACPI_COMPANION(&client->dev);
+ if (!adev)
+ return ERR_PTR(-ENOENT);
+
+ system_id = dmi_first_match(mxt_dmi_table);
+ if (!system_id)
+ return ERR_PTR(-ENOENT);
+
+ acpi_pdata = system_id->driver_data;
+ if (!acpi_pdata)
+ return ERR_PTR(-ENOENT);
+
+ while (acpi_pdata->hid) {
+ if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid))
+ return &acpi_pdata->pdata;
+
+ acpi_pdata++;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+#else
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+ return ERR_PTR(-ENOENT);
}
#endif
+static const struct mxt_platform_data *
+mxt_get_platform_data(struct i2c_client *client)
+{
+ const struct mxt_platform_data *pdata;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (pdata)
+ return pdata;
+
+ pdata = mxt_parse_dt(client);
+ if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+ return pdata;
+
+ pdata = mxt_parse_acpi(client);
+ if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+ return pdata;
+
+ dev_err(&client->dev, "No platform data specified\n");
+ return ERR_PTR(-EINVAL);
+}
+
static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct mxt_data *data;
const struct mxt_platform_data *pdata;
int error;
- pdata = dev_get_platdata(&client->dev);
- if (!pdata) {
- pdata = mxt_parse_dt(client);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
- }
+ pdata = mxt_get_platform_data(client);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
if (!data) {
@@ -2237,6 +2645,15 @@ static const struct of_device_id mxt_of_match[] = {
};
MODULE_DEVICE_TABLE(of, mxt_of_match);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mxt_acpi_id[] = {
+ { "ATML0000", 0 }, /* Touchpad */
+ { "ATML0001", 0 }, /* Touchscreen */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, mxt_acpi_id);
+#endif
+
static const struct i2c_device_id mxt_id[] = {
{ "qt602240_ts", 0 },
{ "atmel_mxt_ts", 0 },
@@ -2251,6 +2668,7 @@ static struct i2c_driver mxt_driver = {
.name = "atmel_mxt_ts",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mxt_of_match),
+ .acpi_match_table = ACPI_PTR(mxt_acpi_id),
.pm = &mxt_pm_ops,
},
.probe = mxt_probe,
diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c
new file mode 100644
index 000000000000..ae460a5c93d5
--- /dev/null
+++ b/drivers/input/touchscreen/bcm_iproc_tsc.c
@@ -0,0 +1,522 @@
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation version 2.
+*
+* This program is distributed "as is" WITHOUT ANY WARRANTY of any
+* kind, whether express or implied; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/serio.h>
+
+#define IPROC_TS_NAME "iproc-ts"
+
+#define PEN_DOWN_STATUS 1
+#define PEN_UP_STATUS 0
+
+#define X_MIN 0
+#define Y_MIN 0
+#define X_MAX 0xFFF
+#define Y_MAX 0xFFF
+
+/* Value given by controller for invalid coordinate. */
+#define INVALID_COORD 0xFFFFFFFF
+
+/* Register offsets */
+#define REGCTL1 0x00
+#define REGCTL2 0x04
+#define INTERRUPT_THRES 0x08
+#define INTERRUPT_MASK 0x0c
+
+#define INTERRUPT_STATUS 0x10
+#define CONTROLLER_STATUS 0x14
+#define FIFO_DATA 0x18
+#define FIFO_DATA_X_Y_MASK 0xFFFF
+#define ANALOG_CONTROL 0x1c
+
+#define AUX_DATA 0x20
+#define DEBOUNCE_CNTR_STAT 0x24
+#define SCAN_CNTR_STAT 0x28
+#define REM_CNTR_STAT 0x2c
+
+#define SETTLING_TIMER_STAT 0x30
+#define SPARE_REG 0x34
+#define SOFT_BYPASS_CONTROL 0x38
+#define SOFT_BYPASS_DATA 0x3c
+
+
+/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */
+#define TS_PEN_INTR_MASK BIT(0)
+#define TS_FIFO_INTR_MASK BIT(2)
+
+/* Bit values for CONTROLLER_STATUS reg1 */
+#define TS_PEN_DOWN BIT(0)
+
+/* Shift values for control reg1 */
+#define SCANNING_PERIOD_SHIFT 24
+#define DEBOUNCE_TIMEOUT_SHIFT 16
+#define SETTLING_TIMEOUT_SHIFT 8
+#define TOUCH_TIMEOUT_SHIFT 0
+
+/* Shift values for coordinates from fifo */
+#define X_COORD_SHIFT 0
+#define Y_COORD_SHIFT 16
+
+/* Bit values for REGCTL2 */
+#define TS_CONTROLLER_EN_BIT BIT(16)
+#define TS_CONTROLLER_AVGDATA_SHIFT 8
+#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT)
+#define TS_CONTROLLER_PWR_LDO BIT(5)
+#define TS_CONTROLLER_PWR_ADC BIT(4)
+#define TS_CONTROLLER_PWR_BGP BIT(3)
+#define TS_CONTROLLER_PWR_TS BIT(2)
+#define TS_WIRE_MODE_BIT BIT(1)
+
+#define dbg_reg(dev, priv, reg) \
+ dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg))
+
+struct tsc_param {
+ /* Each step is 1024 us. Valid 1-256 */
+ u32 scanning_period;
+
+ /* Each step is 512 us. Valid 0-255 */
+ u32 debounce_timeout;
+
+ /*
+ * The settling duration (in ms) is the amount of time the tsc
+ * waits to allow the voltage to settle after turning on the
+ * drivers in detection mode. Valid values: 0-11
+ * 0 = 0.008 ms
+ * 1 = 0.01 ms
+ * 2 = 0.02 ms
+ * 3 = 0.04 ms
+ * 4 = 0.08 ms
+ * 5 = 0.16 ms
+ * 6 = 0.32 ms
+ * 7 = 0.64 ms
+ * 8 = 1.28 ms
+ * 9 = 2.56 ms
+ * 10 = 5.12 ms
+ * 11 = 10.24 ms
+ */
+ u32 settling_timeout;
+
+ /* touch timeout in sample counts */
+ u32 touch_timeout;
+
+ /*
+ * Number of data samples which are averaged before a final data point
+ * is placed into the FIFO
+ */
+ u32 average_data;
+
+ /* FIFO threshold */
+ u32 fifo_threshold;
+
+ /* Optional standard touchscreen properties. */
+ u32 max_x;
+ u32 max_y;
+ u32 fuzz_x;
+ u32 fuzz_y;
+ bool invert_x;
+ bool invert_y;
+};
+
+struct iproc_ts_priv {
+ struct platform_device *pdev;
+ struct input_dev *idev;
+
+ void __iomem *regs;
+ struct clk *tsc_clk;
+
+ int pen_status;
+ struct tsc_param cfg_params;
+};
+
+/*
+ * Set default values the same as hardware reset values
+ * except for fifo_threshold with is set to 1.
+ */
+static const struct tsc_param iproc_default_config = {
+ .scanning_period = 0x5, /* 1 to 256 */
+ .debounce_timeout = 0x28, /* 0 to 255 */
+ .settling_timeout = 0x7, /* 0 to 11 */
+ .touch_timeout = 0xa, /* 0 to 255 */
+ .average_data = 5, /* entry 5 = 32 pts */
+ .fifo_threshold = 1, /* 0 to 31 */
+ .max_x = X_MAX,
+ .max_y = Y_MAX,
+};
+
+static void ts_reg_dump(struct iproc_ts_priv *priv)
+{
+ struct device *dev = &priv->pdev->dev;
+
+ dbg_reg(dev, priv, REGCTL1);
+ dbg_reg(dev, priv, REGCTL2);
+ dbg_reg(dev, priv, INTERRUPT_THRES);
+ dbg_reg(dev, priv, INTERRUPT_MASK);
+ dbg_reg(dev, priv, INTERRUPT_STATUS);
+ dbg_reg(dev, priv, CONTROLLER_STATUS);
+ dbg_reg(dev, priv, FIFO_DATA);
+ dbg_reg(dev, priv, ANALOG_CONTROL);
+ dbg_reg(dev, priv, AUX_DATA);
+ dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT);
+ dbg_reg(dev, priv, SCAN_CNTR_STAT);
+ dbg_reg(dev, priv, REM_CNTR_STAT);
+ dbg_reg(dev, priv, SETTLING_TIMER_STAT);
+ dbg_reg(dev, priv, SPARE_REG);
+ dbg_reg(dev, priv, SOFT_BYPASS_CONTROL);
+ dbg_reg(dev, priv, SOFT_BYPASS_DATA);
+}
+
+static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct iproc_ts_priv *priv = platform_get_drvdata(pdev);
+ u32 intr_status;
+ u32 raw_coordinate;
+ u16 x;
+ u16 y;
+ int i;
+ bool needs_sync = false;
+
+ intr_status = readl(priv->regs + INTERRUPT_STATUS);
+ intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+ if (intr_status == 0)
+ return IRQ_NONE;
+
+ /* Clear all interrupt status bits, write-1-clear */
+ writel(intr_status, priv->regs + INTERRUPT_STATUS);
+
+ /* Pen up/down */
+ if (intr_status & TS_PEN_INTR_MASK) {
+ if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN)
+ priv->pen_status = PEN_DOWN_STATUS;
+ else
+ priv->pen_status = PEN_UP_STATUS;
+
+ input_report_key(priv->idev, BTN_TOUCH, priv->pen_status);
+ needs_sync = true;
+
+ dev_dbg(&priv->pdev->dev,
+ "pen up-down (%d)\n", priv->pen_status);
+ }
+
+ /* coordinates in FIFO exceed the theshold */
+ if (intr_status & TS_FIFO_INTR_MASK) {
+ for (i = 0; i < priv->cfg_params.fifo_threshold; i++) {
+ raw_coordinate = readl(priv->regs + FIFO_DATA);
+ if (raw_coordinate == INVALID_COORD)
+ continue;
+
+ /*
+ * The x and y coordinate are 16 bits each
+ * with the x in the lower 16 bits and y in the
+ * upper 16 bits.
+ */
+ x = (raw_coordinate >> X_COORD_SHIFT) &
+ FIFO_DATA_X_Y_MASK;
+ y = (raw_coordinate >> Y_COORD_SHIFT) &
+ FIFO_DATA_X_Y_MASK;
+
+ /* We only want to retain the 12 msb of the 16 */
+ x = (x >> 4) & 0x0FFF;
+ y = (y >> 4) & 0x0FFF;
+
+ /* adjust x y according to lcd tsc mount angle */
+ if (priv->cfg_params.invert_x)
+ x = priv->cfg_params.max_x - x;
+
+ if (priv->cfg_params.invert_y)
+ y = priv->cfg_params.max_y - y;
+
+ input_report_abs(priv->idev, ABS_X, x);
+ input_report_abs(priv->idev, ABS_Y, y);
+ needs_sync = true;
+
+ dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y);
+ }
+ }
+
+ if (needs_sync)
+ input_sync(priv->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int iproc_ts_start(struct input_dev *idev)
+{
+ struct iproc_ts_priv *priv = input_get_drvdata(idev);
+ u32 val;
+ int error;
+
+ /* Enable clock */
+ error = clk_prepare_enable(priv->tsc_clk);
+ if (error) {
+ dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n",
+ __func__, error);
+ return error;
+ }
+
+ /*
+ * Interrupt is generated when:
+ * FIFO reaches the int_th value, and pen event(up/down)
+ */
+ val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+ writel(val, priv->regs + INTERRUPT_MASK);
+
+ writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES);
+
+ /* Initialize control reg1 */
+ val = 0;
+ val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT;
+ val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
+ val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
+ val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT;
+ writel(val, priv->regs + REGCTL1);
+
+ /* Try to clear all interrupt status */
+ val = readl(priv->regs + INTERRUPT_STATUS);
+ val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
+ writel(val, priv->regs + INTERRUPT_STATUS);
+
+ /* Initialize control reg2 */
+ val = readl(priv->regs + REGCTL2);
+ val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
+
+ val &= ~TS_CONTROLLER_AVGDATA_MASK;
+ val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT;
+
+ val &= ~(TS_CONTROLLER_PWR_LDO | /* PWR up LDO */
+ TS_CONTROLLER_PWR_ADC | /* PWR up ADC */
+ TS_CONTROLLER_PWR_BGP | /* PWR up BGP */
+ TS_CONTROLLER_PWR_TS); /* PWR up TS */
+
+ writel(val, priv->regs + REGCTL2);
+
+ ts_reg_dump(priv);
+
+ return 0;
+}
+
+static void iproc_ts_stop(struct input_dev *dev)
+{
+ u32 val;
+ struct iproc_ts_priv *priv = input_get_drvdata(dev);
+
+ writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */
+
+ /* Only power down touch screen controller */
+ val = readl(priv->regs + REGCTL2);
+ val |= TS_CONTROLLER_PWR_TS;
+ writel(val, priv->regs + REGCTL2);
+
+ clk_disable(priv->tsc_clk);
+}
+
+static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ u32 val;
+
+ priv->cfg_params = iproc_default_config;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32(np, "scanning_period", &val) >= 0) {
+ if (val < 1 || val > 256) {
+ dev_err(dev, "scanning_period (%u) must be [1-256]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.scanning_period = val;
+ }
+
+ if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) {
+ if (val > 255) {
+ dev_err(dev, "debounce_timeout (%u) must be [0-255]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.debounce_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "settling_timeout", &val) >= 0) {
+ if (val > 11) {
+ dev_err(dev, "settling_timeout (%u) must be [0-11]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.settling_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "touch_timeout", &val) >= 0) {
+ if (val > 255) {
+ dev_err(dev, "touch_timeout (%u) must be [0-255]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.touch_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "average_data", &val) >= 0) {
+ if (val > 8) {
+ dev_err(dev, "average_data (%u) must be [0-8]\n", val);
+ return -EINVAL;
+ }
+ priv->cfg_params.average_data = val;
+ }
+
+ if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) {
+ if (val > 31) {
+ dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.fifo_threshold = val;
+ }
+
+ /* Parse optional properties. */
+ of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x);
+ of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y);
+
+ of_property_read_u32(np, "touchscreen-fuzz-x",
+ &priv->cfg_params.fuzz_x);
+ of_property_read_u32(np, "touchscreen-fuzz-y",
+ &priv->cfg_params.fuzz_y);
+
+ priv->cfg_params.invert_x =
+ of_property_read_bool(np, "touchscreen-inverted-x");
+ priv->cfg_params.invert_y =
+ of_property_read_bool(np, "touchscreen-inverted-y");
+
+ return 0;
+}
+
+static int iproc_ts_probe(struct platform_device *pdev)
+{
+ struct iproc_ts_priv *priv;
+ struct input_dev *idev;
+ struct resource *res;
+ int irq;
+ int error;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* touchscreen controller memory mapped regs */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->regs)) {
+ error = PTR_ERR(priv->regs);
+ dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error);
+ return error;
+ }
+
+ priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
+ if (IS_ERR(priv->tsc_clk)) {
+ error = PTR_ERR(priv->tsc_clk);
+ dev_err(&pdev->dev,
+ "failed getting clock tsc_clk: %d\n", error);
+ return error;
+ }
+
+ priv->pdev = pdev;
+ error = iproc_get_tsc_config(&pdev->dev, priv);
+ if (error) {
+ dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error);
+ return error;
+ }
+
+ idev = devm_input_allocate_device(&pdev->dev);
+ if (!idev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ priv->idev = idev;
+ priv->pen_status = PEN_UP_STATUS;
+
+ /* Set input device info */
+ idev->name = IPROC_TS_NAME;
+ idev->dev.parent = &pdev->dev;
+
+ idev->id.bustype = BUS_HOST;
+ idev->id.vendor = SERIO_UNKNOWN;
+ idev->id.product = 0;
+ idev->id.version = 0;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ __set_bit(BTN_TOUCH, idev->keybit);
+
+ input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x,
+ priv->cfg_params.fuzz_x, 0);
+ input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y,
+ priv->cfg_params.fuzz_y, 0);
+
+ idev->open = iproc_ts_start;
+ idev->close = iproc_ts_stop;
+
+ input_set_drvdata(idev, priv);
+ platform_set_drvdata(pdev, priv);
+
+ /* get interrupt */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq);
+ return irq;
+ }
+
+ error = devm_request_irq(&pdev->dev, irq,
+ iproc_touchscreen_interrupt,
+ IRQF_SHARED, IPROC_TS_NAME, pdev);
+ if (error)
+ return error;
+
+ error = input_register_device(priv->idev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "failed to register input device: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id iproc_ts_of_match[] = {
+ {.compatible = "brcm,iproc-touchscreen", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, iproc_ts_of_match);
+
+static struct platform_driver iproc_ts_driver = {
+ .probe = iproc_ts_probe,
+ .driver = {
+ .name = IPROC_TS_NAME,
+ .of_match_table = of_match_ptr(iproc_ts_of_match),
+ },
+};
+
+module_platform_driver(iproc_ts_driver);
+
+MODULE_DESCRIPTION("IPROC Touchscreen driver");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c
new file mode 100644
index 000000000000..32e9db0e04bf
--- /dev/null
+++ b/drivers/input/touchscreen/chipone_icn8318.c
@@ -0,0 +1,316 @@
+/*
+ * Driver for ChipOne icn8318 i2c touchscreen controller
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define ICN8318_REG_POWER 4
+#define ICN8318_REG_TOUCHDATA 16
+
+#define ICN8318_POWER_ACTIVE 0
+#define ICN8318_POWER_MONITOR 1
+#define ICN8318_POWER_HIBERNATE 2
+
+#define ICN8318_MAX_TOUCHES 5
+
+struct icn8318_touch {
+ __u8 slot;
+ __be16 x;
+ __be16 y;
+ __u8 pressure; /* Seems more like finger width then pressure really */
+ __u8 event;
+/* The difference between 2 and 3 is unclear */
+#define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */
+#define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */
+#define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */
+#define ICN8318_EVENT_END 4 /* Finger lifted */
+} __packed;
+
+struct icn8318_touch_data {
+ __u8 softbutton;
+ __u8 touch_count;
+ struct icn8318_touch touches[ICN8318_MAX_TOUCHES];
+} __packed;
+
+struct icn8318_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct gpio_desc *wake_gpio;
+ u32 max_x;
+ u32 max_y;
+ bool invert_x;
+ bool invert_y;
+ bool swap_x_y;
+};
+
+static int icn8318_read_touch_data(struct i2c_client *client,
+ struct icn8318_touch_data *touch_data)
+{
+ u8 reg = ICN8318_REG_TOUCHDATA;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .len = 1,
+ .buf = &reg
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(struct icn8318_touch_data),
+ .buf = (u8 *)touch_data
+ }
+ };
+
+ return i2c_transfer(client->adapter, msg, 2);
+}
+
+static inline bool icn8318_touch_active(u8 event)
+{
+ return (event == ICN8318_EVENT_UPDATE1) ||
+ (event == ICN8318_EVENT_UPDATE2);
+}
+
+static irqreturn_t icn8318_irq(int irq, void *dev_id)
+{
+ struct icn8318_data *data = dev_id;
+ struct device *dev = &data->client->dev;
+ struct icn8318_touch_data touch_data;
+ int i, ret, x, y;
+
+ ret = icn8318_read_touch_data(data->client, &touch_data);
+ if (ret < 0) {
+ dev_err(dev, "Error reading touch data: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ if (touch_data.softbutton) {
+ /*
+ * Other data is invalid when a softbutton is pressed.
+ * This needs some extra devicetree bindings to map the icn8318
+ * softbutton codes to evdev codes. Currently no known devices
+ * use this.
+ */
+ return IRQ_HANDLED;
+ }
+
+ if (touch_data.touch_count > ICN8318_MAX_TOUCHES) {
+ dev_warn(dev, "Too much touches %d > %d\n",
+ touch_data.touch_count, ICN8318_MAX_TOUCHES);
+ touch_data.touch_count = ICN8318_MAX_TOUCHES;
+ }
+
+ for (i = 0; i < touch_data.touch_count; i++) {
+ struct icn8318_touch *touch = &touch_data.touches[i];
+ bool act = icn8318_touch_active(touch->event);
+
+ input_mt_slot(data->input, touch->slot);
+ input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act);
+ if (!act)
+ continue;
+
+ x = be16_to_cpu(touch->x);
+ y = be16_to_cpu(touch->y);
+
+ if (data->invert_x)
+ x = data->max_x - x;
+
+ if (data->invert_y)
+ y = data->max_y - y;
+
+ if (!data->swap_x_y) {
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
+ } else {
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
+ }
+ }
+
+ input_mt_sync_frame(data->input);
+ input_sync(data->input);
+
+ return IRQ_HANDLED;
+}
+
+static int icn8318_start(struct input_dev *dev)
+{
+ struct icn8318_data *data = input_get_drvdata(dev);
+
+ enable_irq(data->client->irq);
+ gpiod_set_value_cansleep(data->wake_gpio, 1);
+
+ return 0;
+}
+
+static void icn8318_stop(struct input_dev *dev)
+{
+ struct icn8318_data *data = input_get_drvdata(dev);
+
+ disable_irq(data->client->irq);
+ i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER,
+ ICN8318_POWER_HIBERNATE);
+ gpiod_set_value_cansleep(data->wake_gpio, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int icn8318_suspend(struct device *dev)
+{
+ struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ mutex_lock(&data->input->mutex);
+ if (data->input->users)
+ icn8318_stop(data->input);
+ mutex_unlock(&data->input->mutex);
+
+ return 0;
+}
+
+static int icn8318_resume(struct device *dev)
+{
+ struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ mutex_lock(&data->input->mutex);
+ if (data->input->users)
+ icn8318_start(data->input);
+ mutex_unlock(&data->input->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume);
+
+static int icn8318_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+ struct icn8318_data *data;
+ struct input_dev *input;
+ u32 fuzz_x = 0, fuzz_y = 0;
+ int error;
+
+ if (!client->irq) {
+ dev_err(dev, "Error no irq specified\n");
+ return -EINVAL;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW);
+ if (IS_ERR(data->wake_gpio)) {
+ error = PTR_ERR(data->wake_gpio);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Error getting wake gpio: %d\n", error);
+ return error;
+ }
+
+ if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
+ of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
+ dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+ return -EINVAL;
+ }
+
+ /* Optional */
+ of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
+ of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
+ data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
+ data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
+ data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->open = icn8318_start;
+ input->close = icn8318_stop;
+ input->dev.parent = dev;
+
+ if (!data->swap_x_y) {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ data->max_x, fuzz_x, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ data->max_y, fuzz_y, 0);
+ } else {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ data->max_y, fuzz_y, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ data->max_x, fuzz_x, 0);
+ }
+
+ error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error)
+ return error;
+
+ data->client = client;
+ data->input = input;
+ input_set_drvdata(input, data);
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq,
+ IRQF_ONESHOT, client->name, data);
+ if (error) {
+ dev_err(dev, "Error requesting irq: %d\n", error);
+ return error;
+ }
+
+ /* Stop device till opened */
+ icn8318_stop(data->input);
+
+ error = input_register_device(input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, data);
+
+ return 0;
+}
+
+static const struct of_device_id icn8318_of_match[] = {
+ { .compatible = "chipone,icn8318" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, icn8318_of_match);
+
+/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */
+static const struct i2c_device_id icn8318_i2c_id[] = {
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
+
+static struct i2c_driver icn8318_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "chipone_icn8318",
+ .pm = &icn8318_pm_ops,
+ .of_match_table = icn8318_of_match,
+ },
+ .probe = icn8318_probe,
+ .id_table = icn8318_i2c_id,
+};
+
+module_i2c_driver(icn8318_driver);
+
+MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
index 568a3d340c8a..5ed31057430c 100644
--- a/drivers/input/touchscreen/cyttsp4_core.c
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -775,7 +775,6 @@ static void cyttsp4_get_touch(struct cyttsp4_mt_data *md,
struct device *dev = &md->input->dev;
struct cyttsp4_sysinfo *si = md->si;
enum cyttsp4_tch_abs abs;
- int tmp;
bool flipped;
for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
@@ -790,9 +789,7 @@ static void cyttsp4_get_touch(struct cyttsp4_mt_data *md,
}
if (md->pdata->flags & CY_FLAG_FLIP) {
- tmp = touch->abs[CY_TCH_X];
- touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y];
- touch->abs[CY_TCH_Y] = tmp;
+ swap(touch->abs[CY_TCH_X], touch->abs[CY_TCH_Y]);
flipped = true;
} else
flipped = false;
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index d4c24fb7704f..e6aef3e48bd9 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -37,6 +37,7 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
#include <linux/input/edt-ft5x06.h>
#define MAX_SUPPORT_POINTS 5
@@ -1034,7 +1035,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
- __set_bit(EV_SYN, input->evbit);
__set_bit(EV_KEY, input->evbit);
__set_bit(EV_ABS, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
@@ -1044,6 +1044,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
0, tsdata->num_x * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0);
+
+ if (!pdata)
+ touchscreen_parse_of_params(input);
+
error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index 926c58e540c0..0efd766a545b 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -98,7 +98,6 @@
#define MAX_FW_UPDATE_RETRIES 30
#define ELAN_FW_PAGESIZE 132
-#define ELAN_FW_FILENAME "elants_i2c.bin"
/* calibration timeout definition */
#define ELAN_CALI_TIMEOUT_MSEC 10000
@@ -697,12 +696,19 @@ static int elants_i2c_fw_update(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
const struct firmware *fw;
+ char *fw_name;
int error;
- error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev);
+ fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version);
+ if (!fw_name)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+ error = request_firmware(&fw, fw_name, &client->dev);
+ kfree(fw_name);
if (error) {
- dev_err(&client->dev, "failed to request firmware %s: %d\n",
- ELAN_FW_FILENAME, error);
+ dev_err(&client->dev, "failed to request firmware: %d\n",
+ error);
return error;
}
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index ca196689f025..b4d12e29abff 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -23,6 +23,8 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
#include <asm/unaligned.h>
struct goodix_ts_data {
@@ -45,9 +47,10 @@ struct goodix_ts_data {
/* Register defines */
#define GOODIX_READ_COOR_ADDR 0x814E
#define GOODIX_REG_CONFIG_DATA 0x8047
-#define GOODIX_REG_VERSION 0x8140
+#define GOODIX_REG_ID 0x8140
#define RESOLUTION_LOC 1
+#define MAX_CONTACTS_LOC 5
#define TRIGGER_LOC 6
static const unsigned long goodix_irq_flags[] = {
@@ -66,7 +69,7 @@ static const unsigned long goodix_irq_flags[] = {
* @len: length of the buffer to write
*/
static int goodix_i2c_read(struct i2c_client *client,
- u16 reg, u8 *buf, int len)
+ u16 reg, u8 *buf, int len)
{
struct i2c_msg msgs[2];
u16 wbuf = cpu_to_be16(reg);
@@ -75,7 +78,7 @@ static int goodix_i2c_read(struct i2c_client *client,
msgs[0].flags = 0;
msgs[0].addr = client->addr;
msgs[0].len = 2;
- msgs[0].buf = (u8 *) &wbuf;
+ msgs[0].buf = (u8 *)&wbuf;
msgs[1].flags = I2C_M_RD;
msgs[1].addr = client->addr;
@@ -98,8 +101,11 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
return error;
}
+ if (!(data[0] & 0x80))
+ return -EAGAIN;
+
touch_num = data[0] & 0x0f;
- if (touch_num > GOODIX_MAX_CONTACTS)
+ if (touch_num > ts->max_touch_num)
return -EPROTO;
if (touch_num > 1) {
@@ -193,8 +199,8 @@ static void goodix_read_config(struct goodix_ts_data *ts)
int error;
error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
- config,
- GOODIX_CONFIG_MAX_LENGTH);
+ config,
+ GOODIX_CONFIG_MAX_LENGTH);
if (error) {
dev_warn(&ts->client->dev,
"Error reading config (%d), using defaults\n",
@@ -202,42 +208,50 @@ static void goodix_read_config(struct goodix_ts_data *ts)
ts->abs_x_max = GOODIX_MAX_WIDTH;
ts->abs_y_max = GOODIX_MAX_HEIGHT;
ts->int_trigger_type = GOODIX_INT_TRIGGER;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
return;
}
ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
- ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
- if (!ts->abs_x_max || !ts->abs_y_max) {
+ ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
+ ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
+ if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
dev_err(&ts->client->dev,
"Invalid config, using defaults\n");
ts->abs_x_max = GOODIX_MAX_WIDTH;
ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
}
}
-
/**
* goodix_read_version - Read goodix touchscreen version
*
* @client: the i2c client
* @version: output buffer containing the version on success
+ * @id: output buffer containing the id on success
*/
-static int goodix_read_version(struct i2c_client *client, u16 *version)
+static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
{
int error;
u8 buf[6];
+ char id_str[5];
- error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf));
+ error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
if (error) {
dev_err(&client->dev, "read version failed: %d\n", error);
return error;
}
- if (version)
- *version = get_unaligned_le16(&buf[4]);
+ memcpy(id_str, buf, 4);
+ id_str[4] = 0;
+ if (kstrtou16(id_str, 10, id))
+ *id = 0x1001;
+
+ *version = get_unaligned_le16(&buf[4]);
- dev_info(&client->dev, "IC VERSION: %6ph\n", buf);
+ dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
return 0;
}
@@ -271,10 +285,13 @@ static int goodix_i2c_test(struct i2c_client *client)
* goodix_request_input_dev - Allocate, populate and register the input device
*
* @ts: our goodix_ts_data pointer
+ * @version: device firmware version
+ * @id: device ID
*
* Must be called during probe
*/
-static int goodix_request_input_dev(struct goodix_ts_data *ts)
+static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
+ u16 id)
{
int error;
@@ -284,26 +301,22 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts)
return -ENOMEM;
}
- ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) |
- BIT_MASK(EV_KEY) |
- BIT_MASK(EV_ABS);
-
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0,
- ts->abs_x_max, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0,
- ts->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
+ 0, ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+ 0, ts->abs_y_max, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
- input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS,
+ input_mt_init_slots(ts->input_dev, ts->max_touch_num,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
ts->input_dev->name = "Goodix Capacitive TouchScreen";
ts->input_dev->phys = "input/ts";
ts->input_dev->id.bustype = BUS_I2C;
ts->input_dev->id.vendor = 0x0416;
- ts->input_dev->id.product = 0x1001;
- ts->input_dev->id.version = 10427;
+ ts->input_dev->id.product = id;
+ ts->input_dev->id.version = version;
error = input_register_device(ts->input_dev);
if (error) {
@@ -321,7 +334,7 @@ static int goodix_ts_probe(struct i2c_client *client,
struct goodix_ts_data *ts;
unsigned long irq_flags;
int error;
- u16 version_info;
+ u16 version_info, id_info;
dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
@@ -343,7 +356,7 @@ static int goodix_ts_probe(struct i2c_client *client,
return error;
}
- error = goodix_read_version(client, &version_info);
+ error = goodix_read_version(client, &version_info, &id_info);
if (error) {
dev_err(&client->dev, "Read version failed.\n");
return error;
@@ -351,7 +364,7 @@ static int goodix_ts_probe(struct i2c_client *client,
goodix_read_config(ts);
- error = goodix_request_input_dev(ts);
+ error = goodix_request_input_dev(ts, version_info, id_info);
if (error)
return error;
@@ -372,11 +385,27 @@ static const struct i2c_device_id goodix_ts_id[] = {
{ }
};
+#ifdef CONFIG_ACPI
static const struct acpi_device_id goodix_acpi_match[] = {
{ "GDIX1001", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id goodix_of_match[] = {
+ { .compatible = "goodix,gt911" },
+ { .compatible = "goodix,gt9110" },
+ { .compatible = "goodix,gt912" },
+ { .compatible = "goodix,gt927" },
+ { .compatible = "goodix,gt9271" },
+ { .compatible = "goodix,gt928" },
+ { .compatible = "goodix,gt967" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, goodix_of_match);
+#endif
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
@@ -384,7 +413,8 @@ static struct i2c_driver goodix_ts_driver = {
.driver = {
.name = "Goodix-TS",
.owner = THIS_MODULE,
- .acpi_match_table = goodix_acpi_match,
+ .acpi_match_table = ACPI_PTR(goodix_acpi_match),
+ .of_match_table = of_match_ptr(goodix_of_match),
},
};
module_i2c_driver(goodix_ts_driver);
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c
index f8f9b84230b1..b82b5207c78b 100644
--- a/drivers/input/touchscreen/of_touchscreen.c
+++ b/drivers/input/touchscreen/of_touchscreen.c
@@ -11,8 +11,41 @@
#include <linux/of.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
+static u32 of_get_optional_u32(struct device_node *np,
+ const char *property)
+{
+ u32 val = 0;
+
+ of_property_read_u32(np, property, &val);
+
+ return val;
+}
+
+static void touchscreen_set_params(struct input_dev *dev,
+ unsigned long axis,
+ int max, int fuzz)
+{
+ struct input_absinfo *absinfo;
+
+ if (!test_bit(axis, dev->absbit)) {
+ /*
+ * Emit a warning only if the axis is not a multitouch
+ * axis, which might not be set by the driver.
+ */
+ if (!input_is_mt_axis(axis))
+ dev_warn(&dev->dev,
+ "DT specifies parameters but the axis is not set up\n");
+ return;
+ }
+
+ absinfo = &dev->absinfo[axis];
+ absinfo->maximum = max;
+ absinfo->fuzz = fuzz;
+}
+
/**
* touchscreen_parse_of_params - parse common touchscreen DT properties
* @dev: device that should be parsed
@@ -24,22 +57,31 @@
void touchscreen_parse_of_params(struct input_dev *dev)
{
struct device_node *np = dev->dev.parent->of_node;
- struct input_absinfo *absinfo;
+ u32 maximum, fuzz;
input_alloc_absinfo(dev);
if (!dev->absinfo)
return;
- absinfo = &dev->absinfo[ABS_X];
- of_property_read_u32(np, "touchscreen-size-x", &absinfo->maximum);
- of_property_read_u32(np, "touchscreen-fuzz-x", &absinfo->fuzz);
+ maximum = of_get_optional_u32(np, "touchscreen-size-x");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_X, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz);
+ }
- absinfo = &dev->absinfo[ABS_Y];
- of_property_read_u32(np, "touchscreen-size-y", &absinfo->maximum);
- of_property_read_u32(np, "touchscreen-fuzz-y", &absinfo->fuzz);
+ maximum = of_get_optional_u32(np, "touchscreen-size-y");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_Y, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz);
+ }
- absinfo = &dev->absinfo[ABS_PRESSURE];
- of_property_read_u32(np, "touchscreen-max-pressure", &absinfo->maximum);
- of_property_read_u32(np, "touchscreen-fuzz-pressure", &absinfo->fuzz);
+ maximum = of_get_optional_u32(np, "touchscreen-max-pressure");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz);
+ }
}
EXPORT_SYMBOL(touchscreen_parse_of_params);
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 2c2107147319..8f3e243a62bf 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -78,7 +78,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
}
ret = i2c_master_recv(tsdata->client, rdbuf, readsize);
- if (ret != sizeof(rdbuf)) {
+ if (ret != readsize) {
dev_err(&tsdata->client->dev,
"%s: i2c_master_recv failed(), ret=%d\n",
__func__, ret);
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index bdfa27dc097b..a4a103e1d11b 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -411,7 +411,7 @@ static const struct dev_pm_ops s3c_ts_pmops = {
};
#endif
-static struct platform_device_id s3cts_driver_ids[] = {
+static const struct platform_device_id s3cts_driver_ids[] = {
{ "s3c2410-ts", 0 },
{ "s3c2440-ts", 0 },
{ "s3c64xx-ts", FEAT_PEN_IRQ },
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
index 2d5ff86b343f..e414d43e5159 100644
--- a/drivers/input/touchscreen/stmpe-ts.c
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -164,7 +164,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)
STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
/* start polling for touch_det to detect release */
- schedule_delayed_work(&ts->work, HZ / 50);
+ schedule_delayed_work(&ts->work, msecs_to_jiffies(50));
return IRQ_HANDLED;
}
@@ -267,27 +267,10 @@ static void stmpe_ts_close(struct input_dev *dev)
static void stmpe_ts_get_platform_info(struct platform_device *pdev,
struct stmpe_touch *ts)
{
- struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
struct device_node *np = pdev->dev.of_node;
- struct stmpe_ts_platform_data *ts_pdata = NULL;
-
- ts->stmpe = stmpe;
-
- if (stmpe->pdata && stmpe->pdata->ts) {
- ts_pdata = stmpe->pdata->ts;
-
- ts->sample_time = ts_pdata->sample_time;
- ts->mod_12b = ts_pdata->mod_12b;
- ts->ref_sel = ts_pdata->ref_sel;
- ts->adc_freq = ts_pdata->adc_freq;
- ts->ave_ctrl = ts_pdata->ave_ctrl;
- ts->touch_det_delay = ts_pdata->touch_det_delay;
- ts->settling = ts_pdata->settling;
- ts->fraction_z = ts_pdata->fraction_z;
- ts->i_drive = ts_pdata->i_drive;
- } else if (np) {
- u32 val;
+ u32 val;
+ if (np) {
if (!of_property_read_u32(np, "st,sample-time", &val))
ts->sample_time = val;
if (!of_property_read_u32(np, "st,mod-12b", &val))
@@ -311,6 +294,7 @@ static void stmpe_ts_get_platform_info(struct platform_device *pdev,
static int stmpe_input_probe(struct platform_device *pdev)
{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
struct stmpe_touch *ts;
struct input_dev *idev;
int error;
@@ -329,6 +313,7 @@ static int stmpe_input_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, ts);
+ ts->stmpe = stmpe;
ts->idev = idev;
ts->dev = &pdev->dev;
@@ -351,14 +336,13 @@ static int stmpe_input_probe(struct platform_device *pdev)
idev->name = STMPE_TS_NAME;
idev->phys = STMPE_TS_NAME"/input0";
idev->id.bustype = BUS_I2C;
- idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
idev->open = stmpe_ts_open;
idev->close = stmpe_ts_close;
input_set_drvdata(idev, ts);
+ input_set_capability(idev, EV_KEY, BTN_TOUCH);
input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
@@ -383,14 +367,19 @@ static int stmpe_ts_remove(struct platform_device *pdev)
static struct platform_driver stmpe_ts_driver = {
.driver = {
- .name = STMPE_TS_NAME,
- },
+ .name = STMPE_TS_NAME,
+ },
.probe = stmpe_input_probe,
.remove = stmpe_ts_remove,
};
module_platform_driver(stmpe_ts_driver);
+static const struct of_device_id stmpe_ts_ids[] = {
+ { .compatible = "st,stmpe-ts", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stmpe_ts_ids);
+
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" STMPE_TS_NAME);
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
index b93a28b955fd..c0116994067d 100644
--- a/drivers/input/touchscreen/sun4i-ts.c
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -30,6 +30,10 @@
* These kinds of heuristics are just asking for trouble (and don't belong
* in the kernel). So this driver offers straight forward, reliable single
* touch functionality only.
+ *
+ * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README)
+ * (looks like the description in the A20 User Manual v1.3 is better
+ * than the one in the A10 User Manual v.1.5)
*/
#include <linux/err.h>
@@ -193,7 +197,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
if (ts->temp_data == -1)
return -EAGAIN;
- *temp = (ts->temp_data - ts->temp_offset) * ts->temp_step;
+ *temp = ts->temp_data * ts->temp_step - ts->temp_offset;
return 0;
}
@@ -246,6 +250,8 @@ static int sun4i_ts_probe(struct platform_device *pdev)
int error;
u32 reg;
bool ts_attached;
+ u32 tp_sensitive_adjust = 15;
+ u32 filter_type = 1;
ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
if (!ts)
@@ -255,22 +261,31 @@ static int sun4i_ts_probe(struct platform_device *pdev)
ts->ignore_fifo_data = true;
ts->temp_data = -1;
if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
- /* Allwinner SDK has temperature = -271 + (value / 6) (C) */
- ts->temp_offset = 1626;
+ /* Allwinner SDK has temperature (C) = (value / 6) - 271 */
+ ts->temp_offset = 271000;
ts->temp_step = 167;
+ } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) {
+ /*
+ * The A10 temperature sensor has quite a wide spread, these
+ * parameters are based on the averaging of the calibration
+ * results of 4 completely different boards, with a spread of
+ * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331.
+ */
+ ts->temp_offset = 257000;
+ ts->temp_step = 133;
} else {
/*
* The user manuals do not contain the formula for calculating
* the temperature. The formula used here is from the AXP209,
* which is designed by X-Powers, an affiliate of Allwinner:
*
- * temperature = -144.7 + (value * 0.1)
+ * temperature (C) = (value * 0.1) - 144.7
*
* Allwinner does not have any documentation whatsoever for
* this hardware. Moreover, it is claimed that the sensor
* is inaccurate and cannot work properly.
*/
- ts->temp_offset = 1447;
+ ts->temp_offset = 144700;
ts->temp_step = 100;
}
@@ -313,14 +328,20 @@ static int sun4i_ts_probe(struct platform_device *pdev)
ts->base + TP_CTRL0);
/*
- * sensitive_adjust = 15 : max, which is not all that sensitive,
+ * tp_sensitive_adjust is an optional property
* tp_mode = 0 : only x and y coordinates, as we don't use dual touch
*/
- writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0),
+ of_property_read_u32(np, "allwinner,tp-sensitive-adjust",
+ &tp_sensitive_adjust);
+ writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0),
ts->base + TP_CTRL2);
- /* Enable median filter, type 1 : 5/3 */
- writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3);
+ /*
+ * Enable median and averaging filter, optional property for
+ * filter type.
+ */
+ of_property_read_u32(np, "allwinner,filter-type", &filter_type);
+ writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3);
/* Enable temperature measurement, period 1953 (2 seconds) */
writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
@@ -330,10 +351,10 @@ static int sun4i_ts_probe(struct platform_device *pdev)
* finally enable tp mode.
*/
reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
- if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
- reg |= TP_MODE_EN(1);
- else
+ if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts"))
reg |= SUN6I_TP_MODE_EN(1);
+ else
+ reg |= TP_MODE_EN(1);
writel(reg, ts->base + TP_CTRL1);
/*
@@ -383,6 +404,7 @@ static int sun4i_ts_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_ts_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-ts", },
+ { .compatible = "allwinner,sun5i-a13-ts", },
{ .compatible = "allwinner,sun6i-a31-ts", },
{ /* sentinel */ }
};
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index f1cb05148b46..a24eba5ea843 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -1,7 +1,7 @@
/*
* Surface2.0/SUR40/PixelSense input driver
*
- * Copyright (c) 2013 by Florian 'floe' Echtler <floe@butterbrot.org>
+ * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org>
*
* Derived from the USB Skeleton driver 1.1,
* Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com)
@@ -12,6 +12,9 @@
* and from the generic hid-multitouch driver,
* Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
*
+ * and from the v4l2-pci-skeleton driver,
+ * Copyright (c) Copyright 2014 Cisco Systems, Inc.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
@@ -31,6 +34,11 @@
#include <linux/input-polldev.h>
#include <linux/input/mt.h>
#include <linux/usb/input.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-sg.h>
/* read 512 bytes from endpoint 0x86 -> get header + blobs */
struct sur40_header {
@@ -82,9 +90,19 @@ struct sur40_data {
struct sur40_blob blobs[];
} __packed;
+/* read 512 bytes from endpoint 0x82 -> get header below
+ * continue reading 16k blocks until header.size bytes read */
+struct sur40_image_header {
+ __le32 magic; /* "SUBF" */
+ __le32 packet_id;
+ __le32 size; /* always 0x0007e900 = 960x540 */
+ __le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */
+ __le32 unknown; /* "epoch?" always 02/03 00 00 00 */
+} __packed;
/* version information */
#define DRIVER_SHORT "sur40"
+#define DRIVER_LONG "Samsung SUR40"
#define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>"
#define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver"
@@ -99,6 +117,13 @@ struct sur40_data {
/* touch data endpoint */
#define TOUCH_ENDPOINT 0x86
+/* video data endpoint */
+#define VIDEO_ENDPOINT 0x82
+
+/* video header fields */
+#define VIDEO_HEADER_MAGIC 0x46425553
+#define VIDEO_PACKET_SIZE 16384
+
/* polling interval (ms) */
#define POLL_INTERVAL 10
@@ -113,21 +138,23 @@ struct sur40_data {
#define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */
#define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */
-/*
- * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
- * here by mistake which is very likely to have corrupted the firmware EEPROM
- * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
- * Should you ever run into a similar problem, the background story to this
- * incident and instructions on how to fix the corrupted EEPROM are available
- * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
-*/
-
+/* master device state */
struct sur40_state {
struct usb_device *usbdev;
struct device *dev;
struct input_polled_dev *input;
+ struct v4l2_device v4l2;
+ struct video_device vdev;
+ struct mutex lock;
+
+ struct vb2_queue queue;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct list_head buf_list;
+ spinlock_t qlock;
+ int sequence;
+
struct sur40_data *bulk_in_buffer;
size_t bulk_in_size;
u8 bulk_in_epaddr;
@@ -135,6 +162,27 @@ struct sur40_state {
char phys[64];
};
+struct sur40_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+/* 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);
+
+/*
+ * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
+ * here by mistake which is very likely to have corrupted the firmware EEPROM
+ * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
+ * Should you ever run into a similar problem, the background story to this
+ * incident and instructions on how to fix the corrupted EEPROM are available
+ * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
+*/
+
+/* command wrapper */
static int sur40_command(struct sur40_state *dev,
u8 command, u16 index, void *buffer, u16 size)
{
@@ -247,7 +295,6 @@ static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input)
/* core function: poll for new input data */
static void sur40_poll(struct input_polled_dev *polldev)
{
-
struct sur40_state *sur40 = polldev->private;
struct input_dev *input = polldev->input;
int result, bulk_read, need_blobs, packet_blobs, i;
@@ -314,6 +361,86 @@ static void sur40_poll(struct input_polled_dev *polldev)
input_mt_sync_frame(input);
input_sync(input);
+
+ sur40_process_video(sur40);
+}
+
+/* deal with video data */
+static void sur40_process_video(struct sur40_state *sur40)
+{
+
+ struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer);
+ struct sur40_buffer *new_buf;
+ struct usb_sg_request sgr;
+ struct sg_table *sgt;
+ int result, bulk_read;
+
+ if (!vb2_start_streaming_called(&sur40->queue))
+ return;
+
+ /* get a new buffer from the list */
+ spin_lock(&sur40->qlock);
+ if (list_empty(&sur40->buf_list)) {
+ dev_dbg(sur40->dev, "buffer queue empty\n");
+ spin_unlock(&sur40->qlock);
+ return;
+ }
+ new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list);
+ list_del(&new_buf->list);
+ spin_unlock(&sur40->qlock);
+
+ /* retrieve data via bulk read */
+ result = usb_bulk_msg(sur40->usbdev,
+ usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT),
+ sur40->bulk_in_buffer, sur40->bulk_in_size,
+ &bulk_read, 1000);
+
+ if (result < 0) {
+ dev_err(sur40->dev, "error in usb_bulk_read\n");
+ goto err_poll;
+ }
+
+ if (bulk_read != sizeof(struct sur40_image_header)) {
+ dev_err(sur40->dev, "received %d bytes (%zd expected)\n",
+ bulk_read, sizeof(struct sur40_image_header));
+ goto err_poll;
+ }
+
+ if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) {
+ dev_err(sur40->dev, "image magic mismatch\n");
+ goto err_poll;
+ }
+
+ if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) {
+ dev_err(sur40->dev, "image size mismatch\n");
+ goto err_poll;
+ }
+
+ sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0);
+
+ result = usb_sg_init(&sgr, sur40->usbdev,
+ usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
+ sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0);
+ if (result < 0) {
+ dev_err(sur40->dev, "error %d in usb_sg_init\n", result);
+ goto err_poll;
+ }
+
+ usb_sg_wait(&sgr);
+ if (sgr.status < 0) {
+ dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status);
+ goto err_poll;
+ }
+
+ /* mark as finished */
+ v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
+ new_buf->vb.v4l2_buf.sequence = sur40->sequence++;
+ new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
+ return;
+
+err_poll:
+ vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR);
}
/* Initialize input device parameters. */
@@ -377,6 +504,11 @@ static int sur40_probe(struct usb_interface *interface,
goto err_free_dev;
}
+ /* initialize locks/lists */
+ INIT_LIST_HEAD(&sur40->buf_list);
+ spin_lock_init(&sur40->qlock);
+ mutex_init(&sur40->lock);
+
/* Set up polled input device control structure */
poll_dev->private = sur40;
poll_dev->poll_interval = POLL_INTERVAL;
@@ -387,7 +519,7 @@ static int sur40_probe(struct usb_interface *interface,
/* Set up regular input device structure */
sur40_input_setup(poll_dev->input);
- poll_dev->input->name = "Samsung SUR40";
+ poll_dev->input->name = DRIVER_LONG;
usb_to_input_id(usbdev, &poll_dev->input->id);
usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
@@ -408,6 +540,7 @@ static int sur40_probe(struct usb_interface *interface,
goto err_free_polldev;
}
+ /* register the polled input device */
error = input_register_polled_device(poll_dev);
if (error) {
dev_err(&interface->dev,
@@ -415,12 +548,54 @@ static int sur40_probe(struct usb_interface *interface,
goto err_free_buffer;
}
+ /* register the video master device */
+ snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG);
+ error = v4l2_device_register(sur40->dev, &sur40->v4l2);
+ if (error) {
+ dev_err(&interface->dev,
+ "Unable to register video master device.");
+ goto err_unreg_v4l2;
+ }
+
+ /* initialize the lock and subdevice */
+ sur40->queue = sur40_queue;
+ sur40->queue.drv_priv = sur40;
+ sur40->queue.lock = &sur40->lock;
+
+ /* 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");
+ goto err_unreg_v4l2;
+ }
+
+ 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);
+ if (error) {
+ dev_err(&interface->dev,
+ "Unable to register video subdevice.");
+ goto err_unreg_video;
+ }
+
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, sur40);
dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC);
return 0;
+err_unreg_video:
+ video_unregister_device(&sur40->vdev);
+err_unreg_v4l2:
+ v4l2_device_unregister(&sur40->v4l2);
err_free_buffer:
kfree(sur40->bulk_in_buffer);
err_free_polldev:
@@ -436,6 +611,10 @@ static void sur40_disconnect(struct usb_interface *interface)
{
struct sur40_state *sur40 = usb_get_intfdata(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);
kfree(sur40->bulk_in_buffer);
@@ -445,12 +624,243 @@ static void sur40_disconnect(struct usb_interface *interface)
dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC);
}
+/*
+ * Setup the constraints of the queue: besides setting the number of planes
+ * per buffer and the size and allocation context of each plane, it also
+ * checks if sufficient buffers have been allocated. Usually 3 is a good
+ * minimum number: many DMA engines need a minimum of 2 buffers in the
+ * queue and you need to have another available for userspace processing.
+ */
+static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ 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;
+
+ if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage)
+ return -EINVAL;
+
+ *nplanes = 1;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage;
+ alloc_ctxs[0] = sur40->alloc_ctx;
+
+ return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size.
+ */
+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;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+ return 0;
+}
+
+/*
+ * Queue this buffer to the DMA engine.
+ */
+static void sur40_buffer_queue(struct vb2_buffer *vb)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
+ struct sur40_buffer *buf = (struct sur40_buffer *)vb;
+
+ spin_lock(&sur40->qlock);
+ list_add_tail(&buf->list, &sur40->buf_list);
+ spin_unlock(&sur40->qlock);
+}
+
+static void return_all_buffers(struct sur40_state *sur40,
+ enum vb2_buffer_state state)
+{
+ struct sur40_buffer *buf, *node;
+
+ spin_lock(&sur40->qlock);
+ list_for_each_entry_safe(buf, node, &sur40->buf_list, list) {
+ vb2_buffer_done(&buf->vb, state);
+ list_del(&buf->list);
+ }
+ spin_unlock(&sur40->qlock);
+}
+
+/*
+ * Start streaming. First check if the minimum number of buffers have been
+ * queued. If not, then return -ENOBUFS and the vb2 framework will call
+ * this function again the next time a buffer has been queued until enough
+ * buffers are available to actually start the DMA engine.
+ */
+static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+
+ sur40->sequence = 0;
+ return 0;
+}
+
+/*
+ * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued
+ * and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void sur40_stop_streaming(struct vb2_queue *vq)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+
+ /* Release all active buffers */
+ return_all_buffers(sur40, VB2_BUF_STATE_ERROR);
+}
+
+/* V4L ioctl */
+static int sur40_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct sur40_state *sur40 = video_drvdata(file);
+
+ 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 |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int sur40_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = V4L2_STD_UNKNOWN;
+ strlcpy(i->name, "In-Cell Sensor", sizeof(i->name));
+ i->capabilities = 0;
+ return 0;
+}
+
+static int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return (i == 0) ? 0 : -EINVAL;
+}
+
+static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int sur40_vidioc_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ f->fmt.pix = sur40_video_format;
+ return 0;
+}
+
+static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ 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;
+}
+
static const struct usb_device_id sur40_table[] = {
{ USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */
{ } /* terminating null entry */
};
MODULE_DEVICE_TABLE(usb, sur40_table);
+/* V4L2 structures */
+static const struct vb2_ops sur40_queue_ops = {
+ .queue_setup = sur40_queue_setup,
+ .buf_prepare = sur40_buffer_prepare,
+ .buf_queue = sur40_buffer_queue,
+ .start_streaming = sur40_start_streaming,
+ .stop_streaming = sur40_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue sur40_queue = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ /*
+ * VB2_USERPTR in currently not enabled: passing a user pointer to
+ * dma-sg will result in segment sizes that are not a multiple of
+ * 512 bytes, which is required by the host controller.
+ */
+ .io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF,
+ .buf_struct_size = sizeof(struct sur40_buffer),
+ .ops = &sur40_queue_ops,
+ .mem_ops = &vb2_dma_sg_memops,
+ .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+ .min_buffers_needed = 3,
+};
+
+static const struct v4l2_file_operations sur40_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 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_enum_input = sur40_vidioc_enum_input,
+ .vidioc_g_input = sur40_vidioc_g_input,
+ .vidioc_s_input = sur40_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 sur40_video_device = {
+ .name = DRIVER_LONG,
+ .fops = &sur40_video_fops,
+ .ioctl_ops = &sur40_video_ioctl_ops,
+ .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,
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
new file mode 100644
index 000000000000..642f4a53de50
--- /dev/null
+++ b/drivers/input/touchscreen/sx8654.c
@@ -0,0 +1,286 @@
+/*
+ * Driver for Semtech SX8654 I2C touchscreen controller.
+ *
+ * Copyright (c) 2015 Armadeus Systems
+ * Sébastien Szymanski <sebastien.szymanski@armadeus.com>
+ *
+ * Using code from:
+ * - sx865x.c
+ * Copyright (c) 2013 U-MoBo Srl
+ * Pierluigi Passaro <p.passaro@u-mobo.com>
+ * - sx8650.c
+ * Copyright (c) 2009 Wayne Roberts
+ * - tsc2007.c
+ * Copyright (c) 2008 Kwangwoo Lee
+ * - ads7846.c
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+/* register addresses */
+#define I2C_REG_TOUCH0 0x00
+#define I2C_REG_TOUCH1 0x01
+#define I2C_REG_CHANMASK 0x04
+#define I2C_REG_IRQMASK 0x22
+#define I2C_REG_IRQSRC 0x23
+#define I2C_REG_SOFTRESET 0x3f
+
+/* commands */
+#define CMD_READ_REGISTER 0x40
+#define CMD_MANUAL 0xc0
+#define CMD_PENTRG 0xe0
+
+/* value for I2C_REG_SOFTRESET */
+#define SOFTRESET_VALUE 0xde
+
+/* bits for I2C_REG_IRQSRC */
+#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08
+#define IRQ_PENRELEASE 0x04
+
+/* bits for RegTouch1 */
+#define CONDIRQ 0x20
+#define FILT_7SA 0x03
+
+/* bits for I2C_REG_CHANMASK */
+#define CONV_X 0x80
+#define CONV_Y 0x40
+
+/* coordinates rate: higher nibble of CTRL0 register */
+#define RATE_MANUAL 0x00
+#define RATE_5000CPS 0xf0
+
+/* power delay: lower nibble of CTRL0 register */
+#define POWDLY_1_1MS 0x0b
+
+#define MAX_12BIT ((1 << 12) - 1)
+
+struct sx8654 {
+ struct input_dev *input;
+ struct i2c_client *client;
+};
+
+static irqreturn_t sx8654_irq(int irq, void *handle)
+{
+ struct sx8654 *sx8654 = handle;
+ int irqsrc;
+ u8 data[4];
+ unsigned int x, y;
+ int retval;
+
+ irqsrc = i2c_smbus_read_byte_data(sx8654->client,
+ CMD_READ_REGISTER | I2C_REG_IRQSRC);
+ dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc);
+
+ if (irqsrc < 0)
+ goto out;
+
+ if (irqsrc & IRQ_PENRELEASE) {
+ dev_dbg(&sx8654->client->dev, "pen release interrupt");
+
+ input_report_key(sx8654->input, BTN_TOUCH, 0);
+ input_sync(sx8654->input);
+ }
+
+ if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
+ dev_dbg(&sx8654->client->dev, "pen touch interrupt");
+
+ retval = i2c_master_recv(sx8654->client, data, sizeof(data));
+ if (retval != sizeof(data))
+ goto out;
+
+ /* invalid data */
+ if (unlikely(data[0] & 0x80 || data[2] & 0x80))
+ goto out;
+
+ x = ((data[0] & 0xf) << 8) | (data[1]);
+ y = ((data[2] & 0xf) << 8) | (data[3]);
+
+ input_report_abs(sx8654->input, ABS_X, x);
+ input_report_abs(sx8654->input, ABS_Y, y);
+ input_report_key(sx8654->input, BTN_TOUCH, 1);
+ input_sync(sx8654->input);
+
+ dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int sx8654_open(struct input_dev *dev)
+{
+ struct sx8654 *sx8654 = input_get_drvdata(dev);
+ struct i2c_client *client = sx8654->client;
+ int error;
+
+ /* enable pen trigger mode */
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0,
+ RATE_5000CPS | POWDLY_1_1MS);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte(client, CMD_PENTRG);
+ if (error) {
+ dev_err(&client->dev, "writing command CMD_PENTRG failed");
+ return error;
+ }
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static void sx8654_close(struct input_dev *dev)
+{
+ struct sx8654 *sx8654 = input_get_drvdata(dev);
+ struct i2c_client *client = sx8654->client;
+ int error;
+
+ disable_irq(client->irq);
+
+ /* enable manual mode mode */
+ error = i2c_smbus_write_byte(client, CMD_MANUAL);
+ if (error) {
+ dev_err(&client->dev, "writing command CMD_MANUAL failed");
+ return;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+ return;
+ }
+}
+
+static int sx8654_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sx8654 *sx8654;
+ struct input_dev *input;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENXIO;
+
+ sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
+ if (!sx8654)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&client->dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = "SX8654 I2C Touchscreen";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+ input->open = sx8654_open;
+ input->close = sx8654_close;
+
+ __set_bit(INPUT_PROP_DIRECT, input->propbit);
+ input_set_capability(input, EV_KEY, BTN_TOUCH);
+ input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+ sx8654->client = client;
+ sx8654->input = input;
+
+ input_set_drvdata(sx8654->input, sx8654);
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
+ SOFTRESET_VALUE);
+ if (error) {
+ dev_err(&client->dev, "writing softreset value failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
+ CONV_X | CONV_Y);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+ IRQ_PENTOUCH_TOUCHCONVDONE |
+ IRQ_PENRELEASE);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
+ CONDIRQ | FILT_7SA);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
+ return error;
+ }
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, sx8654_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, sx8654);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to enable IRQ %d, error: %d\n",
+ client->irq, error);
+ return error;
+ }
+
+ /* Disable the IRQ, we'll enable it in sx8654_open() */
+ disable_irq(client->irq);
+
+ error = input_register_device(sx8654->input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, sx8654);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx8654_of_match[] = {
+ { .compatible = "semtech,sx8654", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sx8654_of_match);
+#endif
+
+static const struct i2c_device_id sx8654_id_table[] = {
+ { "semtech_sx8654", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
+
+static struct i2c_driver sx8654_driver = {
+ .driver = {
+ .name = "sx8654",
+ .of_match_table = of_match_ptr(sx8654_of_match),
+ },
+ .id_table = sx8654_id_table,
+ .probe = sx8654_probe,
+};
+module_i2c_driver(sx8654_driver);
+
+MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>");
+MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 1bf9906b5a3f..ccc8aa615709 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -75,7 +75,7 @@ struct tsc2007 {
u16 model;
u16 x_plate_ohms;
u16 max_rt;
- unsigned long poll_period;
+ unsigned long poll_period; /* in jiffies */
int fuzzx;
int fuzzy;
int fuzzz;
@@ -214,8 +214,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
}
- wait_event_timeout(ts->wait, ts->stopped,
- msecs_to_jiffies(ts->poll_period));
+ wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
}
dev_dbg(&ts->client->dev, "UP\n");
@@ -314,9 +313,9 @@ static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
ts->fuzzz = val32;
if (!of_property_read_u64(np, "ti,poll-period", &val64))
- ts->poll_period = val64;
+ ts->poll_period = msecs_to_jiffies(val64);
else
- ts->poll_period = 1;
+ ts->poll_period = msecs_to_jiffies(1);
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
ts->x_plate_ohms = val32;
@@ -350,7 +349,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
ts->max_rt = pdata->max_rt ? : MAX_12BIT;
- ts->poll_period = pdata->poll_period ? : 1;
+ ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1);
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
ts->fuzzx = pdata->fuzzx;
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index a0966331a89b..f2c6c352c55a 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -132,6 +132,7 @@ enum {
DEVTYPE_GUNZE,
DEVTYPE_DMC_TSC10,
DEVTYPE_IRTOUCH,
+ DEVTYPE_IRTOUCH_HIRES,
DEVTYPE_IDEALTEK,
DEVTYPE_GENERAL_TOUCH,
DEVTYPE_GOTOP,
@@ -198,6 +199,7 @@ static const struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
{USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
{USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+ {USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES},
#endif
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
@@ -1177,6 +1179,15 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.rept_size = 8,
.read_data = irtouch_read_data,
},
+
+ [DEVTYPE_IRTOUCH_HIRES] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 8,
+ .read_data = irtouch_read_data,
+ },
#endif
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
index 19880c7385e3..f58a196521a9 100644
--- a/drivers/input/touchscreen/zforce_ts.c
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -30,7 +30,6 @@
#include <linux/input/mt.h>
#include <linux/platform_data/zforce_ts.h>
#include <linux/regulator/consumer.h>
-#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>