summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/evdev.c11
-rw-r--r--drivers/input/gameport/gameport.c9
-rw-r--r--drivers/input/joydev.c11
-rw-r--r--drivers/input/joystick/db9.c4
-rw-r--r--drivers/input/joystick/gamecon.c3
-rw-r--r--drivers/input/joystick/turbografx.c4
-rw-r--r--drivers/input/joystick/xpad.c116
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c7
-rw-r--r--drivers/input/keyboard/locomokbd.c5
-rw-r--r--drivers/input/keyboard/matrix_keypad.c13
-rw-r--r--drivers/input/keyboard/omap4-keypad.c2
-rw-r--r--drivers/input/keyboard/qt1070.c9
-rw-r--r--drivers/input/keyboard/tca8418_keypad.c2
-rw-r--r--drivers/input/misc/Kconfig12
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/apanel.c3
-rw-r--r--drivers/input/misc/axp20x-pek.c62
-rw-r--r--drivers/input/misc/bma150.c11
-rw-r--r--drivers/input/misc/cpcap-pwrbutton.c117
-rw-r--r--drivers/input/misc/dm355evm_keys.c79
-rw-r--r--drivers/input/misc/drv260x.c4
-rw-r--r--drivers/input/misc/pm8xxx-vibrator.c78
-rw-r--r--drivers/input/misc/pwm-beeper.c15
-rw-r--r--drivers/input/misc/soc_button_array.c185
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c5
-rw-r--r--drivers/input/misc/wistron_btns.c5
-rw-r--r--drivers/input/misc/xen-kbdfront.c45
-rw-r--r--drivers/input/misc/yealink.h2
-rw-r--r--drivers/input/mouse/Kconfig16
-rw-r--r--drivers/input/mouse/Makefile2
-rw-r--r--drivers/input/mouse/alps.c76
-rw-r--r--drivers/input/mouse/alps.h6
-rw-r--r--drivers/input/mouse/elantech.c8
-rw-r--r--drivers/input/mouse/psmouse-base.c216
-rw-r--r--drivers/input/mouse/psmouse-smbus.c302
-rw-r--r--drivers/input/mouse/psmouse.h102
-rw-r--r--drivers/input/mouse/synaptics.c980
-rw-r--r--drivers/input/mouse/synaptics.h154
-rw-r--r--drivers/input/mouse/synaptics_i2c.c9
-rw-r--r--drivers/input/mousedev.c11
-rw-r--r--drivers/input/rmi4/rmi_driver.c13
-rw-r--r--drivers/input/rmi4/rmi_f12.c18
-rw-r--r--drivers/input/rmi4/rmi_f34.c27
-rw-r--r--drivers/input/rmi4/rmi_f34.h7
-rw-r--r--drivers/input/rmi4/rmi_f34v7.c117
-rw-r--r--drivers/input/rmi4/rmi_i2c.c51
-rw-r--r--drivers/input/rmi4/rmi_smbus.c94
-rw-r--r--drivers/input/rmi4/rmi_spi.c44
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h7
-rw-r--r--drivers/input/serio/serio.c30
-rw-r--r--drivers/input/sparse-keymap.c39
-rw-r--r--drivers/input/touchscreen/Kconfig28
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c67
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c131
-rw-r--r--drivers/input/touchscreen/ad7879.c152
-rw-r--r--drivers/input/touchscreen/ad7879.h14
-rw-r--r--drivers/input/touchscreen/ads7846.c2
-rw-r--r--drivers/input/touchscreen/ar1021_i2c.c30
-rw-r--r--drivers/input/touchscreen/eeti_ts.c226
-rw-r--r--drivers/input/touchscreen/imx6ul_tsc.c15
-rw-r--r--drivers/input/touchscreen/lpc32xx_ts.c13
-rw-r--r--drivers/input/touchscreen/max11801_ts.c7
-rw-r--r--drivers/input/touchscreen/melfas_mip4.c11
-rw-r--r--drivers/input/touchscreen/mxs-lradc-ts.c714
-rw-r--r--drivers/input/touchscreen/silead.c13
-rw-r--r--drivers/input/touchscreen/sur40.c2
-rw-r--r--drivers/input/touchscreen/tps6507x-ts.c33
-rw-r--r--drivers/input/touchscreen/tsc2007.h101
-rw-r--r--drivers/input/touchscreen/tsc2007_core.c (renamed from drivers/input/touchscreen/tsc2007.c)102
-rw-r--r--drivers/input/touchscreen/tsc2007_iio.c140
71 files changed, 3435 insertions, 1518 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e9ae3d500a55..925571475005 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -1354,8 +1354,6 @@ static void evdev_cleanup(struct evdev *evdev)
evdev_mark_dead(evdev);
evdev_hangup(evdev);
- cdev_del(&evdev->cdev);
-
/* evdev is marked dead so no one else accesses evdev->open */
if (evdev->open) {
input_flush_device(handle, NULL);
@@ -1416,12 +1414,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
- evdev->cdev.kobj.parent = &evdev->dev.kobj;
- error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
- if (error)
- goto err_unregister_handle;
- error = device_add(&evdev->dev);
+ error = cdev_device_add(&evdev->cdev, &evdev->dev);
if (error)
goto err_cleanup_evdev;
@@ -1429,7 +1423,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
err_cleanup_evdev:
evdev_cleanup(evdev);
- err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
@@ -1442,7 +1435,7 @@ static void evdev_disconnect(struct input_handle *handle)
{
struct evdev *evdev = handle->private;
- device_del(&evdev->dev);
+ cdev_device_del(&evdev->cdev, &evdev->dev);
evdev_cleanup(evdev);
input_free_minor(MINOR(evdev->dev.devt));
input_unregister_handle(handle);
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 4a2a9e370be7..cedc665364cd 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -385,8 +385,8 @@ static int gameport_queue_event(void *object, struct module *owner,
}
if (!try_module_get(owner)) {
- pr_warning("Can't get module reference, dropping event %d\n",
- event_type);
+ pr_warn("Can't get module reference, dropping event %d\n",
+ event_type);
kfree(event);
retval = -EINVAL;
goto out;
@@ -542,9 +542,8 @@ static void gameport_init_port(struct gameport *gameport)
INIT_LIST_HEAD(&gameport->node);
spin_lock_init(&gameport->timer_lock);
- init_timer(&gameport->poll_timer);
- gameport->poll_timer.function = gameport_run_poll_handler;
- gameport->poll_timer.data = (unsigned long)gameport;
+ setup_timer(&gameport->poll_timer, gameport_run_poll_handler,
+ (unsigned long)gameport);
}
/*
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 065e67bf56dd..29d677c714d2 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -742,8 +742,6 @@ static void joydev_cleanup(struct joydev *joydev)
joydev_mark_dead(joydev);
joydev_hangup(joydev);
- cdev_del(&joydev->cdev);
-
/* joydev is marked dead so no one else accesses joydev->open */
if (joydev->open)
input_close_device(handle);
@@ -913,12 +911,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
goto err_free_joydev;
cdev_init(&joydev->cdev, &joydev_fops);
- joydev->cdev.kobj.parent = &joydev->dev.kobj;
- error = cdev_add(&joydev->cdev, joydev->dev.devt, 1);
- if (error)
- goto err_unregister_handle;
- error = device_add(&joydev->dev);
+ error = cdev_device_add(&joydev->cdev, &joydev->dev);
if (error)
goto err_cleanup_joydev;
@@ -926,7 +920,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
err_cleanup_joydev:
joydev_cleanup(joydev);
- err_unregister_handle:
input_unregister_handle(&joydev->handle);
err_free_joydev:
put_device(&joydev->dev);
@@ -939,7 +932,7 @@ static void joydev_disconnect(struct input_handle *handle)
{
struct joydev *joydev = handle->private;
- device_del(&joydev->dev);
+ cdev_device_del(&joydev->cdev, &joydev->dev);
joydev_cleanup(joydev);
input_free_minor(MINOR(joydev->dev.devt));
input_unregister_handle(handle);
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index da326090c2b0..f4ad83eab67f 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -609,9 +609,7 @@ static void db9_attach(struct parport *pp)
db9->pd = pd;
db9->mode = mode;
db9->parportno = pp->number;
- init_timer(&db9->timer);
- db9->timer.data = (long) db9;
- db9->timer.function = db9_timer;
+ setup_timer(&db9->timer, db9_timer, (long)db9);
for (i = 0; i < (min(db9_mode->n_pads, DB9_MAX_DEVICES)); i++) {
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index eae14d512353..c43f087a496d 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -870,7 +870,8 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
err = gc_n64_init_ff(input_dev, idx);
if (err) {
- pr_warning("Failed to initiate rumble for N64 device %d\n", idx);
+ pr_warn("Failed to initiate rumble for N64 device %d\n",
+ idx);
goto err_free_dev;
}
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
index 77f575dd0901..a1fdc75a438d 100644
--- a/drivers/input/joystick/turbografx.c
+++ b/drivers/input/joystick/turbografx.c
@@ -200,9 +200,7 @@ static void tgfx_attach(struct parport *pp)
mutex_init(&tgfx->sem);
tgfx->pd = pd;
tgfx->parportno = pp->number;
- init_timer(&tgfx->timer);
- tgfx->timer.data = (long) tgfx;
- tgfx->timer.function = tgfx_timer;
+ setup_timer(&tgfx->timer, tgfx_timer, (long)tgfx);
for (i = 0; i < n_devs; i++) {
if (n_buttons[i] < 1)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 155fcb3b6230..df83fdc6c0e7 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -202,6 +202,7 @@ static const struct xpad_device {
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+ { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
{ 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 },
@@ -326,6 +327,7 @@ static struct usb_device_id xpad_table[] = {
XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */
XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */
+ XPAD_XBOXONE_VENDOR(0x1532), /* Razer Wildcat */
XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */
@@ -337,6 +339,64 @@ static struct usb_device_id xpad_table[] = {
MODULE_DEVICE_TABLE(usb, xpad_table);
+struct xboxone_init_packet {
+ u16 idVendor;
+ u16 idProduct;
+ const u8 *data;
+ u8 len;
+};
+
+#define XBOXONE_INIT_PKT(_vid, _pid, _data) \
+ { \
+ .idVendor = (_vid), \
+ .idProduct = (_pid), \
+ .data = (_data), \
+ .len = ARRAY_SIZE(_data), \
+ }
+
+
+/*
+ * This packet is required for all Xbox One pads with 2015
+ * or later firmware installed (or present from the factory).
+ */
+static const u8 xboxone_fw2015_init[] = {
+ 0x05, 0x20, 0x00, 0x01, 0x00
+};
+
+/*
+ * This packet is required for the Titanfall 2 Xbox One pads
+ * (0x0e6f:0x0165) to finish initialization and for Hori pads
+ * (0x0f0d:0x0067) to make the analog sticks work.
+ */
+static const u8 xboxone_hori_init[] = {
+ 0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
+ 0x00, 0x00, 0x00, 0x80, 0x00
+};
+
+/*
+ * A rumble packet is required for some PowerA pads to start
+ * sending input reports. One of those pads is (0x24c6:0x543a).
+ */
+static const u8 xboxone_zerorumble_init[] = {
+ 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * This specifies the selection of init packets that a gamepad
+ * will be sent on init *and* the order in which they will be
+ * sent. The correct sequence number will be added when the
+ * packet is going to be sent.
+ */
+static const struct xboxone_init_packet xboxone_init_packets[] = {
+ XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init),
+ XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init),
+ XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_zerorumble_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_zerorumble_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_zerorumble_init),
+};
+
struct xpad_output_packet {
u8 data[XPAD_PKT_LEN];
u8 len;
@@ -373,6 +433,7 @@ struct usb_xpad {
struct xpad_output_packet out_packets[XPAD_NUM_OUT_PACKETS];
int last_out_packet;
+ int init_seq;
#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
struct xpad_led *led;
@@ -748,11 +809,47 @@ exit:
}
/* Callers must hold xpad->odata_lock spinlock */
+static bool xpad_prepare_next_init_packet(struct usb_xpad *xpad)
+{
+ const struct xboxone_init_packet *init_packet;
+
+ if (xpad->xtype != XTYPE_XBOXONE)
+ return false;
+
+ /* Perform initialization sequence for Xbox One pads that require it */
+ while (xpad->init_seq < ARRAY_SIZE(xboxone_init_packets)) {
+ init_packet = &xboxone_init_packets[xpad->init_seq++];
+
+ if (init_packet->idVendor != 0 &&
+ init_packet->idVendor != xpad->dev->id.vendor)
+ continue;
+
+ if (init_packet->idProduct != 0 &&
+ init_packet->idProduct != xpad->dev->id.product)
+ continue;
+
+ /* This packet applies to our device, so prepare to send it */
+ memcpy(xpad->odata, init_packet->data, init_packet->len);
+ xpad->irq_out->transfer_buffer_length = init_packet->len;
+
+ /* Update packet with current sequence number */
+ xpad->odata[2] = xpad->odata_serial++;
+ return true;
+ }
+
+ return false;
+}
+
+/* Callers must hold xpad->odata_lock spinlock */
static bool xpad_prepare_next_out_packet(struct usb_xpad *xpad)
{
struct xpad_output_packet *pkt, *packet = NULL;
int i;
+ /* We may have init packets to send before we can send user commands */
+ if (xpad_prepare_next_init_packet(xpad))
+ return true;
+
for (i = 0; i < XPAD_NUM_OUT_PACKETS; i++) {
if (++xpad->last_out_packet >= XPAD_NUM_OUT_PACKETS)
xpad->last_out_packet = 0;
@@ -938,24 +1035,17 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
static int xpad_start_xbox_one(struct usb_xpad *xpad)
{
- struct xpad_output_packet *packet =
- &xpad->out_packets[XPAD_OUT_CMD_IDX];
unsigned long flags;
int retval;
spin_lock_irqsave(&xpad->odata_lock, flags);
- /* Xbox one controller needs to be initialized. */
- packet->data[0] = 0x05;
- packet->data[1] = 0x20;
- packet->data[2] = xpad->odata_serial++; /* packet serial */
- packet->data[3] = 0x01; /* rumble bit enable? */
- packet->data[4] = 0x00;
- packet->len = 5;
- packet->pending = true;
-
- /* Reset the sequence so we send out start packet first */
- xpad->last_out_packet = -1;
+ /*
+ * Begin the init sequence by attempting to send a packet.
+ * We will cycle through the init packet sequence before
+ * sending any packets from the output ring.
+ */
+ xpad->init_seq = 0;
retval = xpad_try_sending_next_out_packet(xpad);
spin_unlock_irqrestore(&xpad->odata_lock, flags);
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 6a250d65f8fe..c7a8120b13c0 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -30,6 +30,7 @@
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/sysrq.h>
#include <linux/input/matrix_keypad.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
@@ -260,6 +261,12 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
ckdev->ec->event_size);
break;
+ case EC_MKBP_EVENT_SYSRQ:
+ val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq);
+ dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val);
+ handle_sysrq(val);
+ break;
+
case EC_MKBP_EVENT_BUTTON:
case EC_MKBP_EVENT_SWITCH:
/*
diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
index c94d610b9d78..0d74312d5b02 100644
--- a/drivers/input/keyboard/locomokbd.c
+++ b/drivers/input/keyboard/locomokbd.c
@@ -264,9 +264,8 @@ static int locomokbd_probe(struct locomo_dev *dev)
spin_lock_init(&locomokbd->lock);
- init_timer(&locomokbd->timer);
- locomokbd->timer.function = locomokbd_timer_callback;
- locomokbd->timer.data = (unsigned long) locomokbd;
+ setup_timer(&locomokbd->timer, locomokbd_timer_callback,
+ (unsigned long)locomokbd);
locomokbd->suspend_jiffies = jiffies;
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 18839cd5f76e..1f316d66e6f7 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -42,9 +42,10 @@ struct matrix_keypad {
};
/*
- * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
- * minmal side effect when scanning other columns, here it is configured to
- * be input, and it should work on most platforms.
+ * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into
+ * HiZ when de-activated to cause minmal side effect when scanning other
+ * columns. In that case it is configured here to be input, otherwise it is
+ * driven with the inactive value.
*/
static void __activate_col(const struct matrix_keypad_platform_data *pdata,
int col, bool on)
@@ -55,7 +56,8 @@ static void __activate_col(const struct matrix_keypad_platform_data *pdata,
gpio_direction_output(pdata->col_gpios[col], level_on);
} else {
gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
- gpio_direction_input(pdata->col_gpios[col]);
+ if (!pdata->drive_inactive_cols)
+ gpio_direction_input(pdata->col_gpios[col]);
}
}
@@ -432,6 +434,9 @@ matrix_keypad_parse_dt(struct device *dev)
if (of_get_property(np, "gpio-activelow", NULL))
pdata->active_low = true;
+ pdata->drive_inactive_cols =
+ of_property_read_bool(np, "drive-inactive-cols");
+
of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
of_property_read_u32(np, "col-scan-delay-us",
&pdata->col_scan_delay_us);
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index ebc67ba41fe2..940d38b08e6b 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -358,7 +358,7 @@ static int omap4_keypad_probe(struct platform_device *pdev)
"omap4-keypad", keypad_data);
if (error) {
dev_err(&pdev->dev, "failed to register interrupt\n");
- goto err_free_input;
+ goto err_free_keymap;
}
device_init_wakeup(&pdev->dev, true);
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index 5a5778729e37..76bb51309a78 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -274,9 +274,18 @@ static const struct i2c_device_id qt1070_id[] = {
};
MODULE_DEVICE_TABLE(i2c, qt1070_id);
+#ifdef CONFIG_OF
+static const struct of_device_id qt1070_of_match[] = {
+ { .compatible = "qt1070", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qt1070_of_match);
+#endif
+
static struct i2c_driver qt1070_driver = {
.driver = {
.name = "qt1070",
+ .of_match_table = of_match_ptr(qt1070_of_match),
.pm = &qt1070_pm_ops,
},
.id_table = qt1070_id,
diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c
index 44dd7689c571..e37e335e406f 100644
--- a/drivers/input/keyboard/tca8418_keypad.c
+++ b/drivers/input/keyboard/tca8418_keypad.c
@@ -188,8 +188,6 @@ static void tca8418_read_keypad(struct tca8418_keypad *keypad_data)
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keymap[code], state);
- /* Read for next loop */
- error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
} while (1);
input_sync(input);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5b6c52210d20..3872488c3fd7 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -143,7 +143,7 @@ config INPUT_PM8941_PWRKEY
config INPUT_PM8XXX_VIBRATOR
tristate "Qualcomm PM8XXX vibrator support"
- depends on MFD_PM8XXX
+ depends on MFD_PM8XXX || MFD_SPMI_PMIC
select INPUT_FF_MEMLESS
help
This option enables device driver support for the vibrator
@@ -316,6 +316,16 @@ config INPUT_COBALT_BTNS
To compile this driver as a module, choose M here: the
module will be called cobalt_btns.
+config INPUT_CPCAP_PWRBUTTON
+ tristate "CPCAP OnKey"
+ depends on MFD_CPCAP
+ help
+ Say Y here if you want to enable power key reporting via the
+ Motorola CPCAP chip.
+
+ To compile this driver as a module, choose M here. The module will
+ be called cpcap-pwrbutton.
+
config INPUT_WISTRON_BTNS
tristate "x86 Wistron laptop button interface"
depends on X86_32
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index b10523f2878e..b923a9828c88 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
+obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.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
diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c
index 53630afab606..aad1df04c854 100644
--- a/drivers/input/misc/apanel.c
+++ b/drivers/input/misc/apanel.c
@@ -314,7 +314,8 @@ static int __init apanel_init(void)
if (devno >= APANEL_DEV_MAX)
pr_notice(APANEL ": unknown device %u found\n", devno);
else if (device_chip[devno] != CHIP_NONE)
- pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
+ pr_warn(APANEL ": duplicate entry for devno %u\n",
+ devno);
else if (method != 1 && method != 2 && method != 4) {
pr_notice(APANEL ": unknown method %u for devno %u\n",
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
index 1ac898db303a..f11807db6979 100644
--- a/drivers/input/misc/axp20x-pek.c
+++ b/drivers/input/misc/axp20x-pek.c
@@ -13,6 +13,7 @@
* GNU General Public License for more details.
*/
+#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/init.h>
@@ -188,21 +189,13 @@ static void axp20x_remove_sysfs_group(void *_data)
sysfs_remove_group(&dev->kobj, &axp20x_attribute_group);
}
-static int axp20x_pek_probe(struct platform_device *pdev)
+static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
+ struct platform_device *pdev)
{
- struct axp20x_pek *axp20x_pek;
- struct axp20x_dev *axp20x;
+ struct axp20x_dev *axp20x = axp20x_pek->axp20x;
struct input_dev *idev;
int error;
- axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
- GFP_KERNEL);
- if (!axp20x_pek)
- return -ENOMEM;
-
- axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
- axp20x = axp20x_pek->axp20x;
-
axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
if (axp20x_pek->irq_dbr < 0) {
dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
@@ -239,7 +232,7 @@ static int axp20x_pek_probe(struct platform_device *pdev)
axp20x_pek_irq, 0,
"axp20x-pek-dbr", idev);
if (error < 0) {
- dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
+ dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
axp20x_pek->irq_dbr, error);
return error;
}
@@ -248,30 +241,57 @@ static int axp20x_pek_probe(struct platform_device *pdev)
axp20x_pek_irq, 0,
"axp20x-pek-dbf", idev);
if (error < 0) {
- dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
+ dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
axp20x_pek->irq_dbf, error);
return error;
}
- error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
+ error = input_register_device(idev);
if (error) {
- dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n",
+ dev_err(&pdev->dev, "Can't register input device: %d\n",
error);
return error;
}
- error = devm_add_action(&pdev->dev,
- axp20x_remove_sysfs_group, &pdev->dev);
+ return 0;
+}
+
+static int axp20x_pek_probe(struct platform_device *pdev)
+{
+ struct axp20x_pek *axp20x_pek;
+ int error;
+
+ axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
+ GFP_KERNEL);
+ if (!axp20x_pek)
+ return -ENOMEM;
+
+ axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
+
+ /*
+ * Do not register the input device if there is an "INTCFD9"
+ * gpio button ACPI device, that handles the power button too,
+ * and otherwise we end up reporting all presses twice.
+ */
+ if (!acpi_dev_found("INTCFD9") ||
+ !IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY)) {
+ error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
+ if (error)
+ return error;
+ }
+
+ error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
if (error) {
- axp20x_remove_sysfs_group(&pdev->dev);
- dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
+ dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n",
error);
return error;
}
- error = input_register_device(idev);
+ error = devm_add_action(&pdev->dev,
+ axp20x_remove_sysfs_group, &pdev->dev);
if (error) {
- dev_err(axp20x->dev, "Can't register input device: %d\n",
+ axp20x_remove_sysfs_group(&pdev->dev);
+ dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
error);
return error;
}
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
index 1fa85379f86c..1efcfdf9f8a8 100644
--- a/drivers/input/misc/bma150.c
+++ b/drivers/input/misc/bma150.c
@@ -70,7 +70,6 @@
#define BMA150_CFG_5_REG 0x11
#define BMA150_CHIP_ID 2
-#define BMA180_CHIP_ID 3
#define BMA150_CHIP_ID_REG BMA150_DATA_0_REG
#define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG
@@ -538,13 +537,8 @@ static int bma150_probe(struct i2c_client *client,
return -EIO;
}
- /*
- * Note if the IIO CONFIG_BMA180 driver is enabled we want to fail
- * the probe for the bma180 as the iio driver is preferred.
- */
chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
- if (chip_id != BMA150_CHIP_ID &&
- (IS_ENABLED(CONFIG_BMA180) || chip_id != BMA180_CHIP_ID)) {
+ if (chip_id != BMA150_CHIP_ID) {
dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
return -EINVAL;
}
@@ -648,9 +642,6 @@ static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
static const struct i2c_device_id bma150_id[] = {
{ "bma150", 0 },
-#if !IS_ENABLED(CONFIG_BMA180)
- { "bma180", 0 },
-#endif
{ "smb380", 0 },
{ "bma023", 0 },
{ }
diff --git a/drivers/input/misc/cpcap-pwrbutton.c b/drivers/input/misc/cpcap-pwrbutton.c
new file mode 100644
index 000000000000..0abef63217e2
--- /dev/null
+++ b/drivers/input/misc/cpcap-pwrbutton.c
@@ -0,0 +1,117 @@
+/**
+ * CPCAP Power Button Input Driver
+ *
+ * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/motorola-cpcap.h>
+
+#define CPCAP_IRQ_ON 23
+#define CPCAP_IRQ_ON_BITMASK (1 << (CPCAP_IRQ_ON % 16))
+
+struct cpcap_power_button {
+ struct regmap *regmap;
+ struct input_dev *idev;
+ struct device *dev;
+};
+
+static irqreturn_t powerbutton_irq(int irq, void *_button)
+{
+ struct cpcap_power_button *button = _button;
+ int val;
+
+ val = cpcap_sense_virq(button->regmap, irq);
+ if (val < 0) {
+ dev_err(button->dev, "irq read failed: %d", val);
+ return IRQ_HANDLED;
+ }
+
+ pm_wakeup_event(button->dev, 0);
+ input_report_key(button->idev, KEY_POWER, val);
+ input_sync(button->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int cpcap_power_button_probe(struct platform_device *pdev)
+{
+ struct cpcap_power_button *button;
+ int irq = platform_get_irq(pdev, 0);
+ int err;
+
+ button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL);
+ if (!button)
+ return -ENOMEM;
+
+ button->idev = devm_input_allocate_device(&pdev->dev);
+ if (!button->idev)
+ return -ENOMEM;
+
+ button->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!button->regmap)
+ return -ENODEV;
+
+ button->dev = &pdev->dev;
+
+ button->idev->name = "cpcap-pwrbutton";
+ button->idev->phys = "cpcap-pwrbutton/input0";
+ button->idev->dev.parent = button->dev;
+ input_set_capability(button->idev, EV_KEY, KEY_POWER);
+
+ err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button);
+ if (err < 0) {
+ dev_err(&pdev->dev, "IRQ request failed: %d\n", err);
+ return err;
+ }
+
+ err = input_register_device(button->idev);
+ if (err) {
+ dev_err(&pdev->dev, "Input register failed: %d\n", err);
+ return err;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = {
+ { .compatible = "motorola,cpcap-pwrbutton" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table);
+#endif
+
+static struct platform_driver cpcap_power_button_driver = {
+ .probe = cpcap_power_button_probe,
+ .driver = {
+ .name = "cpcap-pwrbutton",
+ .of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table),
+ },
+};
+module_platform_driver(cpcap_power_button_driver);
+
+MODULE_ALIAS("platform:cpcap-pwrbutton");
+MODULE_DESCRIPTION("CPCAP Power Button");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
index 82e272ebc0ed..bab256ef32b9 100644
--- a/drivers/input/misc/dm355evm_keys.c
+++ b/drivers/input/misc/dm355evm_keys.c
@@ -32,7 +32,6 @@
struct dm355evm_keys {
struct input_dev *input;
struct device *dev;
- int irq;
};
/* These initial keycodes can be remapped */
@@ -176,74 +175,49 @@ static int dm355evm_keys_probe(struct platform_device *pdev)
{
struct dm355evm_keys *keys;
struct input_dev *input;
- int status;
+ int irq;
+ int error;
- /* allocate instance struct and input dev */
- keys = kzalloc(sizeof *keys, GFP_KERNEL);
- input = input_allocate_device();
- if (!keys || !input) {
- status = -ENOMEM;
- goto fail1;
- }
+ keys = devm_kzalloc(&pdev->dev, sizeof (*keys), GFP_KERNEL);
+ if (!keys)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return -ENOMEM;
keys->dev = &pdev->dev;
keys->input = input;
- /* set up "threaded IRQ handler" */
- status = platform_get_irq(pdev, 0);
- if (status < 0)
- goto fail1;
- keys->irq = status;
-
input->name = "DM355 EVM Controls";
input->phys = "dm355evm/input0";
- input->dev.parent = &pdev->dev;
input->id.bustype = BUS_I2C;
input->id.product = 0x0355;
input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
- status = sparse_keymap_setup(input, dm355evm_keys, NULL);
- if (status)
- goto fail1;
+ error = sparse_keymap_setup(input, dm355evm_keys, NULL);
+ if (error)
+ return error;
/* REVISIT: flush the event queue? */
- status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(&pdev->dev), keys);
- if (status < 0)
- goto fail2;
-
- /* register */
- status = input_register_device(input);
- if (status < 0)
- goto fail3;
-
- platform_set_drvdata(pdev, keys);
-
- return 0;
-
-fail3:
- free_irq(keys->irq, keys);
-fail2:
- sparse_keymap_free(input);
-fail1:
- input_free_device(input);
- kfree(keys);
- dev_err(&pdev->dev, "can't register, err %d\n", status);
-
- return status;
-}
+ /* set up "threaded IRQ handler" */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
-static int dm355evm_keys_remove(struct platform_device *pdev)
-{
- struct dm355evm_keys *keys = platform_get_drvdata(pdev);
+ error = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, dm355evm_keys_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&pdev->dev), keys);
+ if (error)
+ return error;
- free_irq(keys->irq, keys);
- sparse_keymap_free(keys->input);
- input_unregister_device(keys->input);
- kfree(keys);
+ /* register */
+ error = input_register_device(input);
+ if (error)
+ return error;
return 0;
}
@@ -259,7 +233,6 @@ static int dm355evm_keys_remove(struct platform_device *pdev)
*/
static struct platform_driver dm355evm_keys_driver = {
.probe = dm355evm_keys_probe,
- .remove = dm355evm_keys_remove,
.driver = {
.name = "dm355evm_keys",
},
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index fb089d36c0d6..17eb84ab4c0b 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -652,7 +652,6 @@ static const struct i2c_device_id drv260x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, drv260x_id);
-#ifdef CONFIG_OF
static const struct of_device_id drv260x_of_match[] = {
{ .compatible = "ti,drv2604", },
{ .compatible = "ti,drv2604l", },
@@ -661,13 +660,12 @@ static const struct of_device_id drv260x_of_match[] = {
{ }
};
MODULE_DEVICE_TABLE(of, drv260x_of_match);
-#endif
static struct i2c_driver drv260x_driver = {
.probe = drv260x_probe,
.driver = {
.name = "drv260x-haptics",
- .of_match_table = of_match_ptr(drv260x_of_match),
+ .of_match_table = drv260x_of_match,
.pm = &drv260x_pm_ops,
},
.id_table = drv260x_id,
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
index 5113877153d7..7dd1c1fbe42a 100644
--- a/drivers/input/misc/pm8xxx-vibrator.c
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -10,19 +10,15 @@
* GNU General Public License for more details.
*/
-#include <linux/module.h>
-#include <linux/kernel.h>
#include <linux/errno.h>
-#include <linux/platform_device.h>
#include <linux/input.h>
-#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
-
-#define VIB_DRV 0x4A
-
-#define VIB_DRV_SEL_MASK 0xf8
-#define VIB_DRV_SEL_SHIFT 0x03
-#define VIB_DRV_EN_MANUAL_MASK 0xfc
+#include <linux/slab.h>
#define VIB_MAX_LEVEL_mV (3100)
#define VIB_MIN_LEVEL_mV (1200)
@@ -30,20 +26,48 @@
#define MAX_FF_SPEED 0xff
+struct pm8xxx_regs {
+ unsigned int enable_addr;
+ unsigned int enable_mask;
+
+ unsigned int drv_addr;
+ unsigned int drv_mask;
+ unsigned int drv_shift;
+ unsigned int drv_en_manual_mask;
+};
+
+static const struct pm8xxx_regs pm8058_regs = {
+ .drv_addr = 0x4A,
+ .drv_mask = 0xf8,
+ .drv_shift = 3,
+ .drv_en_manual_mask = 0xfc,
+};
+
+static struct pm8xxx_regs pm8916_regs = {
+ .enable_addr = 0xc046,
+ .enable_mask = BIT(7),
+ .drv_addr = 0xc041,
+ .drv_mask = 0x1F,
+ .drv_shift = 0,
+ .drv_en_manual_mask = 0,
+};
+
/**
* struct pm8xxx_vib - structure to hold vibrator data
* @vib_input_dev: input device supporting force feedback
* @work: work structure to set the vibration parameters
* @regmap: regmap for register read/write
+ * @regs: registers' info
* @speed: speed of vibration set from userland
* @active: state of vibrator
* @level: level of vibration to set in the chip
- * @reg_vib_drv: VIB_DRV register value
+ * @reg_vib_drv: regs->drv_addr register value
*/
struct pm8xxx_vib {
struct input_dev *vib_input_dev;
struct work_struct work;
struct regmap *regmap;
+ const struct pm8xxx_regs *regs;
int speed;
int level;
bool active;
@@ -59,18 +83,24 @@ static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
{
int rc;
unsigned int val = vib->reg_vib_drv;
+ const struct pm8xxx_regs *regs = vib->regs;
if (on)
- val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+ val |= (vib->level << regs->drv_shift) & regs->drv_mask;
else
- val &= ~VIB_DRV_SEL_MASK;
+ val &= ~regs->drv_mask;
- rc = regmap_write(vib->regmap, VIB_DRV, val);
+ rc = regmap_write(vib->regmap, regs->drv_addr, val);
if (rc < 0)
return rc;
vib->reg_vib_drv = val;
- return 0;
+
+ if (regs->enable_mask)
+ rc = regmap_update_bits(vib->regmap, regs->enable_addr,
+ on ? regs->enable_mask : 0, val);
+
+ return rc;
}
/**
@@ -80,10 +110,11 @@ static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
static void pm8xxx_work_handler(struct work_struct *work)
{
struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work);
+ const struct pm8xxx_regs *regs = vib->regs;
int rc;
unsigned int val;
- rc = regmap_read(vib->regmap, VIB_DRV, &val);
+ rc = regmap_read(vib->regmap, regs->drv_addr, &val);
if (rc < 0)
return;
@@ -147,6 +178,7 @@ static int pm8xxx_vib_probe(struct platform_device *pdev)
struct input_dev *input_dev;
int error;
unsigned int val;
+ const struct pm8xxx_regs *regs;
vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL);
if (!vib)
@@ -163,16 +195,19 @@ static int pm8xxx_vib_probe(struct platform_device *pdev)
INIT_WORK(&vib->work, pm8xxx_work_handler);
vib->vib_input_dev = input_dev;
+ regs = of_device_get_match_data(&pdev->dev);
+
/* operate in manual mode */
- error = regmap_read(vib->regmap, VIB_DRV, &val);
+ error = regmap_read(vib->regmap, regs->drv_addr, &val);
if (error < 0)
return error;
- val &= ~VIB_DRV_EN_MANUAL_MASK;
- error = regmap_write(vib->regmap, VIB_DRV, val);
+ val &= regs->drv_en_manual_mask;
+ error = regmap_write(vib->regmap, regs->drv_addr, val);
if (error < 0)
return error;
+ vib->regs = regs;
vib->reg_vib_drv = val;
input_dev->name = "pm8xxx_vib_ffmemless";
@@ -212,8 +247,9 @@ static int __maybe_unused pm8xxx_vib_suspend(struct device *dev)
static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
static const struct of_device_id pm8xxx_vib_id_table[] = {
- { .compatible = "qcom,pm8058-vib" },
- { .compatible = "qcom,pm8921-vib" },
+ { .compatible = "qcom,pm8058-vib", .data = &pm8058_regs },
+ { .compatible = "qcom,pm8921-vib", .data = &pm8058_regs },
+ { .compatible = "qcom,pm8916-vib", .data = &pm8916_regs },
{ }
};
MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table);
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index e53801dbd560..edca0d737750 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -29,6 +30,7 @@ struct pwm_beeper {
struct regulator *amplifier;
struct work_struct work;
unsigned long period;
+ unsigned int bell_frequency;
bool suspended;
bool amplifier_on;
};
@@ -94,7 +96,7 @@ static int pwm_beeper_event(struct input_dev *input,
switch (code) {
case SND_BELL:
- value = value ? 1000 : 0;
+ value = value ? beeper->bell_frequency : 0;
break;
case SND_TONE:
break;
@@ -131,6 +133,7 @@ static int pwm_beeper_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct pwm_beeper *beeper;
struct pwm_state state;
+ u32 bell_frequency;
int error;
beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL);
@@ -167,6 +170,16 @@ static int pwm_beeper_probe(struct platform_device *pdev)
INIT_WORK(&beeper->work, pwm_beeper_work);
+ error = device_property_read_u32(dev, "beeper-hz", &bell_frequency);
+ if (error) {
+ bell_frequency = 1000;
+ dev_dbg(dev,
+ "failed to parse 'beeper-hz' property, using default: %uHz\n",
+ bell_frequency);
+ }
+
+ beeper->bell_frequency = bell_frequency;
+
beeper->input = devm_input_allocate_device(dev);
if (!beeper->input) {
dev_err(dev, "Failed to allocate input device\n");
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
index ddb2f22fca7a..e37d37273182 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
@@ -20,13 +20,6 @@
#include <linux/gpio.h>
#include <linux/platform_device.h>
-/*
- * Definition of buttons on the tablet. The ACPI index of each button
- * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
- * Platforms"
- */
-#define MAX_NBUTTONS 5
-
struct soc_button_info {
const char *name;
int acpi_index;
@@ -55,7 +48,7 @@ static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
struct gpio_desc *desc;
int gpio;
- desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index, GPIOD_ASIS);
+ desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -79,14 +72,19 @@ soc_button_device_create(struct platform_device *pdev,
int gpio;
int error;
+ for (info = button_info; info->name; info++)
+ if (info->autorepeat == autorepeat)
+ n_buttons++;
+
gpio_keys_pdata = devm_kzalloc(&pdev->dev,
sizeof(*gpio_keys_pdata) +
- sizeof(*gpio_keys) * MAX_NBUTTONS,
+ sizeof(*gpio_keys) * n_buttons,
GFP_KERNEL);
if (!gpio_keys_pdata)
return ERR_PTR(-ENOMEM);
gpio_keys = (void *)(gpio_keys_pdata + 1);
+ n_buttons = 0;
for (info = button_info; info->name; info++) {
if (info->autorepeat != autorepeat)
@@ -140,6 +138,153 @@ err_free_mem:
return ERR_PTR(error);
}
+static int soc_button_get_acpi_object_int(const union acpi_object *obj)
+{
+ if (obj->type != ACPI_TYPE_INTEGER)
+ return -1;
+
+ return obj->integer.value;
+}
+
+/* Parse a single ACPI0011 _DSD button descriptor */
+static int soc_button_parse_btn_desc(struct device *dev,
+ const union acpi_object *desc,
+ int collection_uid,
+ struct soc_button_info *info)
+{
+ int upage, usage;
+
+ if (desc->type != ACPI_TYPE_PACKAGE ||
+ desc->package.count != 5 ||
+ /* First byte should be 1 (control) */
+ soc_button_get_acpi_object_int(&desc->package.elements[0]) != 1 ||
+ /* Third byte should be collection uid */
+ soc_button_get_acpi_object_int(&desc->package.elements[2]) !=
+ collection_uid) {
+ dev_err(dev, "Invalid ACPI Button Descriptor\n");
+ return -ENODEV;
+ }
+
+ info->event_type = EV_KEY;
+ info->acpi_index =
+ soc_button_get_acpi_object_int(&desc->package.elements[1]);
+ upage = soc_button_get_acpi_object_int(&desc->package.elements[3]);
+ usage = soc_button_get_acpi_object_int(&desc->package.elements[4]);
+
+ /*
+ * The UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e descriptors use HID
+ * usage page and usage codes, but otherwise the device is not HID
+ * compliant: it uses one irq per button instead of generating HID
+ * input reports and some buttons should generate wakeups where as
+ * others should not, so we cannot use the HID subsystem.
+ *
+ * Luckily all devices only use a few usage page + usage combinations,
+ * so we can simply check for the known combinations here.
+ */
+ if (upage == 0x01 && usage == 0x81) {
+ info->name = "power";
+ info->event_code = KEY_POWER;
+ info->wakeup = true;
+ } else if (upage == 0x07 && usage == 0xe3) {
+ info->name = "home";
+ info->event_code = KEY_LEFTMETA;
+ info->wakeup = true;
+ } else if (upage == 0x0c && usage == 0xe9) {
+ info->name = "volume_up";
+ info->event_code = KEY_VOLUMEUP;
+ info->autorepeat = true;
+ } else if (upage == 0x0c && usage == 0xea) {
+ info->name = "volume_down";
+ info->event_code = KEY_VOLUMEDOWN;
+ info->autorepeat = true;
+ } else {
+ dev_warn(dev, "Unknown button index %d upage %02x usage %02x, ignoring\n",
+ info->acpi_index, upage, usage);
+ info->name = "unknown";
+ info->event_code = KEY_RESERVED;
+ }
+
+ return 0;
+}
+
+/* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */
+static const u8 btns_desc_uuid[16] = {
+ 0x25, 0xd6, 0x6b, 0xfa, 0xe8, 0x9c, 0x0d, 0x47,
+ 0xa2, 0xc7, 0xb3, 0xca, 0x36, 0xc4, 0x28, 0x2e
+};
+
+/* Parse ACPI0011 _DSD button descriptors */
+static struct soc_button_info *soc_button_get_button_info(struct device *dev)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ const union acpi_object *desc, *el0, *uuid, *btns_desc = NULL;
+ struct soc_button_info *button_info;
+ acpi_status status;
+ int i, btn, collection_uid = -1;
+
+ status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL,
+ &buf, ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "ACPI _DSD object not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Look for the Button Descriptors UUID */
+ desc = buf.pointer;
+ for (i = 0; (i + 1) < desc->package.count; i += 2) {
+ uuid = &desc->package.elements[i];
+
+ if (uuid->type != ACPI_TYPE_BUFFER ||
+ uuid->buffer.length != 16 ||
+ desc->package.elements[i + 1].type != ACPI_TYPE_PACKAGE) {
+ break;
+ }
+
+ if (memcmp(uuid->buffer.pointer, btns_desc_uuid, 16) == 0) {
+ btns_desc = &desc->package.elements[i + 1];
+ break;
+ }
+ }
+
+ if (!btns_desc) {
+ dev_err(dev, "ACPI Button Descriptors not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* The first package describes the collection */
+ el0 = &btns_desc->package.elements[0];
+ if (el0->type == ACPI_TYPE_PACKAGE &&
+ el0->package.count == 5 &&
+ /* First byte should be 0 (collection) */
+ soc_button_get_acpi_object_int(&el0->package.elements[0]) == 0 &&
+ /* Third byte should be 0 (top level collection) */
+ soc_button_get_acpi_object_int(&el0->package.elements[2]) == 0) {
+ collection_uid = soc_button_get_acpi_object_int(
+ &el0->package.elements[1]);
+ }
+ if (collection_uid == -1) {
+ dev_err(dev, "Invalid Button Collection Descriptor\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* There are package.count - 1 buttons + 1 terminating empty entry */
+ button_info = devm_kcalloc(dev, btns_desc->package.count,
+ sizeof(*button_info), GFP_KERNEL);
+ if (!button_info)
+ return ERR_PTR(-ENOMEM);
+
+ /* Parse the button descriptors */
+ for (i = 1, btn = 0; i < btns_desc->package.count; i++, btn++) {
+ if (soc_button_parse_btn_desc(dev,
+ &btns_desc->package.elements[i],
+ collection_uid,
+ &button_info[btn]))
+ return ERR_PTR(-ENODEV);
+ }
+
+ return button_info;
+}
+
static int soc_button_remove(struct platform_device *pdev)
{
struct soc_button_data *priv = platform_get_drvdata(pdev);
@@ -167,11 +312,18 @@ static int soc_button_probe(struct platform_device *pdev)
if (!id)
return -ENODEV;
- button_info = (struct soc_button_info *)id->driver_data;
+ if (!id->driver_data) {
+ button_info = soc_button_get_button_info(dev);
+ if (IS_ERR(button_info))
+ return PTR_ERR(button_info);
+ } else {
+ button_info = (struct soc_button_info *)id->driver_data;
+ }
- if (gpiod_count(dev, KBUILD_MODNAME) <= 0) {
+ error = gpiod_count(dev, NULL);
+ if (error < 0) {
dev_dbg(dev, "no GPIO attached, ignoring...\n");
- return -ENODEV;
+ return error;
}
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -197,9 +349,17 @@ static int soc_button_probe(struct platform_device *pdev)
if (!priv->children[0] && !priv->children[1])
return -ENODEV;
+ if (!id->driver_data)
+ devm_kfree(dev, button_info);
+
return 0;
}
+/*
+ * Definition of buttons on the tablet. The ACPI index of each button
+ * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
+ * Platforms"
+ */
static struct soc_button_info soc_button_PNP0C40[] = {
{ "power", 0, EV_KEY, KEY_POWER, false, true },
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
@@ -211,6 +371,7 @@ static struct soc_button_info soc_button_PNP0C40[] = {
static const struct acpi_device_id soc_button_acpi_match[] = {
{ "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+ { "ACPI0011", 0 },
{ }
};
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index 54162d2cbcfc..1c13005b228f 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -64,13 +64,12 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev)
return -ENOMEM;
}
- pwr->evbit[0] = BIT_MASK(EV_KEY);
- pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+ input_set_capability(pwr, EV_KEY, KEY_POWER);
pwr->name = "twl4030_pwrbutton";
pwr->phys = "twl4030_pwrbutton/input0";
pwr->dev.parent = &pdev->dev;
- err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq,
+ err = devm_request_threaded_irq(&pdev->dev, irq, NULL, powerbutton_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
"twl4030_pwrbutton", pwr);
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index e25f87ba19f6..43e67f546366 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -1243,12 +1243,10 @@ static int setup_input_dev(void)
error = input_register_polled_device(wistron_idev);
if (error)
- goto err_free_keymap;
+ goto err_free_dev;
return 0;
- err_free_keymap:
- sparse_keymap_free(input_dev);
err_free_dev:
input_free_polled_device(wistron_idev);
return error;
@@ -1300,7 +1298,6 @@ static int wistron_remove(struct platform_device *dev)
{
wistron_led_remove();
input_unregister_polled_device(wistron_idev);
- sparse_keymap_free(wistron_idev->input);
input_free_polled_device(wistron_idev);
bios_detach();
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 3900875dec10..690148f9940e 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -41,6 +41,12 @@ struct xenkbd_info {
char phys[32];
};
+enum { KPARAM_X, KPARAM_Y, KPARAM_CNT };
+static int ptr_size[KPARAM_CNT] = { XENFB_WIDTH, XENFB_HEIGHT };
+module_param_array(ptr_size, int, NULL, 0444);
+MODULE_PARM_DESC(ptr_size,
+ "Pointing device width, height in pixels (default 800,600)");
+
static int xenkbd_remove(struct xenbus_device *);
static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
static void xenkbd_disconnect_backend(struct xenkbd_info *);
@@ -84,8 +90,8 @@ static irqreturn_t input_handler(int rq, void *dev_id)
input_report_key(dev, event->key.keycode,
event->key.pressed);
else
- pr_warning("unhandled keycode 0x%x\n",
- event->key.keycode);
+ pr_warn("unhandled keycode 0x%x\n",
+ event->key.keycode);
break;
case XENKBD_TYPE_POS:
input_report_abs(dev, ABS_X, event->pos.abs_x);
@@ -128,12 +134,17 @@ static int xenkbd_probe(struct xenbus_device *dev,
if (!info->page)
goto error_nomem;
+ /* Set input abs params to match backend screen res */
abs = xenbus_read_unsigned(dev->otherend, "feature-abs-pointer", 0);
+ ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend, "width",
+ ptr_size[KPARAM_X]);
+ ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend, "height",
+ ptr_size[KPARAM_Y]);
if (abs) {
ret = xenbus_write(XBT_NIL, dev->nodename,
"request-abs-pointer", "1");
if (ret) {
- pr_warning("xenkbd: can't request abs-pointer");
+ pr_warn("xenkbd: can't request abs-pointer\n");
abs = 0;
}
}
@@ -174,8 +185,8 @@ static int xenkbd_probe(struct xenbus_device *dev,
if (abs) {
__set_bit(EV_ABS, ptr->evbit);
- input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
- input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
+ input_set_abs_params(ptr, ABS_X, 0, ptr_size[KPARAM_X], 0, 0);
+ input_set_abs_params(ptr, ABS_Y, 0, ptr_size[KPARAM_Y], 0, 0);
} else {
input_set_capability(ptr, EV_REL, REL_X);
input_set_capability(ptr, EV_REL, REL_Y);
@@ -309,9 +320,6 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *info)
static void xenkbd_backend_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
{
- struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
- int ret, val;
-
switch (backend_state) {
case XenbusStateInitialising:
case XenbusStateInitialised:
@@ -321,15 +329,6 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
break;
case XenbusStateInitWait:
-InitWait:
- if (xenbus_read_unsigned(info->xbdev->otherend,
- "feature-abs-pointer", 0)) {
- ret = xenbus_write(XBT_NIL, info->xbdev->nodename,
- "request-abs-pointer", "1");
- if (ret)
- pr_warning("xenkbd: can't request abs-pointer");
- }
-
xenbus_switch_state(dev, XenbusStateConnected);
break;
@@ -340,17 +339,7 @@ InitWait:
* get Connected twice here.
*/
if (dev->state != XenbusStateConnected)
- goto InitWait; /* no InitWait seen yet, fudge it */
-
- /* Set input abs params to match backend screen res */
- if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
- "width", "%d", &val) > 0)
- input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
-
- if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
- "height", "%d", &val) > 0)
- input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
-
+ xenbus_switch_state(dev, XenbusStateConnected);
break;
case XenbusStateClosed:
diff --git a/drivers/input/misc/yealink.h b/drivers/input/misc/yealink.h
index 1e0f52397010..934c247f8a0f 100644
--- a/drivers/input/misc/yealink.h
+++ b/drivers/input/misc/yealink.h
@@ -28,7 +28,7 @@
struct yld_ctl_packet {
u8 cmd; /* command code, see below */
u8 size; /* 1-11, size of used data bytes. */
- u16 offset; /* internal packet offset */
+ __be16 offset; /* internal packet offset */
u8 data[11];
s8 sum; /* negative sum of 15 preceding bytes */
} __attribute__ ((packed));
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 096abb4ad5cd..89ebb8f39fee 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -78,6 +78,18 @@ config MOUSE_PS2_SYNAPTICS
If unsure, say Y.
+config MOUSE_PS2_SYNAPTICS_SMBUS
+ bool "Synaptics PS/2 SMbus companion" if EXPERT
+ default y
+ depends on MOUSE_PS2
+ depends on I2C=y || I2C=MOUSE_PS2
+ select MOUSE_PS2_SMBUS
+ help
+ Say Y here if you have a Synaptics RMI4 touchpad connected to
+ to an SMBus, but enumerated through PS/2.
+
+ If unsure, say Y.
+
config MOUSE_PS2_CYPRESS
bool "Cypress PS/2 mouse protocol extension" if EXPERT
default y
@@ -171,6 +183,10 @@ config MOUSE_PS2_VMMOUSE
If unsure, say N.
+config MOUSE_PS2_SMBUS
+ bool
+ depends on MOUSE_PS2
+
config MOUSE_SERIAL
tristate "Serial mouse"
select SERIO
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 6168b134937b..56bf0ad877c6 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -39,6 +39,8 @@ psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE) += vmmouse.o
+psmouse-$(CONFIG_MOUSE_PS2_SMBUS) += psmouse-smbus.o
+
elan_i2c-objs := elan_i2c_core.o
elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C) += elan_i2c_i2c.o
elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_SMBUS) += elan_i2c_smbus.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index f210e19ddba6..262d1057c1da 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -106,39 +106,36 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
#define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */
static const struct alps_model_info alps_model_data[] = {
- { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */
- { { 0x33, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */
- { { 0x53, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x53, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x60, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */
- { { 0x63, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x63, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x63, 0x02, 0x28 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */
- { { 0x63, 0x02, 0x3c }, 0x00, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */
- { { 0x63, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */
- { { 0x63, 0x02, 0x64 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x63, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */
- { { 0x73, 0x00, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */
- { { 0x73, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
- { { 0x73, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */
-
/*
* XXX This entry is suspicious. First byte has zero lower nibble,
* which is what a normal mouse would report. Also, the value 0x0e
* isn't valid per PS/2 spec.
*/
- { { 0x20, 0x02, 0x0e }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
-
- { { 0x22, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
- { { 0x22, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */
- /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
- { { 0x62, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf,
- ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },
- { { 0x73, 0x00, 0x14 }, 0x00, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */
- { { 0x73, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */
- { { 0x52, 0x01, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff,
+ { { 0x20, 0x02, 0x0e }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+
+ { { 0x22, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+ { { 0x22, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */
+ { { 0x32, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */
+ { { 0x33, 0x02, 0x0a }, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */
+ { { 0x52, 0x01, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Toshiba Tecra A11-11L */
- { { 0x73, 0x02, 0x64 }, 0x8a, { ALPS_PROTO_V4, 0x8f, 0x8f, 0 } },
+ { { 0x53, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x53, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x60, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */
+ { { 0x62, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xcf, 0xcf,
+ ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
+ { { 0x63, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x63, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x63, 0x02, 0x28 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */
+ { { 0x63, 0x02, 0x3c }, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */
+ { { 0x63, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */
+ { { 0x63, 0x02, 0x64 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x63, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */
+ { { 0x73, 0x00, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */
+ { { 0x73, 0x00, 0x14 }, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */
+ { { 0x73, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+ { { 0x73, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */
+ { { 0x73, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */
};
static const struct alps_protocol_info alps_v3_protocol_data = {
@@ -149,6 +146,10 @@ static const struct alps_protocol_info alps_v3_rushmore_data = {
ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT
};
+static const struct alps_protocol_info alps_v4_protocol_data = {
+ ALPS_PROTO_V4, 0x8f, 0x8f, 0
+};
+
static const struct alps_protocol_info alps_v5_protocol_data = {
ALPS_PROTO_V5, 0xc8, 0xd8, 0
};
@@ -161,6 +162,10 @@ static const struct alps_protocol_info alps_v8_protocol_data = {
ALPS_PROTO_V8, 0x18, 0x18, 0
};
+static const struct alps_protocol_info alps_v9_protocol_data = {
+ ALPS_PROTO_V9, 0xc8, 0xc8, 0
+};
+
/*
* Some v2 models report the stick buttons in separate bits
*/
@@ -2806,12 +2811,8 @@ static const struct alps_protocol_info *alps_match_table(unsigned char *e7,
for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
model = &alps_model_data[i];
- if (!memcmp(e7, model->signature, sizeof(model->signature)) &&
- (!model->command_mode_resp ||
- model->command_mode_resp == ec[2])) {
-
+ if (!memcmp(e7, model->signature, sizeof(model->signature)))
return &model->protocol_info;
- }
}
return NULL;
@@ -2849,7 +2850,10 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
protocol = alps_match_table(e7, ec);
if (!protocol) {
- if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
+ if (e7[0] == 0x73 && e7[1] == 0x02 && e7[2] == 0x64 &&
+ ec[2] == 0x8a) {
+ protocol = &alps_v4_protocol_data;
+ } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
protocol = &alps_v5_protocol_data;
} else if (ec[0] == 0x88 &&
@@ -2863,6 +2867,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
} else if (e7[0] == 0x73 && e7[1] == 0x03 &&
(e7[2] == 0x14 || e7[2] == 0x28)) {
protocol = &alps_v8_protocol_data;
+ } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0xc8) {
+ protocol = &alps_v9_protocol_data;
+ psmouse_warn(psmouse,
+ "Unsupported ALPS V9 touchpad: E7=%3ph, EC=%3ph\n",
+ e7, ec);
+ return -EINVAL;
} else {
psmouse_dbg(psmouse,
"Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 4334f2805d93..ed2d6879fa52 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -23,6 +23,7 @@
#define ALPS_PROTO_V6 0x600
#define ALPS_PROTO_V7 0x700 /* t3btl t4s */
#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */
+#define ALPS_PROTO_V9 0x900 /* ss3btl */
#define MAX_TOUCHES 4
@@ -172,10 +173,6 @@ struct alps_protocol_info {
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
- * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
- * (aka command mode response) identifies the firmware minor version. This
- * can be used to distinguish different hardware models which are not
- * uniquely identifiable through their E7 responses.
* @protocol_info: information about protocol used by the device.
*
* Many (but not all) ALPS touchpads can be identified by looking at the
@@ -184,7 +181,6 @@ struct alps_protocol_info {
*/
struct alps_model_info {
u8 signature[3];
- u8 command_mode_resp;
struct alps_protocol_info protocol_info;
};
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index efc8ec342351..e73d968023f7 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1118,6 +1118,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
* Avatar AVIU-145A2 0x361f00 ? clickpad
* Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons
+ * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons
* Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons
* Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons
* Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**)
@@ -1524,6 +1525,13 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
},
},
{
+ /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"),
+ },
+ },
+ {
/* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index a598b7223cef..f73b47b8c578 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -116,17 +116,6 @@ static DEFINE_MUTEX(psmouse_mutex);
static struct workqueue_struct *kpsmoused_wq;
-struct psmouse_protocol {
- enum psmouse_type type;
- bool maxproto;
- bool ignore_parity; /* Protocol should ignore parity errors from KBC */
- bool try_passthru; /* Try protocol also on passthrough ports */
- const char *name;
- const char *alias;
- int (*detect)(struct psmouse *, bool);
- int (*init)(struct psmouse *);
-};
-
static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
{
input_report_key(dev, BTN_LEFT, buttons & BIT(0));
@@ -148,7 +137,7 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
/* Full packet accumulated, process it */
- switch (psmouse->type) {
+ switch (psmouse->protocol->type) {
case PSMOUSE_IMPS:
/* IntelliMouse has scroll wheel */
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
@@ -325,7 +314,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
goto out;
if (unlikely((flags & SERIO_TIMEOUT) ||
- ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) {
+ ((flags & SERIO_PARITY) &&
+ !psmouse->protocol->ignore_parity))) {
if (psmouse->state == PSMOUSE_ACTIVATED)
psmouse_warn(psmouse,
@@ -372,7 +362,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
}
if (psmouse->packet[1] == PSMOUSE_RET_ID ||
- (psmouse->type == PSMOUSE_HGPK &&
+ (psmouse->protocol->type == PSMOUSE_HGPK &&
psmouse->packet[1] == PSMOUSE_RET_BAT)) {
__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
serio_reconnect(serio);
@@ -783,7 +773,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.name = "SynPS/2",
.alias = "synaptics",
.detect = synaptics_detect,
- .init = synaptics_init,
+ .init = synaptics_init_absolute,
},
{
.type = PSMOUSE_SYNAPTICS_RELATIVE,
@@ -793,6 +783,16 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.init = synaptics_init_relative,
},
#endif
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
+ {
+ .type = PSMOUSE_SYNAPTICS_SMBUS,
+ .name = "SynSMBus",
+ .alias = "synaptics-smbus",
+ .detect = synaptics_detect,
+ .init = synaptics_init_smbus,
+ .smbus_companion = true,
+ },
+#endif
#ifdef CONFIG_MOUSE_PS2_ALPS
{
.type = PSMOUSE_ALPS,
@@ -959,6 +959,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+ psmouse->protocol = &psmouse_protocols[0];
+
psmouse->set_rate = psmouse_set_rate;
psmouse->set_resolution = psmouse_set_resolution;
psmouse->set_scale = psmouse_set_scale;
@@ -966,6 +968,7 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
psmouse->protocol_handler = psmouse_process_byte;
psmouse->pktsize = 3;
psmouse->reconnect = NULL;
+ psmouse->fast_reconnect = NULL;
psmouse->disconnect = NULL;
psmouse->cleanup = NULL;
psmouse->pt_activate = NULL;
@@ -1018,6 +1021,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
unsigned int max_proto, bool set_properties)
{
bool synaptics_hardware = false;
+ int ret;
/*
* Always check for focaltech, this is safe as it uses pnp-id
@@ -1080,9 +1084,14 @@ static int psmouse_extensions(struct psmouse *psmouse,
* enabled first, since we try detecting Synaptics
* even when protocol is disabled.
*/
- if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
- (!set_properties || synaptics_init(psmouse) == 0)) {
- return PSMOUSE_SYNAPTICS;
+ if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) ||
+ IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) {
+ if (!set_properties)
+ return PSMOUSE_SYNAPTICS;
+
+ ret = synaptics_init(psmouse);
+ if (ret >= 0)
+ return ret;
}
/*
@@ -1431,9 +1440,8 @@ static void psmouse_cleanup(struct serio *serio)
*/
static void psmouse_disconnect(struct serio *serio)
{
- struct psmouse *psmouse, *parent = NULL;
-
- psmouse = serio_get_drvdata(serio);
+ struct psmouse *psmouse = serio_get_drvdata(serio);
+ struct psmouse *parent = NULL;
sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
@@ -1461,7 +1469,10 @@ static void psmouse_disconnect(struct serio *serio)
serio_close(serio);
serio_set_drvdata(serio, NULL);
- input_unregister_device(psmouse->dev);
+
+ if (psmouse->dev)
+ input_unregister_device(psmouse->dev);
+
kfree(psmouse);
if (parent)
@@ -1475,6 +1486,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
{
const struct psmouse_protocol *selected_proto;
struct input_dev *input_dev = psmouse->dev;
+ enum psmouse_type type;
input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
@@ -1487,15 +1499,13 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
if (proto->init && proto->init(psmouse) < 0)
return -1;
- psmouse->type = proto->type;
selected_proto = proto;
} else {
- psmouse->type = psmouse_extensions(psmouse,
- psmouse_max_proto, true);
- selected_proto = psmouse_protocol_by_type(psmouse->type);
+ type = psmouse_extensions(psmouse, psmouse_max_proto, true);
+ selected_proto = psmouse_protocol_by_type(type);
}
- psmouse->ignore_parity = selected_proto->ignore_parity;
+ psmouse->protocol = selected_proto;
/*
* If mouse's packet size is 3 there is no point in polling the
@@ -1521,7 +1531,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
input_dev->phys = psmouse->phys;
input_dev->id.bustype = BUS_I8042;
input_dev->id.vendor = 0x0002;
- input_dev->id.product = psmouse->type;
+ input_dev->id.product = psmouse->protocol->type;
input_dev->id.version = psmouse->model;
return 0;
@@ -1583,12 +1593,18 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
psmouse_switch_protocol(psmouse, NULL);
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- psmouse_initialize(psmouse);
+ if (!psmouse->protocol->smbus_companion) {
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_initialize(psmouse);
- error = input_register_device(psmouse->dev);
- if (error)
- goto err_protocol_disconnect;
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_protocol_disconnect;
+ } else {
+ /* Smbus companion will be reporting events, not us. */
+ input_free_device(input_dev);
+ psmouse->dev = input_dev = NULL;
+ }
if (parent && parent->pt_activate)
parent->pt_activate(parent);
@@ -1597,7 +1613,12 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
if (error)
goto err_pt_deactivate;
- psmouse_activate(psmouse);
+ /*
+ * PS/2 devices having SMBus companions should stay disabled
+ * on PS/2 side, in order to have SMBus part operable.
+ */
+ if (!psmouse->protocol->smbus_companion)
+ psmouse_activate(psmouse);
out:
/* If this is a pass-through port the parent needs to be re-activated */
@@ -1610,8 +1631,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
err_pt_deactivate:
if (parent && parent->pt_deactivate)
parent->pt_deactivate(parent);
- input_unregister_device(psmouse->dev);
- input_dev = NULL; /* so we don't try to free it below */
+ if (input_dev) {
+ input_unregister_device(input_dev);
+ input_dev = NULL; /* so we don't try to free it below */
+ }
err_protocol_disconnect:
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
@@ -1628,15 +1651,26 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
goto out;
}
-static int psmouse_reconnect(struct serio *serio)
+static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
{
struct psmouse *psmouse = serio_get_drvdata(serio);
struct psmouse *parent = NULL;
- unsigned char type;
+ int (*reconnect_handler)(struct psmouse *);
+ enum psmouse_type type;
int rc = -1;
mutex_lock(&psmouse_mutex);
+ if (fast_reconnect) {
+ reconnect_handler = psmouse->fast_reconnect;
+ if (!reconnect_handler) {
+ rc = -ENOENT;
+ goto out_unlock;
+ }
+ } else {
+ reconnect_handler = psmouse->reconnect;
+ }
+
if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
parent = serio_get_drvdata(serio->parent);
psmouse_deactivate(parent);
@@ -1644,8 +1678,8 @@ static int psmouse_reconnect(struct serio *serio)
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
- if (psmouse->reconnect) {
- if (psmouse->reconnect(psmouse))
+ if (reconnect_handler) {
+ if (reconnect_handler(psmouse))
goto out;
} else {
psmouse_reset(psmouse);
@@ -1654,7 +1688,7 @@ static int psmouse_reconnect(struct serio *serio)
goto out;
type = psmouse_extensions(psmouse, psmouse_max_proto, false);
- if (psmouse->type != type)
+ if (psmouse->protocol->type != type)
goto out;
}
@@ -1662,14 +1696,21 @@ static int psmouse_reconnect(struct serio *serio)
* OK, the device type (and capabilities) match the old one,
* we can continue using it, complete initialization
*/
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
-
- psmouse_initialize(psmouse);
+ if (!psmouse->protocol->smbus_companion) {
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_initialize(psmouse);
+ }
if (parent && parent->pt_activate)
parent->pt_activate(parent);
- psmouse_activate(psmouse);
+ /*
+ * PS/2 devices having SMBus companions should stay disabled
+ * on PS/2 side, in order to have SMBus part operable.
+ */
+ if (!psmouse->protocol->smbus_companion)
+ psmouse_activate(psmouse);
+
rc = 0;
out:
@@ -1677,10 +1718,21 @@ out:
if (parent)
psmouse_activate(parent);
+out_unlock:
mutex_unlock(&psmouse_mutex);
return rc;
}
+static int psmouse_reconnect(struct serio *serio)
+{
+ return __psmouse_reconnect(serio, false);
+}
+
+static int psmouse_fast_reconnect(struct serio *serio)
+{
+ return __psmouse_reconnect(serio, true);
+}
+
static struct serio_device_id psmouse_serio_ids[] = {
{
.type = SERIO_8042,
@@ -1708,6 +1760,7 @@ static struct serio_driver psmouse_drv = {
.interrupt = psmouse_interrupt,
.connect = psmouse_connect,
.reconnect = psmouse_reconnect,
+ .fast_reconnect = psmouse_fast_reconnect,
.disconnect = psmouse_disconnect,
.cleanup = psmouse_cleanup,
};
@@ -1717,9 +1770,11 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *de
{
struct serio *serio = to_serio_port(dev);
struct psmouse_attribute *attr = to_psmouse_attr(devattr);
- struct psmouse *psmouse;
+ struct psmouse *psmouse = serio_get_drvdata(serio);
- psmouse = serio_get_drvdata(serio);
+ if (psmouse->protocol->smbus_companion &&
+ devattr != &psmouse_attr_protocol.dattr)
+ return -ENOENT;
return attr->show(psmouse, attr->data, buf);
}
@@ -1738,6 +1793,12 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
psmouse = serio_get_drvdata(serio);
+ if (psmouse->protocol->smbus_companion &&
+ devattr != &psmouse_attr_protocol.dattr) {
+ retval = -ENOENT;
+ goto out_unlock;
+ }
+
if (attr->protect) {
if (psmouse->state == PSMOUSE_IGNORE) {
retval = -ENODEV;
@@ -1749,13 +1810,14 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
psmouse_deactivate(parent);
}
- psmouse_deactivate(psmouse);
+ if (!psmouse->protocol->smbus_companion)
+ psmouse_deactivate(psmouse);
}
retval = attr->set(psmouse, attr->data, buf, count);
if (attr->protect) {
- if (retval != -ENODEV)
+ if (retval != -ENODEV && !psmouse->protocol->smbus_companion)
psmouse_activate(psmouse);
if (parent)
@@ -1792,7 +1854,7 @@ static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const
static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf)
{
- return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name);
+ return sprintf(buf, "%s\n", psmouse->protocol->name);
}
static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count)
@@ -1808,7 +1870,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
if (!proto)
return -EINVAL;
- if (psmouse->type == proto->type)
+ if (psmouse->protocol == proto)
return count;
new_dev = input_allocate_device();
@@ -1832,7 +1894,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
return -ENODEV;
}
- if (psmouse->type == proto->type) {
+ if (psmouse->protocol == proto) {
input_free_device(new_dev);
return count; /* switched by other thread */
}
@@ -1845,7 +1907,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
}
old_dev = psmouse->dev;
- old_proto = psmouse_protocol_by_type(psmouse->type);
+ old_proto = psmouse->protocol;
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
@@ -1864,23 +1926,29 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
psmouse_initialize(psmouse);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- error = input_register_device(psmouse->dev);
- if (error) {
- if (psmouse->disconnect)
- psmouse->disconnect(psmouse);
+ if (psmouse->protocol->smbus_companion) {
+ input_free_device(psmouse->dev);
+ psmouse->dev = NULL;
+ } else {
+ error = input_register_device(psmouse->dev);
+ if (error) {
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
- psmouse_set_state(psmouse, PSMOUSE_IGNORE);
- input_free_device(new_dev);
- psmouse->dev = old_dev;
- psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
- psmouse_switch_protocol(psmouse, old_proto);
- psmouse_initialize(psmouse);
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ input_free_device(new_dev);
+ psmouse->dev = old_dev;
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+ psmouse_switch_protocol(psmouse, old_proto);
+ psmouse_initialize(psmouse);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- return error;
+ return error;
+ }
}
- input_unregister_device(old_dev);
+ if (old_dev)
+ input_unregister_device(old_dev);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
@@ -1947,16 +2015,27 @@ static int __init psmouse_init(void)
synaptics_module_init();
hgpk_module_init();
+ err = psmouse_smbus_module_init();
+ if (err)
+ return err;
+
kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0);
if (!kpsmoused_wq) {
pr_err("failed to create kpsmoused workqueue\n");
- return -ENOMEM;
+ err = -ENOMEM;
+ goto err_smbus_exit;
}
err = serio_register_driver(&psmouse_drv);
if (err)
- destroy_workqueue(kpsmoused_wq);
+ goto err_destroy_wq;
+
+ return 0;
+err_destroy_wq:
+ destroy_workqueue(kpsmoused_wq);
+err_smbus_exit:
+ psmouse_smbus_module_exit();
return err;
}
@@ -1964,6 +2043,7 @@ static void __exit psmouse_exit(void)
{
serio_unregister_driver(&psmouse_drv);
destroy_workqueue(kpsmoused_wq);
+ psmouse_smbus_module_exit();
}
module_init(psmouse_init);
diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
new file mode 100644
index 000000000000..c7ac24d119c1
--- /dev/null
+++ b/drivers/input/mouse/psmouse-smbus.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2017 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/libps2.h>
+#include <linux/i2c.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include "psmouse.h"
+
+struct psmouse_smbus_dev {
+ struct i2c_board_info board;
+ struct psmouse *psmouse;
+ struct i2c_client *client;
+ struct list_head node;
+ bool dead;
+};
+
+static LIST_HEAD(psmouse_smbus_list);
+static DEFINE_MUTEX(psmouse_smbus_mutex);
+
+static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
+{
+ struct psmouse_smbus_dev *smbdev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
+ return;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
+ if (smbdev->dead)
+ continue;
+
+ if (smbdev->client)
+ continue;
+
+ /*
+ * Here would be a good place to check if device is actually
+ * present, but it seems that SMBus will not respond unless we
+ * fully reset PS/2 connection. So cross our fingers, and try
+ * to switch over, hopefully our system will not have too many
+ * "host notify" I2C adapters.
+ */
+ psmouse_dbg(smbdev->psmouse,
+ "SMBus candidate adapter appeared, triggering rescan\n");
+ serio_rescan(smbdev->psmouse->ps2dev.serio);
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+}
+
+static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
+{
+ struct psmouse_smbus_dev *smbdev, *tmp;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+ if (smbdev->client != client)
+ continue;
+
+ kfree(client->dev.platform_data);
+ client->dev.platform_data = NULL;
+
+ if (!smbdev->dead) {
+ psmouse_dbg(smbdev->psmouse,
+ "Marking SMBus companion %s as gone\n",
+ dev_name(&smbdev->client->dev));
+ smbdev->dead = true;
+ serio_rescan(smbdev->psmouse->ps2dev.serio);
+ } else {
+ list_del(&smbdev->node);
+ kfree(smbdev);
+ }
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+}
+
+static int psmouse_smbus_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->type == &i2c_adapter_type)
+ psmouse_smbus_check_adapter(to_i2c_adapter(dev));
+ break;
+
+ case BUS_NOTIFY_REMOVED_DEVICE:
+ if (dev->type == &i2c_client_type)
+ psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block psmouse_smbus_notifier = {
+ .notifier_call = psmouse_smbus_notifier_call,
+};
+
+static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
+{
+ return PSMOUSE_FULL_PACKET;
+}
+
+static int psmouse_smbus_reconnect(struct psmouse *psmouse)
+{
+ psmouse_deactivate(psmouse);
+
+ return 0;
+}
+
+struct psmouse_smbus_removal_work {
+ struct work_struct work;
+ struct i2c_client *client;
+};
+
+static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
+{
+ struct psmouse_smbus_removal_work *rwork =
+ container_of(work, struct psmouse_smbus_removal_work, work);
+
+ dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
+ i2c_unregister_device(rwork->client);
+
+ kfree(rwork);
+}
+
+/*
+ * This schedules removal of SMBus companion device. We have to do
+ * it in a separate tread to avoid deadlocking on psmouse_mutex in
+ * case the device has a trackstick (which is also driven by psmouse).
+ *
+ * Note that this may be racing with i2c adapter removal, but we
+ * can't do anything about that: i2c automatically destroys clients
+ * attached to an adapter that is being removed. This has to be
+ * fixed in i2c core.
+ */
+static void psmouse_smbus_schedule_remove(struct i2c_client *client)
+{
+ struct psmouse_smbus_removal_work *rwork;
+
+ rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
+ if (rwork) {
+ INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
+ rwork->client = client;
+
+ schedule_work(&rwork->work);
+ }
+}
+
+static void psmouse_smbus_disconnect(struct psmouse *psmouse)
+{
+ struct psmouse_smbus_dev *smbdev = psmouse->private;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ if (smbdev->dead) {
+ list_del(&smbdev->node);
+ kfree(smbdev);
+ } else {
+ smbdev->dead = true;
+ psmouse_dbg(smbdev->psmouse,
+ "posting removal request for SMBus companion %s\n",
+ dev_name(&smbdev->client->dev));
+ psmouse_smbus_schedule_remove(smbdev->client);
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+
+ psmouse->private = NULL;
+}
+
+static int psmouse_smbus_create_companion(struct device *dev, void *data)
+{
+ struct psmouse_smbus_dev *smbdev = data;
+ unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
+ struct i2c_adapter *adapter;
+
+ adapter = i2c_verify_adapter(dev);
+ if (!adapter)
+ return 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
+ return 0;
+
+ smbdev->client = i2c_new_probed_device(adapter, &smbdev->board,
+ addr_list, NULL);
+ if (!smbdev->client)
+ return 0;
+
+ /* We have our(?) device, stop iterating i2c bus. */
+ return 1;
+}
+
+void psmouse_smbus_cleanup(struct psmouse *psmouse)
+{
+ struct psmouse_smbus_dev *smbdev, *tmp;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+ if (psmouse == smbdev->psmouse) {
+ list_del(&smbdev->node);
+ kfree(smbdev);
+ }
+ }
+
+ mutex_unlock(&psmouse_smbus_mutex);
+}
+
+int psmouse_smbus_init(struct psmouse *psmouse,
+ const struct i2c_board_info *board,
+ const void *pdata, size_t pdata_size,
+ bool leave_breadcrumbs)
+{
+ struct psmouse_smbus_dev *smbdev;
+ int error;
+
+ smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
+ if (!smbdev)
+ return -ENOMEM;
+
+ smbdev->psmouse = psmouse;
+ smbdev->board = *board;
+
+ smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
+ if (!smbdev->board.platform_data) {
+ kfree(smbdev);
+ return -ENOMEM;
+ }
+
+ psmouse->private = smbdev;
+ psmouse->protocol_handler = psmouse_smbus_process_byte;
+ psmouse->reconnect = psmouse_smbus_reconnect;
+ psmouse->fast_reconnect = psmouse_smbus_reconnect;
+ psmouse->disconnect = psmouse_smbus_disconnect;
+ psmouse->resync_time = 0;
+
+ psmouse_deactivate(psmouse);
+
+ mutex_lock(&psmouse_smbus_mutex);
+ list_add_tail(&smbdev->node, &psmouse_smbus_list);
+ mutex_unlock(&psmouse_smbus_mutex);
+
+ /* Bind to already existing adapters right away */
+ error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
+
+ if (smbdev->client) {
+ /* We have our companion device */
+ return 0;
+ }
+
+ /*
+ * If we did not create i2c device we will not need platform
+ * data even if we are leaving breadcrumbs.
+ */
+ kfree(smbdev->board.platform_data);
+ smbdev->board.platform_data = NULL;
+
+ if (error < 0 || !leave_breadcrumbs) {
+ mutex_lock(&psmouse_smbus_mutex);
+ list_del(&smbdev->node);
+ mutex_unlock(&psmouse_smbus_mutex);
+
+ kfree(smbdev);
+ }
+
+ return error < 0 ? error : -EAGAIN;
+}
+
+int __init psmouse_smbus_module_init(void)
+{
+ int error;
+
+ error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
+ if (error) {
+ pr_err("failed to register i2c bus notifier: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+void psmouse_smbus_module_exit(void)
+{
+ bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
+ flush_scheduled_work();
+}
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 8c83b8e2505c..38855e425f01 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -44,21 +44,60 @@ enum psmouse_scale {
PSMOUSE_SCALE21
};
+enum psmouse_type {
+ PSMOUSE_NONE,
+ PSMOUSE_PS2,
+ PSMOUSE_PS2PP,
+ PSMOUSE_THINKPS,
+ PSMOUSE_GENPS,
+ PSMOUSE_IMPS,
+ PSMOUSE_IMEX,
+ PSMOUSE_SYNAPTICS,
+ PSMOUSE_ALPS,
+ PSMOUSE_LIFEBOOK,
+ PSMOUSE_TRACKPOINT,
+ PSMOUSE_TOUCHKIT_PS2,
+ PSMOUSE_CORTRON,
+ PSMOUSE_HGPK,
+ PSMOUSE_ELANTECH,
+ PSMOUSE_FSP,
+ PSMOUSE_SYNAPTICS_RELATIVE,
+ PSMOUSE_CYPRESS,
+ PSMOUSE_FOCALTECH,
+ PSMOUSE_VMMOUSE,
+ PSMOUSE_BYD,
+ PSMOUSE_SYNAPTICS_SMBUS,
+ PSMOUSE_AUTO /* This one should always be last */
+};
+
+struct psmouse;
+
+struct psmouse_protocol {
+ enum psmouse_type type;
+ bool maxproto;
+ bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+ bool try_passthru; /* Try protocol also on passthrough ports */
+ bool smbus_companion; /* "Protocol" is a stub, device is on SMBus */
+ const char *name;
+ const char *alias;
+ int (*detect)(struct psmouse *, bool);
+ int (*init)(struct psmouse *);
+};
+
struct psmouse {
void *private;
struct input_dev *dev;
struct ps2dev ps2dev;
struct delayed_work resync_work;
- char *vendor;
- char *name;
+ const char *vendor;
+ const char *name;
+ const struct psmouse_protocol *protocol;
unsigned char packet[8];
unsigned char badbyte;
unsigned char pktcnt;
unsigned char pktsize;
- unsigned char type;
unsigned char oob_data_type;
unsigned char extra_buttons;
- bool ignore_parity;
bool acks_disable_command;
unsigned int model;
unsigned long last;
@@ -80,6 +119,7 @@ struct psmouse {
void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale);
int (*reconnect)(struct psmouse *psmouse);
+ int (*fast_reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
void (*cleanup)(struct psmouse *psmouse);
int (*poll)(struct psmouse *psmouse);
@@ -88,31 +128,6 @@ struct psmouse {
void (*pt_deactivate)(struct psmouse *psmouse);
};
-enum psmouse_type {
- PSMOUSE_NONE,
- PSMOUSE_PS2,
- PSMOUSE_PS2PP,
- PSMOUSE_THINKPS,
- PSMOUSE_GENPS,
- PSMOUSE_IMPS,
- PSMOUSE_IMEX,
- PSMOUSE_SYNAPTICS,
- PSMOUSE_ALPS,
- PSMOUSE_LIFEBOOK,
- PSMOUSE_TRACKPOINT,
- PSMOUSE_TOUCHKIT_PS2,
- PSMOUSE_CORTRON,
- PSMOUSE_HGPK,
- PSMOUSE_ELANTECH,
- PSMOUSE_FSP,
- PSMOUSE_SYNAPTICS_RELATIVE,
- PSMOUSE_CYPRESS,
- PSMOUSE_FOCALTECH,
- PSMOUSE_VMMOUSE,
- PSMOUSE_BYD,
- PSMOUSE_AUTO /* This one should always be last */
-};
-
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
unsigned long delay);
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
@@ -195,5 +210,34 @@ static struct psmouse_attribute psmouse_attr_##_name = { \
&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
+#ifdef CONFIG_MOUSE_PS2_SMBUS
+
+int psmouse_smbus_module_init(void);
+void psmouse_smbus_module_exit(void);
+
+struct i2c_board_info;
+
+int psmouse_smbus_init(struct psmouse *psmouse,
+ const struct i2c_board_info *board,
+ const void *pdata, size_t pdata_size,
+ bool leave_breadcrumbs);
+void psmouse_smbus_cleanup(struct psmouse *psmouse);
+
+#else /* !CONFIG_MOUSE_PS2_SMBUS */
+
+static inline int psmouse_smbus_module_init(void)
+{
+ return 0;
+}
+
+static inline void psmouse_smbus_module_exit(void)
+{
+}
+
+static inline void psmouse_smbus_cleanup(struct psmouse *psmouse)
+{
+}
+
+#endif /* CONFIG_MOUSE_PS2_SMBUS */
#endif /* _PSMOUSE_H */
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 597ee4b01d9f..131df9d3660f 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -29,6 +29,8 @@
#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/libps2.h>
+#include <linux/rmi.h>
+#include <linux/i2c.h>
#include <linux/slab.h>
#include "psmouse.h"
#include "synaptics.h"
@@ -77,22 +79,27 @@
/*
* Set the synaptics touchpad mode byte by special commands
*/
-static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
+static int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode)
{
- unsigned char param[1];
+ u8 param[1];
+ int error;
+
+ error = psmouse_sliced_command(psmouse, mode);
+ if (error)
+ return error;
- if (psmouse_sliced_command(psmouse, mode))
- return -1;
param[0] = SYN_PS_SET_MODE2;
- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
- return -1;
+ error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE);
+ if (error)
+ return error;
+
return 0;
}
int synaptics_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[4];
+ u8 param[4];
param[0] = 0;
@@ -119,59 +126,8 @@ void synaptics_reset(struct psmouse *psmouse)
synaptics_mode_cmd(psmouse, 0);
}
-#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
-
-static bool cr48_profile_sensor;
-
-#define ANY_BOARD_ID 0
-struct min_max_quirk {
- const char * const *pnp_ids;
- struct {
- unsigned long int min, max;
- } board_id;
- int x_min, x_max, y_min, y_max;
-};
-
-static const struct min_max_quirk min_max_pnpid_table[] = {
- {
- (const char * const []){"LEN0033", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1024, 5052, 2258, 4832
- },
- {
- (const char * const []){"LEN0042", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1232, 5710, 1156, 4696
- },
- {
- (const char * const []){"LEN0034", "LEN0036", "LEN0037",
- "LEN0039", "LEN2002", "LEN2004",
- NULL},
- {ANY_BOARD_ID, 2961},
- 1024, 5112, 2024, 4832
- },
- {
- (const char * const []){"LEN2000", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1024, 5113, 2021, 4832
- },
- {
- (const char * const []){"LEN2001", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1024, 5022, 2508, 4832
- },
- {
- (const char * const []){"LEN2006", NULL},
- {2691, 2691},
- 1024, 5045, 2457, 4832
- },
- {
- (const char * const []){"LEN2006", NULL},
- {ANY_BOARD_ID, ANY_BOARD_ID},
- 1264, 5675, 1171, 4688
- },
- { }
-};
+#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
+ defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
/* This list has been kindly provided by Synaptics. */
static const char * const topbuttonpad_pnp_ids[] = {
@@ -211,101 +167,109 @@ static const char * const topbuttonpad_pnp_ids[] = {
NULL
};
-/* This list has been kindly provided by Synaptics. */
-static const char * const forcepad_pnp_ids[] = {
- "SYN300D",
- "SYN3014",
+static const char * const smbus_pnp_ids[] = {
+ /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */
+ "LEN0048", /* X1 Carbon 3 */
+ "LEN0046", /* X250 */
+ "LEN004a", /* W541 */
+ "LEN200f", /* T450s */
NULL
};
-/*****************************************************************************
- * Synaptics communications functions
- ****************************************************************************/
-
/*
- * Synaptics touchpads report the y coordinate from bottom to top, which is
- * opposite from what userspace expects.
- * This function is used to invert y before reporting.
+ * Send a command to the synpatics touchpad by special commands
*/
-static int synaptics_invert_y(int y)
+static int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param)
{
- return YMAX_NOMINAL + YMIN_NOMINAL - y;
+ int error;
+
+ error = psmouse_sliced_command(psmouse, cmd);
+ if (error)
+ return error;
+
+ error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int synaptics_query_int(struct psmouse *psmouse, u8 query_cmd, u32 *val)
+{
+ int error;
+ union {
+ __be32 be_val;
+ char buf[4];
+ } resp = { 0 };
+
+ error = synaptics_send_cmd(psmouse, query_cmd, resp.buf + 1);
+ if (error)
+ return error;
+
+ *val = be32_to_cpu(resp.be_val);
+ return 0;
}
/*
- * Send a command to the synpatics touchpad by special commands
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
*/
-static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
+static int synaptics_identify(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- if (psmouse_sliced_command(psmouse, c))
- return -1;
- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
- return -1;
- return 0;
+ int error;
+
+ error = synaptics_query_int(psmouse, SYN_QUE_IDENTIFY, &info->identity);
+ if (error)
+ return error;
+
+ return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO;
}
/*
* Read the model-id bytes from the touchpad
* see also SYN_MODEL_* macros
*/
-static int synaptics_model_id(struct psmouse *psmouse)
+static int synaptics_model_id(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
- unsigned char mi[3];
-
- if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
- return -1;
- priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
- return 0;
+ return synaptics_query_int(psmouse, SYN_QUE_MODEL, &info->model_id);
}
-static int synaptics_more_extended_queries(struct psmouse *psmouse)
+/*
+ * Read the firmware id from the touchpad
+ */
+static int synaptics_firmware_id(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
- unsigned char buf[3];
-
- if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf))
- return -1;
-
- priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2];
-
- return 0;
+ return synaptics_query_int(psmouse, SYN_QUE_FIRMWARE_ID,
+ &info->firmware_id);
}
/*
* Read the board id and the "More Extended Queries" from the touchpad
* The board id is encoded in the "QUERY MODES" response
*/
-static int synaptics_query_modes(struct psmouse *psmouse)
+static int synaptics_query_modes(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
- unsigned char bid[3];
+ u8 bid[3];
+ int error;
/* firmwares prior 7.5 have no board_id encoded */
- if (SYN_ID_FULL(priv->identity) < 0x705)
+ if (SYN_ID_FULL(info->identity) < 0x705)
return 0;
- if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid))
- return -1;
- priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
-
- if (SYN_MEXT_CAP_BIT(bid[0]))
- return synaptics_more_extended_queries(psmouse);
+ error = synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid);
+ if (error)
+ return error;
- return 0;
-}
+ info->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
-/*
- * Read the firmware id from the touchpad
- */
-static int synaptics_firmware_id(struct psmouse *psmouse)
-{
- struct synaptics_data *priv = psmouse->private;
- unsigned char fwid[3];
+ if (SYN_MEXT_CAP_BIT(bid[0]))
+ return synaptics_query_int(psmouse, SYN_QUE_MEXT_CAPAB_10,
+ &info->ext_cap_10);
- if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid))
- return -1;
- priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
return 0;
}
@@ -313,139 +277,236 @@ static int synaptics_firmware_id(struct psmouse *psmouse)
* Read the capability-bits from the touchpad
* see also the SYN_CAP_* macros
*/
-static int synaptics_capability(struct psmouse *psmouse)
+static int synaptics_capability(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
- unsigned char cap[3];
+ int error;
- if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
- return -1;
- priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
- priv->ext_cap = priv->ext_cap_0c = 0;
+ error = synaptics_query_int(psmouse, SYN_QUE_CAPABILITIES,
+ &info->capabilities);
+ if (error)
+ return error;
+
+ info->ext_cap = info->ext_cap_0c = 0;
/*
* Older firmwares had submodel ID fixed to 0x47
*/
- if (SYN_ID_FULL(priv->identity) < 0x705 &&
- SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) {
- return -1;
+ if (SYN_ID_FULL(info->identity) < 0x705 &&
+ SYN_CAP_SUBMODEL_ID(info->capabilities) != 0x47) {
+ return -ENXIO;
}
/*
* Unless capExtended is set the rest of the flags should be ignored
*/
- if (!SYN_CAP_EXTENDED(priv->capabilities))
- priv->capabilities = 0;
+ if (!SYN_CAP_EXTENDED(info->capabilities))
+ info->capabilities = 0;
- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
+ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 1) {
+ error = synaptics_query_int(psmouse, SYN_QUE_EXT_CAPAB,
+ &info->ext_cap);
+ if (error) {
psmouse_warn(psmouse,
"device claims to have extended capabilities, but I'm not able to read them.\n");
} else {
- priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
-
/*
* if nExtBtn is greater than 8 it should be considered
* invalid and treated as 0
*/
- if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
- priv->ext_cap &= 0xff0fff;
+ if (SYN_CAP_MULTI_BUTTON_NO(info->ext_cap) > 8)
+ info->ext_cap &= ~SYN_CAP_MB_MASK;
}
}
- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) {
+ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 4) {
+ error = synaptics_query_int(psmouse, SYN_QUE_EXT_CAPAB_0C,
+ &info->ext_cap_0c);
+ if (error)
psmouse_warn(psmouse,
"device claims to have extended capability 0x0c, but I'm not able to read it.\n");
- } else {
- priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];
- }
}
return 0;
}
/*
- * Identify Touchpad
- * See also the SYN_ID_* macros
- */
-static int synaptics_identify(struct psmouse *psmouse)
-{
- struct synaptics_data *priv = psmouse->private;
- unsigned char id[3];
-
- if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
- return -1;
- priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
- if (SYN_ID_IS_SYNAPTICS(priv->identity))
- return 0;
- return -1;
-}
-
-/*
* Read touchpad resolution and maximum reported coordinates
* Resolution is left zero if touchpad does not support the query
*/
-
-static int synaptics_resolution(struct psmouse *psmouse)
+static int synaptics_resolution(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
- unsigned char resp[3];
+ u8 resp[3];
+ int error;
- if (SYN_ID_MAJOR(priv->identity) < 4)
+ if (SYN_ID_MAJOR(info->identity) < 4)
return 0;
- if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) {
+ error = synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp);
+ if (!error) {
if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
- priv->x_res = resp[0]; /* x resolution in units/mm */
- priv->y_res = resp[2]; /* y resolution in units/mm */
+ info->x_res = resp[0]; /* x resolution in units/mm */
+ info->y_res = resp[2]; /* y resolution in units/mm */
}
}
- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
- SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
+ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 5 &&
+ SYN_CAP_MAX_DIMENSIONS(info->ext_cap_0c)) {
+ error = synaptics_send_cmd(psmouse,
+ SYN_QUE_EXT_MAX_COORDS, resp);
+ if (error) {
psmouse_warn(psmouse,
"device claims to have max coordinates query, but I'm not able to read it.\n");
} else {
- priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
- priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ info->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+ info->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
psmouse_info(psmouse,
"queried max coordinates: x [..%d], y [..%d]\n",
- priv->x_max, priv->y_max);
+ info->x_max, info->y_max);
}
}
- if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) &&
- (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 ||
+ if (SYN_CAP_MIN_DIMENSIONS(info->ext_cap_0c) &&
+ (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 7 ||
/*
* Firmware v8.1 does not report proper number of extended
* capabilities, but has been proven to report correct min
* coordinates.
*/
- SYN_ID_FULL(priv->identity) == 0x801)) {
- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
+ SYN_ID_FULL(info->identity) == 0x801)) {
+ error = synaptics_send_cmd(psmouse,
+ SYN_QUE_EXT_MIN_COORDS, resp);
+ if (error) {
psmouse_warn(psmouse,
"device claims to have min coordinates query, but I'm not able to read it.\n");
} else {
- priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
- priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ info->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+ info->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
psmouse_info(psmouse,
"queried min coordinates: x [%d..], y [%d..]\n",
- priv->x_min, priv->y_min);
+ info->x_min, info->y_min);
}
}
return 0;
}
+static int synaptics_query_hardware(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+{
+ int error;
+
+ error = synaptics_identify(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_model_id(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_firmware_id(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_query_modes(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_capability(psmouse, info);
+ if (error)
+ return error;
+
+ error = synaptics_resolution(psmouse, info);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+
+static bool cr48_profile_sensor;
+
+#define ANY_BOARD_ID 0
+struct min_max_quirk {
+ const char * const *pnp_ids;
+ struct {
+ u32 min, max;
+ } board_id;
+ u32 x_min, x_max, y_min, y_max;
+};
+
+static const struct min_max_quirk min_max_pnpid_table[] = {
+ {
+ (const char * const []){"LEN0033", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5052, 2258, 4832
+ },
+ {
+ (const char * const []){"LEN0042", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1232, 5710, 1156, 4696
+ },
+ {
+ (const char * const []){"LEN0034", "LEN0036", "LEN0037",
+ "LEN0039", "LEN2002", "LEN2004",
+ NULL},
+ {ANY_BOARD_ID, 2961},
+ 1024, 5112, 2024, 4832
+ },
+ {
+ (const char * const []){"LEN2000", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5113, 2021, 4832
+ },
+ {
+ (const char * const []){"LEN2001", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5022, 2508, 4832
+ },
+ {
+ (const char * const []){"LEN2006", NULL},
+ {2691, 2691},
+ 1024, 5045, 2457, 4832
+ },
+ {
+ (const char * const []){"LEN2006", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1264, 5675, 1171, 4688
+ },
+ { }
+};
+
+/* This list has been kindly provided by Synaptics. */
+static const char * const forcepad_pnp_ids[] = {
+ "SYN300D",
+ "SYN3014",
+ NULL
+};
+
+/*****************************************************************************
+ * Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Synaptics touchpads report the y coordinate from bottom to top, which is
+ * opposite from what userspace expects.
+ * This function is used to invert y before reporting.
+ */
+static int synaptics_invert_y(int y)
+{
+ return YMAX_NOMINAL + YMIN_NOMINAL - y;
+}
+
/*
* Apply quirk(s) if the hardware matches
*/
-
-static void synaptics_apply_quirks(struct psmouse *psmouse)
+static void synaptics_apply_quirks(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
- struct synaptics_data *priv = psmouse->private;
int i;
for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
@@ -454,62 +515,45 @@ static void synaptics_apply_quirks(struct psmouse *psmouse)
continue;
if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID &&
- priv->board_id < min_max_pnpid_table[i].board_id.min)
+ info->board_id < min_max_pnpid_table[i].board_id.min)
continue;
if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID &&
- priv->board_id > min_max_pnpid_table[i].board_id.max)
+ info->board_id > min_max_pnpid_table[i].board_id.max)
continue;
- priv->x_min = min_max_pnpid_table[i].x_min;
- priv->x_max = min_max_pnpid_table[i].x_max;
- priv->y_min = min_max_pnpid_table[i].y_min;
- priv->y_max = min_max_pnpid_table[i].y_max;
+ info->x_min = min_max_pnpid_table[i].x_min;
+ info->x_max = min_max_pnpid_table[i].x_max;
+ info->y_min = min_max_pnpid_table[i].y_min;
+ info->y_max = min_max_pnpid_table[i].y_max;
psmouse_info(psmouse,
"quirked min/max coordinates: x [%d..%d], y [%d..%d]\n",
- priv->x_min, priv->x_max,
- priv->y_min, priv->y_max);
+ info->x_min, info->x_max,
+ info->y_min, info->y_max);
break;
}
}
-static int synaptics_query_hardware(struct psmouse *psmouse)
-{
- if (synaptics_identify(psmouse))
- return -1;
- if (synaptics_model_id(psmouse))
- return -1;
- if (synaptics_firmware_id(psmouse))
- return -1;
- if (synaptics_query_modes(psmouse))
- return -1;
- if (synaptics_capability(psmouse))
- return -1;
- if (synaptics_resolution(psmouse))
- return -1;
-
- synaptics_apply_quirks(psmouse);
-
- return 0;
-}
-
static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
{
- static unsigned char param = 0xc8;
+ static u8 param = 0xc8;
struct synaptics_data *priv = psmouse->private;
+ int error;
- if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
- SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
+ if (!(SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)))
return 0;
- if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
- return -1;
+ error = psmouse_sliced_command(psmouse, SYN_QUE_MODEL);
+ if (error)
+ return error;
- if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
- return -1;
+ error = ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE);
+ if (error)
+ return error;
/* Advanced gesture mode also sends multi finger data */
- priv->capabilities |= BIT(1);
+ priv->info.capabilities |= BIT(1);
return 0;
}
@@ -517,6 +561,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
static int synaptics_set_mode(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
+ int error;
priv->mode = 0;
if (priv->absolute_mode)
@@ -525,16 +570,21 @@ static int synaptics_set_mode(struct psmouse *psmouse)
priv->mode |= SYN_BIT_DISABLE_GESTURE;
if (psmouse->rate >= 80)
priv->mode |= SYN_BIT_HIGH_RATE;
- if (SYN_CAP_EXTENDED(priv->capabilities))
+ if (SYN_CAP_EXTENDED(priv->info.capabilities))
priv->mode |= SYN_BIT_W_MODE;
- if (synaptics_mode_cmd(psmouse, priv->mode))
- return -1;
+ error = synaptics_mode_cmd(psmouse, priv->mode);
+ if (error)
+ return error;
- if (priv->absolute_mode &&
- synaptics_set_advanced_gesture_mode(psmouse)) {
- psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
- return -1;
+ if (priv->absolute_mode) {
+ error = synaptics_set_advanced_gesture_mode(psmouse);
+ if (error) {
+ psmouse_err(psmouse,
+ "Advanced gesture mode init failed: %d\n",
+ error);
+ return error;
+ }
}
return 0;
@@ -558,15 +608,20 @@ static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
/*****************************************************************************
* Synaptics pass-through PS/2 port support
****************************************************************************/
-static int synaptics_pt_write(struct serio *serio, unsigned char c)
+static int synaptics_pt_write(struct serio *serio, u8 c)
{
struct psmouse *parent = serio_get_drvdata(serio->parent);
- char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
+ u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
+ int error;
+
+ error = psmouse_sliced_command(parent, c);
+ if (error)
+ return error;
+
+ error = ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE);
+ if (error)
+ return error;
- if (psmouse_sliced_command(parent, c))
- return -1;
- if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
- return -1;
return 0;
}
@@ -592,13 +647,12 @@ static void synaptics_pt_stop(struct serio *serio)
serio_continue_rx(parent->ps2dev.serio);
}
-static int synaptics_is_pt_packet(unsigned char *buf)
+static int synaptics_is_pt_packet(u8 *buf)
{
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
}
-static void synaptics_pass_pt_packet(struct serio *ptport,
- unsigned char *packet)
+static void synaptics_pass_pt_packet(struct serio *ptport, u8 *packet)
{
struct psmouse *child = serio_get_drvdata(ptport);
@@ -661,7 +715,7 @@ static void synaptics_pt_create(struct psmouse *psmouse)
* Functions to interpret the absolute mode packets
****************************************************************************/
-static void synaptics_parse_agm(const unsigned char buf[],
+static void synaptics_parse_agm(const u8 buf[],
struct synaptics_data *priv,
struct synaptics_hw_state *hw)
{
@@ -688,31 +742,31 @@ static void synaptics_parse_agm(const unsigned char buf[],
}
}
-static void synaptics_parse_ext_buttons(const unsigned char buf[],
+static void synaptics_parse_ext_buttons(const u8 buf[],
struct synaptics_data *priv,
struct synaptics_hw_state *hw)
{
unsigned int ext_bits =
- (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+ (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
unsigned int ext_mask = GENMASK(ext_bits - 1, 0);
hw->ext_buttons = buf[4] & ext_mask;
hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits;
}
-static int synaptics_parse_hw_state(const unsigned char buf[],
+static int synaptics_parse_hw_state(const u8 buf[],
struct synaptics_data *priv,
struct synaptics_hw_state *hw)
{
memset(hw, 0, sizeof(struct synaptics_hw_state));
- if (SYN_MODEL_NEWABS(priv->model_id)) {
+ if (SYN_MODEL_NEWABS(priv->info.model_id)) {
hw->w = (((buf[0] & 0x30) >> 2) |
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
- if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
- SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+ if ((SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)) &&
hw->w == 2) {
synaptics_parse_agm(buf, priv, hw);
return 1;
@@ -765,7 +819,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->left = priv->report_press;
- } else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+ } else if (SYN_CAP_CLICKPAD(priv->info.ext_cap_0c)) {
/*
* Clickpad's button is transmitted as middle button,
* however, since it is primary button, we will report
@@ -773,18 +827,18 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
*/
hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
- } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+ } else if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) {
hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
if (hw->w == 2)
- hw->scroll = (signed char)(buf[1]);
+ hw->scroll = (s8)buf[1];
}
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
- if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 &&
+ if (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) > 0 &&
((buf[0] ^ buf[3]) & 0x02)) {
synaptics_parse_ext_buttons(buf, priv, hw);
}
@@ -853,24 +907,24 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
- int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+ int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
int i;
- if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+ if (!SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap))
return;
/* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
- if ((SYN_ID_FULL(priv->identity) == 0x801 ||
- SYN_ID_FULL(priv->identity) == 0x802) &&
+ if ((SYN_ID_FULL(priv->info.identity) == 0x801 ||
+ SYN_ID_FULL(priv->info.identity) == 0x802) &&
!((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
return;
- if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) {
+ if (!SYN_CAP_EXT_BUTTONS_STICK(priv->info.ext_cap_10)) {
for (i = 0; i < ext_bits; i++) {
input_report_key(dev, BTN_0 + 2 * i,
- hw->ext_buttons & (1 << i));
+ hw->ext_buttons & BIT(i));
input_report_key(dev, BTN_1 + 2 * i,
- hw->ext_buttons & (1 << (i + ext_bits)));
+ hw->ext_buttons & BIT(i + ext_bits));
}
return;
}
@@ -884,9 +938,9 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
u8 pt_buttons;
/* The trackstick expects at most 3 buttons */
- pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) |
- SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
- SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+ pt_buttons = SYN_EXT_BUTTON_STICK_L(hw->ext_buttons) |
+ SYN_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
+ SYN_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
serio_interrupt(priv->pt_port,
PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA);
@@ -903,10 +957,10 @@ static void synaptics_report_buttons(struct psmouse *psmouse,
input_report_key(dev, BTN_LEFT, hw->left);
input_report_key(dev, BTN_RIGHT, hw->right);
- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities))
input_report_key(dev, BTN_MIDDLE, hw->middle);
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
input_report_key(dev, BTN_FORWARD, hw->up);
input_report_key(dev, BTN_BACK, hw->down);
}
@@ -931,7 +985,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
pos[i].y = synaptics_invert_y(hw[i]->y);
}
- input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
+ input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->info.x_res);
for (i = 0; i < nsemi; i++) {
input_mt_slot(dev, slot[i]);
@@ -985,6 +1039,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
+ struct synaptics_device_info *info = &priv->info;
struct synaptics_hw_state hw;
int num_fingers;
int finger_width;
@@ -992,7 +1047,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
return;
- if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
synaptics_image_sensor_process(psmouse, &hw);
return;
}
@@ -1020,18 +1075,18 @@ static void synaptics_process_packet(struct psmouse *psmouse)
if (hw.z > 0 && hw.x > 1) {
num_fingers = 1;
finger_width = 5;
- if (SYN_CAP_EXTENDED(priv->capabilities)) {
+ if (SYN_CAP_EXTENDED(info->capabilities)) {
switch (hw.w) {
case 0 ... 1:
- if (SYN_CAP_MULTIFINGER(priv->capabilities))
+ if (SYN_CAP_MULTIFINGER(info->capabilities))
num_fingers = hw.w + 2;
break;
case 2:
- if (SYN_MODEL_PEN(priv->model_id))
+ if (SYN_MODEL_PEN(info->model_id))
; /* Nothing, treat a pen as a single finger */
break;
case 4 ... 15:
- if (SYN_CAP_PALMDETECT(priv->capabilities))
+ if (SYN_CAP_PALMDETECT(info->capabilities))
finger_width = hw.w;
break;
}
@@ -1046,7 +1101,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
return;
}
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c))
synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
num_fingers);
@@ -1063,11 +1118,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
}
input_report_abs(dev, ABS_PRESSURE, hw.z);
- if (SYN_CAP_PALMDETECT(priv->capabilities))
+ if (SYN_CAP_PALMDETECT(info->capabilities))
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
- if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+ if (SYN_CAP_MULTIFINGER(info->capabilities)) {
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
}
@@ -1077,18 +1132,18 @@ static void synaptics_process_packet(struct psmouse *psmouse)
input_sync(dev);
}
-static int synaptics_validate_byte(struct psmouse *psmouse,
- int idx, unsigned char pkt_type)
+static bool synaptics_validate_byte(struct psmouse *psmouse,
+ int idx, enum synaptics_pkt_type pkt_type)
{
- static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
- static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
- static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
- static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
- static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
- const char *packet = psmouse->packet;
+ static const u8 newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
+ static const u8 newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
+ static const u8 newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
+ static const u8 oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
+ static const u8 oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+ const u8 *packet = psmouse->packet;
if (idx < 0 || idx > 4)
- return 0;
+ return false;
switch (pkt_type) {
@@ -1104,19 +1159,21 @@ static int synaptics_validate_byte(struct psmouse *psmouse,
default:
psmouse_err(psmouse, "unknown packet type %d\n", pkt_type);
- return 0;
+ return false;
}
}
-static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
+static enum synaptics_pkt_type
+synaptics_detect_pkt_type(struct psmouse *psmouse)
{
int i;
- for (i = 0; i < 5; i++)
+ for (i = 0; i < 5; i++) {
if (!synaptics_validate_byte(psmouse, i, SYN_NEWABS_STRICT)) {
psmouse_info(psmouse, "using relaxed packet validation\n");
return SYN_NEWABS_RELAXED;
}
+ }
return SYN_NEWABS_STRICT;
}
@@ -1129,7 +1186,7 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
- if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
+ if (SYN_CAP_PASS_THROUGH(priv->info.capabilities) &&
synaptics_is_pt_packet(psmouse->packet)) {
if (priv->pt_port)
synaptics_pass_pt_packet(priv->pt_port,
@@ -1148,26 +1205,27 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
* Driver initialization/cleanup functions
****************************************************************************/
static void set_abs_position_params(struct input_dev *dev,
- struct synaptics_data *priv, int x_code,
- int y_code)
+ struct synaptics_device_info *info,
+ int x_code, int y_code)
{
- int x_min = priv->x_min ?: XMIN_NOMINAL;
- int x_max = priv->x_max ?: XMAX_NOMINAL;
- int y_min = priv->y_min ?: YMIN_NOMINAL;
- int y_max = priv->y_max ?: YMAX_NOMINAL;
- int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
+ int x_min = info->x_min ?: XMIN_NOMINAL;
+ int x_max = info->x_max ?: XMAX_NOMINAL;
+ int y_min = info->y_min ?: YMIN_NOMINAL;
+ int y_max = info->y_max ?: YMAX_NOMINAL;
+ int fuzz = SYN_CAP_REDUCED_FILTERING(info->ext_cap_0c) ?
SYN_REDUCED_FILTER_FUZZ : 0;
input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
- input_abs_set_res(dev, x_code, priv->x_res);
- input_abs_set_res(dev, y_code, priv->y_res);
+ input_abs_set_res(dev, x_code, info->x_res);
+ input_abs_set_res(dev, y_code, info->y_res);
}
static void set_input_params(struct psmouse *psmouse,
struct synaptics_data *priv)
{
struct input_dev *dev = psmouse->dev;
+ struct synaptics_device_info *info = &priv->info;
int i;
/* Things that apply to both modes */
@@ -1176,7 +1234,7 @@ static void set_input_params(struct psmouse *psmouse,
__set_bit(BTN_LEFT, dev->keybit);
__set_bit(BTN_RIGHT, dev->keybit);
- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
__set_bit(BTN_MIDDLE, dev->keybit);
if (!priv->absolute_mode) {
@@ -1189,15 +1247,15 @@ static void set_input_params(struct psmouse *psmouse,
/* Absolute mode */
__set_bit(EV_ABS, dev->evbit);
- set_abs_position_params(dev, priv, ABS_X, ABS_Y);
+ set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (cr48_profile_sensor)
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
- if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
- set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
- ABS_MT_POSITION_Y);
+ if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
+ set_abs_position_params(dev, info,
+ ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
@@ -1205,9 +1263,9 @@ static void set_input_params(struct psmouse *psmouse,
/* Image sensors can signal 4 and 5 finger clicks */
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
- } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
- set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
- ABS_MT_POSITION_Y);
+ } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) {
+ set_abs_position_params(dev, info,
+ ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
/*
* Profile sensor in CR-48 tracks contacts reasonably well,
* other non-image sensors with AGM use semi-mt.
@@ -1218,35 +1276,35 @@ static void set_input_params(struct psmouse *psmouse,
INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
}
- if (SYN_CAP_PALMDETECT(priv->capabilities))
+ if (SYN_CAP_PALMDETECT(info->capabilities))
input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
__set_bit(BTN_TOUCH, dev->keybit);
__set_bit(BTN_TOOL_FINGER, dev->keybit);
- if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+ if (SYN_CAP_MULTIFINGER(info->capabilities)) {
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
}
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
- SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+ if (SYN_CAP_FOUR_BUTTON(info->capabilities) ||
+ SYN_CAP_MIDDLE_BUTTON(info->capabilities)) {
__set_bit(BTN_FORWARD, dev->keybit);
__set_bit(BTN_BACK, dev->keybit);
}
- if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
- for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+ if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
+ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++)
__set_bit(BTN_0 + i, dev->keybit);
__clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
- if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+ if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
- !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
+ !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
/* Clickpads report only left button */
__clear_bit(BTN_RIGHT, dev->keybit);
@@ -1300,7 +1358,14 @@ static void synaptics_disconnect(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
- if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
+ /*
+ * We might have left a breadcrumb when trying to
+ * set up SMbus companion.
+ */
+ psmouse_smbus_cleanup(psmouse);
+
+ if (!priv->absolute_mode &&
+ SYN_ID_DISGEST_SUPPORTED(priv->info.identity))
device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_disable_gesture.dattr);
@@ -1312,8 +1377,8 @@ static void synaptics_disconnect(struct psmouse *psmouse)
static int synaptics_reconnect(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
- struct synaptics_data old_priv = *priv;
- unsigned char param[2];
+ struct synaptics_device_info info;
+ u8 param[2];
int retry = 0;
int error;
@@ -1334,32 +1399,34 @@ static int synaptics_reconnect(struct psmouse *psmouse)
} while (error && ++retry < 3);
if (error)
- return -1;
+ return error;
if (retry > 1)
psmouse_dbg(psmouse, "reconnected after %d tries\n", retry);
- if (synaptics_query_hardware(psmouse)) {
+ error = synaptics_query_hardware(psmouse, &info);
+ if (error) {
psmouse_err(psmouse, "Unable to query device.\n");
- return -1;
+ return error;
}
- if (synaptics_set_mode(psmouse)) {
+ error = synaptics_set_mode(psmouse);
+ if (error) {
psmouse_err(psmouse, "Unable to initialize device.\n");
- return -1;
+ return error;
}
- if (old_priv.identity != priv->identity ||
- old_priv.model_id != priv->model_id ||
- old_priv.capabilities != priv->capabilities ||
- old_priv.ext_cap != priv->ext_cap) {
+ if (info.identity != priv->info.identity ||
+ info.model_id != priv->info.model_id ||
+ info.capabilities != priv->info.capabilities ||
+ info.ext_cap != priv->info.ext_cap) {
psmouse_err(psmouse,
- "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
- old_priv.identity, priv->identity,
- old_priv.model_id, priv->model_id,
- old_priv.capabilities, priv->capabilities,
- old_priv.ext_cap, priv->ext_cap);
- return -1;
+ "hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x).\n",
+ priv->info.identity, info.identity,
+ priv->info.model_id, info.model_id,
+ priv->info.capabilities, info.capabilities,
+ priv->info.ext_cap, info.ext_cap);
+ return -ENXIO;
}
return 0;
@@ -1439,36 +1506,22 @@ void __init synaptics_module_init(void)
cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
}
-static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+static int synaptics_init_ps2(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool absolute_mode)
{
struct synaptics_data *priv;
- int err = -1;
+ int err;
- /*
- * The OLPC XO has issues with Synaptics' absolute mode; the constant
- * packet spew overloads the EC such that key presses on the keyboard
- * are missed. Given that, don't even attempt to use Absolute mode.
- * Relative mode seems to work just fine.
- */
- if (absolute_mode && broken_olpc_ec) {
- psmouse_info(psmouse,
- "OLPC XO detected, not enabling Synaptics protocol.\n");
- return -ENODEV;
- }
+ synaptics_apply_quirks(psmouse, info);
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- psmouse_reset(psmouse);
-
- if (synaptics_query_hardware(psmouse)) {
- psmouse_err(psmouse, "Unable to query device.\n");
- goto init_fail;
- }
-
+ priv->info = *info;
priv->absolute_mode = absolute_mode;
- if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+ if (SYN_ID_DISGEST_SUPPORTED(info->identity))
priv->disable_gesture = true;
/*
@@ -1477,20 +1530,22 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
*/
priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
- if (synaptics_set_mode(psmouse)) {
+ err = synaptics_set_mode(psmouse);
+ if (err) {
psmouse_err(psmouse, "Unable to initialize device.\n");
goto init_fail;
}
- priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
+ priv->pkt_type = SYN_MODEL_NEWABS(info->model_id) ?
+ SYN_NEWABS : SYN_OLDABS;
psmouse_info(psmouse,
- "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n",
- SYN_ID_MODEL(priv->identity),
- SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
- priv->model_id,
- priv->capabilities, priv->ext_cap, priv->ext_cap_0c,
- priv->ext_cap_10, priv->board_id, priv->firmware_id);
+ "Touchpad model: %lu, fw: %lu.%lu, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u\n",
+ SYN_ID_MODEL(info->identity),
+ SYN_ID_MAJOR(info->identity), SYN_ID_MINOR(info->identity),
+ info->model_id,
+ info->capabilities, info->ext_cap, info->ext_cap_0c,
+ info->ext_cap_10, info->board_id, info->firmware_id);
set_input_params(psmouse, priv);
@@ -1501,8 +1556,8 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
* Hardware info bits seem to be good candidates as they
* are documented to be for Synaptics corp. internal use.
*/
- psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
- (priv->model_id & 0x000000ff);
+ psmouse->model = ((info->model_id & 0x00ff0000) >> 8) |
+ (info->model_id & 0x000000ff);
if (absolute_mode) {
psmouse->protocol_handler = synaptics_process_byte;
@@ -1520,7 +1575,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
/* Synaptics can usually stay in sync without extra help */
psmouse->resync_time = 0;
- if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+ if (SYN_CAP_PASS_THROUGH(info->capabilities))
synaptics_pt_create(psmouse);
/*
@@ -1535,7 +1590,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
psmouse->rate = 40;
}
- if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
+ if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(info->identity)) {
err = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_disable_gesture.dattr);
if (err) {
@@ -1553,7 +1608,23 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
return err;
}
-int synaptics_init(struct psmouse *psmouse)
+static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+{
+ struct synaptics_device_info info;
+ int error;
+
+ psmouse_reset(psmouse);
+
+ error = synaptics_query_hardware(psmouse, &info);
+ if (error) {
+ psmouse_err(psmouse, "Unable to query device: %d\n", error);
+ return error;
+ }
+
+ return synaptics_init_ps2(psmouse, &info, absolute_mode);
+}
+
+int synaptics_init_absolute(struct psmouse *psmouse)
{
return __synaptics_init(psmouse, true);
}
@@ -1563,15 +1634,204 @@ int synaptics_init_relative(struct psmouse *psmouse)
return __synaptics_init(psmouse, false);
}
+static int synaptics_setup_ps2(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+{
+ bool absolute_mode = true;
+ int error;
+
+ /*
+ * The OLPC XO has issues with Synaptics' absolute mode; the constant
+ * packet spew overloads the EC such that key presses on the keyboard
+ * are missed. Given that, don't even attempt to use Absolute mode.
+ * Relative mode seems to work just fine.
+ */
+ if (broken_olpc_ec) {
+ psmouse_info(psmouse,
+ "OLPC XO detected, forcing relative protocol.\n");
+ absolute_mode = false;
+ }
+
+ error = synaptics_init_ps2(psmouse, info, absolute_mode);
+ if (error)
+ return error;
+
+ return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE;
+}
+
#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
void __init synaptics_module_init(void)
{
}
-int synaptics_init(struct psmouse *psmouse)
+static int __maybe_unused
+synaptics_setup_ps2(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
+
+/*
+ * The newest Synaptics device can use a secondary bus (called InterTouch) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+ SYNAPTICS_INTERTOUCH_NOT_SET = -1,
+ SYNAPTICS_INTERTOUCH_OFF,
+ SYNAPTICS_INTERTOUCH_ON,
+};
+
+static int synaptics_intertouch = SYNAPTICS_INTERTOUCH_NOT_SET;
+module_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644);
+MODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device.");
+
+static int synaptics_create_intertouch(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool leave_breadcrumbs)
+{
+ bool topbuttonpad =
+ psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+ !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10);
+ const struct rmi_device_platform_data pdata = {
+ .sensor_pdata = {
+ .sensor_type = rmi_sensor_touchpad,
+ .axis_align.flip_y = true,
+ /* to prevent cursors jumps: */
+ .kernel_tracking = true,
+ .topbuttonpad = topbuttonpad,
+ },
+ .f30_data = {
+ .buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c),
+ .trackstick_buttons =
+ !!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10),
+ },
+ };
+ const struct i2c_board_info intertouch_board = {
+ I2C_BOARD_INFO("rmi4_smbus", 0x2c),
+ .flags = I2C_CLIENT_HOST_NOTIFY,
+ };
+
+ return psmouse_smbus_init(psmouse, &intertouch_board,
+ &pdata, sizeof(pdata),
+ leave_breadcrumbs);
+}
+
+/**
+ * synaptics_setup_intertouch - called once the PS/2 devices are enumerated
+ * and decides to instantiate a SMBus InterTouch device.
+ */
+static int synaptics_setup_intertouch(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool leave_breadcrumbs)
+{
+ int error;
+
+ if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF)
+ return -ENXIO;
+
+ if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) {
+ if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+ !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids))
+ return -ENXIO;
+ }
+
+ psmouse_info(psmouse, "Trying to set up SMBus access\n");
+
+ error = synaptics_create_intertouch(psmouse, info, leave_breadcrumbs);
+ if (error) {
+ if (error == -EAGAIN)
+ psmouse_info(psmouse, "SMbus companion is not ready yet\n");
+ else
+ psmouse_err(psmouse, "unable to create intertouch device\n");
+
+ return error;
+ }
+
+ return 0;
+}
+
+int synaptics_init_smbus(struct psmouse *psmouse)
+{
+ struct synaptics_device_info info;
+ int error;
+
+ psmouse_reset(psmouse);
+
+ error = synaptics_query_hardware(psmouse, &info);
+ if (error) {
+ psmouse_err(psmouse, "Unable to query device: %d\n", error);
+ return error;
+ }
+
+ if (!SYN_CAP_INTERTOUCH(info.ext_cap_0c))
+ return -ENXIO;
+
+ return synaptics_create_intertouch(psmouse, &info, false);
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+static int __maybe_unused
+synaptics_setup_intertouch(struct psmouse *psmouse,
+ struct synaptics_device_info *info,
+ bool leave_breadcrumbs)
+{
+ return -ENOSYS;
+}
+
+int synaptics_init_smbus(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
+ defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
+
+int synaptics_init(struct psmouse *psmouse)
+{
+ struct synaptics_device_info info;
+ int error;
+ int retval;
+
+ psmouse_reset(psmouse);
+
+ error = synaptics_query_hardware(psmouse, &info);
+ if (error) {
+ psmouse_err(psmouse, "Unable to query device: %d\n", error);
+ return error;
+ }
+
+ if (SYN_CAP_INTERTOUCH(info.ext_cap_0c)) {
+ error = synaptics_setup_intertouch(psmouse, &info, true);
+ if (!error)
+ return PSMOUSE_SYNAPTICS_SMBUS;
+ }
+
+ retval = synaptics_setup_ps2(psmouse, &info);
+ if (retval < 0) {
+ /*
+ * Not using any flavor of Synaptics support, so clean up
+ * SMbus breadcrumbs, if any.
+ */
+ psmouse_smbus_cleanup(psmouse);
+ }
+
+ return retval;
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+int synaptics_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 116ae2546ace..fc00e005c611 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -25,36 +25,37 @@
#define SYN_QUE_MEXT_CAPAB_10 0x10
/* synatics modes */
-#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
-#define SYN_BIT_HIGH_RATE (1 << 6)
-#define SYN_BIT_SLEEP_MODE (1 << 3)
-#define SYN_BIT_DISABLE_GESTURE (1 << 2)
-#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1)
-#define SYN_BIT_W_MODE (1 << 0)
+#define SYN_BIT_ABSOLUTE_MODE BIT(7)
+#define SYN_BIT_HIGH_RATE BIT(6)
+#define SYN_BIT_SLEEP_MODE BIT(3)
+#define SYN_BIT_DISABLE_GESTURE BIT(2)
+#define SYN_BIT_FOUR_BYTE_CLIENT BIT(1)
+#define SYN_BIT_W_MODE BIT(0)
/* synaptics model ID bits */
-#define SYN_MODEL_ROT180(m) ((m) & (1 << 23))
-#define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22))
-#define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f)
-#define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f)
-#define SYN_MODEL_NEWABS(m) ((m) & (1 << 7))
-#define SYN_MODEL_PEN(m) ((m) & (1 << 6))
-#define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5))
-#define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f)
+#define SYN_MODEL_ROT180(m) ((m) & BIT(23))
+#define SYN_MODEL_PORTRAIT(m) ((m) & BIT(22))
+#define SYN_MODEL_SENSOR(m) (((m) & GENMASK(21, 16)) >> 16)
+#define SYN_MODEL_HARDWARE(m) (((m) & GENMASK(15, 9)) >> 9)
+#define SYN_MODEL_NEWABS(m) ((m) & BIT(7))
+#define SYN_MODEL_PEN(m) ((m) & BIT(6))
+#define SYN_MODEL_SIMPLIC(m) ((m) & BIT(5))
+#define SYN_MODEL_GEOMETRY(m) ((m) & GENMASK(3, 0))
/* synaptics capability bits */
-#define SYN_CAP_EXTENDED(c) ((c) & (1 << 23))
-#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18))
-#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7))
-#define SYN_CAP_SLEEP(c) ((c) & (1 << 4))
-#define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3))
-#define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1))
-#define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0))
-#define SYN_CAP_SUBMODEL_ID(c) (((c) & 0x00ff00) >> 8)
-#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
-#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
-#define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16)
-#define SYN_MEXT_CAP_BIT(m) ((m) & (1 << 1))
+#define SYN_CAP_EXTENDED(c) ((c) & BIT(23))
+#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & BIT(18))
+#define SYN_CAP_PASS_THROUGH(c) ((c) & BIT(7))
+#define SYN_CAP_SLEEP(c) ((c) & BIT(4))
+#define SYN_CAP_FOUR_BUTTON(c) ((c) & BIT(3))
+#define SYN_CAP_MULTIFINGER(c) ((c) & BIT(1))
+#define SYN_CAP_PALMDETECT(c) ((c) & BIT(0))
+#define SYN_CAP_SUBMODEL_ID(c) (((c) & GENMASK(15, 8)) >> 8)
+#define SYN_EXT_CAP_REQUESTS(c) (((c) & GENMASK(22, 20)) >> 20)
+#define SYN_CAP_MB_MASK GENMASK(15, 12)
+#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & SYN_CAP_MB_MASK) >> 12)
+#define SYN_CAP_PRODUCT_ID(ec) (((ec) & GENMASK(23, 16)) >> 16)
+#define SYN_MEXT_CAP_BIT(m) ((m) & BIT(1))
/*
* The following describes response for the 0x0c query.
@@ -83,13 +84,14 @@
* hinged at the top.
* 2 0x20 report min query 0x0f gives min coord reported
*/
-#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
-#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
-#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
-#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & 0x002000)
-#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
-#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
-#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
+#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & BIT(20)) /* 1-button ClickPad */
+#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & BIT(8)) /* 2-button ClickPad */
+#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & BIT(17))
+#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & BIT(13))
+#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & BIT(19))
+#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & BIT(10))
+#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & BIT(11))
+#define SYN_CAP_INTERTOUCH(ex0c) ((ex0c) & BIT(14))
/*
* The following descibes response for the 0x10 query.
@@ -108,42 +110,44 @@
* 3 0xff SecurePad height the height of the SecurePad fingerprint
* reader.
*/
-#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000)
-#define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000)
+#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & BIT(16))
+#define SYN_CAP_SECUREPAD(ex10) ((ex10) & BIT(17))
-#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01))
-#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02))
-#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04))
+#define SYN_EXT_BUTTON_STICK_L(eb) (((eb) & BIT(0)) >> 0)
+#define SYN_EXT_BUTTON_STICK_M(eb) (((eb) & BIT(1)) >> 1)
+#define SYN_EXT_BUTTON_STICK_R(eb) (((eb) & BIT(2)) >> 2)
/* synaptics modes query bits */
-#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
-#define SYN_MODE_RATE(m) ((m) & (1 << 6))
-#define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3))
-#define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2))
-#define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1))
-#define SYN_MODE_WMODE(m) ((m) & (1 << 0))
+#define SYN_MODE_ABSOLUTE(m) ((m) & BIT(7))
+#define SYN_MODE_RATE(m) ((m) & BIT(6))
+#define SYN_MODE_BAUD_SLEEP(m) ((m) & BIT(3))
+#define SYN_MODE_DISABLE_GESTURE(m) ((m) & BIT(2))
+#define SYN_MODE_PACKSIZE(m) ((m) & BIT(1))
+#define SYN_MODE_WMODE(m) ((m) & BIT(0))
/* synaptics identify query bits */
-#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f)
-#define SYN_ID_MAJOR(i) ((i) & 0x0f)
-#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff)
+#define SYN_ID_MODEL(i) (((i) & GENMASK(7, 4)) >> 4)
+#define SYN_ID_MAJOR(i) (((i) & GENMASK(3, 0)) >> 0)
+#define SYN_ID_MINOR(i) (((i) & GENMASK(23, 16)) >> 16)
#define SYN_ID_FULL(i) ((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i))
-#define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47)
+#define SYN_ID_IS_SYNAPTICS(i) (((i) & GENMASK(15, 8)) == 0x004700U)
#define SYN_ID_DISGEST_SUPPORTED(i) (SYN_ID_MAJOR(i) >= 4)
/* synaptics special commands */
#define SYN_PS_SET_MODE2 0x14
#define SYN_PS_CLIENT_CMD 0x28
-/* synaptics packet types */
-#define SYN_NEWABS 0
-#define SYN_NEWABS_STRICT 1
-#define SYN_NEWABS_RELAXED 2
-#define SYN_OLDABS 3
-
/* amount to fuzz position data when touchpad reports reduced filtering */
#define SYN_REDUCED_FILTER_FUZZ 8
+/* synaptics packet types */
+enum synaptics_pkt_type {
+ SYN_NEWABS,
+ SYN_NEWABS_STRICT,
+ SYN_NEWABS_RELAXED,
+ SYN_OLDABS,
+};
+
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
@@ -157,26 +161,30 @@ struct synaptics_hw_state {
unsigned int middle:1;
unsigned int up:1;
unsigned int down:1;
- unsigned char ext_buttons;
- signed char scroll;
+ u8 ext_buttons;
+ s8 scroll;
+};
+
+/* Data read from the touchpad */
+struct synaptics_device_info {
+ u32 model_id; /* Model-ID */
+ u32 firmware_id; /* Firmware-ID */
+ u32 board_id; /* Board-ID */
+ u32 capabilities; /* Capabilities */
+ u32 ext_cap; /* Extended Capabilities */
+ u32 ext_cap_0c; /* Ext Caps from 0x0c query */
+ u32 ext_cap_10; /* Ext Caps from 0x10 query */
+ u32 identity; /* Identification */
+ u32 x_res, y_res; /* X/Y resolution in units/mm */
+ u32 x_max, y_max; /* Max coordinates (from FW) */
+ u32 x_min, y_min; /* Min coordinates (from FW) */
};
struct synaptics_data {
- /* Data read from the touchpad */
- unsigned long int model_id; /* Model-ID */
- unsigned long int firmware_id; /* Firmware-ID */
- unsigned long int board_id; /* Board-ID */
- unsigned long int capabilities; /* Capabilities */
- unsigned long int ext_cap; /* Extended Capabilities */
- unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */
- unsigned long int ext_cap_10; /* Ext Caps from 0x10 query */
- unsigned long int identity; /* Identification */
- unsigned int x_res, y_res; /* X/Y resolution in units/mm */
- unsigned int x_max, y_max; /* Max coordinates (from FW) */
- unsigned int x_min, y_min; /* Min coordinates (from FW) */
-
- unsigned char pkt_type; /* packet type - old, new, etc */
- unsigned char mode; /* current mode byte */
+ struct synaptics_device_info info;
+
+ enum synaptics_pkt_type pkt_type; /* packet type - old, new, etc */
+ u8 mode; /* current mode byte */
int scroll;
bool absolute_mode; /* run in Absolute mode */
@@ -200,8 +208,10 @@ struct synaptics_data {
void synaptics_module_init(void);
int synaptics_detect(struct psmouse *psmouse, bool set_properties);
-int synaptics_init(struct psmouse *psmouse);
+int synaptics_init_absolute(struct psmouse *psmouse);
int synaptics_init_relative(struct psmouse *psmouse);
+int synaptics_init_smbus(struct psmouse *psmouse);
+int synaptics_init(struct psmouse *psmouse);
void synaptics_reset(struct psmouse *psmouse);
#endif /* _SYNAPTICS_H */
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index cb2bf203f4ca..8538318d332c 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -652,9 +652,18 @@ static const struct i2c_device_id synaptics_i2c_id_table[] = {
};
MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
+#ifdef CONFIG_OF
+static const struct of_device_id synaptics_i2c_of_match[] = {
+ { .compatible = "synaptics,synaptics_i2c", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, synaptics_i2c_of_match);
+#endif
+
static struct i2c_driver synaptics_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(synaptics_i2c_of_match),
.pm = &synaptics_i2c_pm,
},
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index b604564dec5c..0e0ff84088fd 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -812,8 +812,6 @@ static void mousedev_cleanup(struct mousedev *mousedev)
mousedev_mark_dead(mousedev);
mousedev_hangup(mousedev);
- cdev_del(&mousedev->cdev);
-
/* mousedev is marked dead so no one else accesses mousedev->open */
if (mousedev->open)
input_close_device(handle);
@@ -901,12 +899,8 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
}
cdev_init(&mousedev->cdev, &mousedev_fops);
- mousedev->cdev.kobj.parent = &mousedev->dev.kobj;
- error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1);
- if (error)
- goto err_unregister_handle;
- error = device_add(&mousedev->dev);
+ error = cdev_device_add(&mousedev->cdev, &mousedev->dev);
if (error)
goto err_cleanup_mousedev;
@@ -914,7 +908,6 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
err_cleanup_mousedev:
mousedev_cleanup(mousedev);
- err_unregister_handle:
if (!mixdev)
input_unregister_handle(&mousedev->handle);
err_free_mousedev:
@@ -927,7 +920,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
static void mousedev_destroy(struct mousedev *mousedev)
{
- device_del(&mousedev->dev);
+ cdev_device_del(&mousedev->cdev, &mousedev->dev);
mousedev_cleanup(mousedev);
input_free_minor(MINOR(mousedev->dev.devt));
if (mousedev != mousedev_mix)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index d64fc92858f2..4f2bb5947a4e 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -251,7 +251,7 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
ret = devm_request_threaded_irq(&rmi_dev->dev, pdata->irq, NULL,
rmi_irq_fn, irq_flags | IRQF_ONESHOT,
- dev_name(rmi_dev->xport->dev),
+ dev_driver_string(rmi_dev->xport->dev),
rmi_dev);
if (ret < 0) {
dev_err(&rmi_dev->dev, "Failed to register interrupt %d\n",
@@ -1234,16 +1234,21 @@ static int rmi_driver_probe(struct device *dev)
if (retval < 0)
goto err_destroy_functions;
- if (data->f01_container->dev.driver)
+ if (data->f01_container->dev.driver) {
/* Driver already bound, so enable ATTN now. */
- return rmi_enable_sensor(rmi_dev);
+ retval = rmi_enable_sensor(rmi_dev);
+ if (retval)
+ goto err_disable_irq;
+ }
return 0;
+err_disable_irq:
+ rmi_disable_irq(rmi_dev, false);
err_destroy_functions:
rmi_free_function_list(rmi_dev);
err:
- return retval < 0 ? retval : 0;
+ return retval;
}
static struct rmi_driver rmi_physical_driver = {
diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c
index 07aff4356fe0..8b0db086d68a 100644
--- a/drivers/input/rmi4/rmi_f12.c
+++ b/drivers/input/rmi4/rmi_f12.c
@@ -113,20 +113,16 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
}
if (rmi_register_desc_has_subpacket(item, 2)) {
- sensor->axis_align.clip_x_low = buf[offset];
- sensor->axis_align.clip_x_high = sensor->max_x
- - buf[offset + 1];
- sensor->axis_align.clip_y_low = buf[offset + 2];
- sensor->axis_align.clip_y_high = sensor->max_y
- - buf[offset + 3];
+ /* Units 1/128 sensor pitch */
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev,
+ "%s: Inactive Border xlo:%d xhi:%d ylo:%d yhi:%d\n",
+ __func__,
+ buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+
offset += 4;
}
- rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x low: %d x high: %d y low: %d y high: %d\n",
- __func__,
- sensor->axis_align.clip_x_low, sensor->axis_align.clip_x_high,
- sensor->axis_align.clip_y_low, sensor->axis_align.clip_y_high);
-
if (rmi_register_desc_has_subpacket(item, 3)) {
rx_receivers = buf[offset];
tx_receivers = buf[offset + 1];
diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
index 425fe140e9df..b8ee78e0d61f 100644
--- a/drivers/input/rmi4/rmi_f34.c
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -105,16 +105,27 @@ static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits)
{
struct f34_data *f34 = dev_get_drvdata(&fn->dev);
int ret;
+ u8 status;
- if (f34->bl_version != 5)
- return 0;
+ if (f34->bl_version == 5) {
+ ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address,
+ &status);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n",
+ __func__, status, ret);
- ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address, &f34->v5.status);
- rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n",
- __func__, f34->v5.status, ret);
-
- if (!ret && !(f34->v5.status & 0x7f))
- complete(&f34->v5.cmd_done);
+ if (!ret && !(status & 0x7f))
+ complete(&f34->v5.cmd_done);
+ } else {
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ f34->fn->fd.data_base_addr +
+ f34->v7.off.flash_status,
+ &status, sizeof(status));
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n",
+ __func__, status, ret);
+
+ if (!ret && !(status & 0x1f))
+ complete(&f34->v7.cmd_done);
+ }
return 0;
}
diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h
index 43a91349b28d..32c4e9581c68 100644
--- a/drivers/input/rmi4/rmi_f34.h
+++ b/drivers/input/rmi4/rmi_f34.h
@@ -30,6 +30,7 @@
#define F34_IDLE_WAIT_MS 500
#define F34_ENABLE_WAIT_MS 300
#define F34_ERASE_WAIT_MS 5000
+#define F34_WRITE_WAIT_MS 3000
#define F34_BOOTLOADER_ID_LEN 2
@@ -47,11 +48,6 @@
#define CONFIG_ID_SIZE 32
#define PRODUCT_ID_SIZE 10
-#define ENABLE_WAIT_MS (1 * 1000)
-#define WRITE_WAIT_MS (3 * 1000)
-
-#define MIN_SLEEP_TIME_US 50
-#define MAX_SLEEP_TIME_US 100
#define HAS_BSR BIT(5)
#define HAS_CONFIG_ID BIT(3)
@@ -292,6 +288,7 @@ struct f34v7_data {
const void *config_data;
const void *image;
+ struct completion cmd_done;
};
struct f34_data {
diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c
index 56c6c39ad31e..10c0d11b72c9 100644
--- a/drivers/input/rmi4/rmi_f34v7.c
+++ b/drivers/input/rmi4/rmi_f34v7.c
@@ -15,6 +15,7 @@
#include <asm/unaligned.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/jiffies.h>
#include "rmi_driver.h"
#include "rmi_f34.h"
@@ -31,7 +32,7 @@ static int rmi_f34v7_read_flash_status(struct f34_data *f34)
sizeof(status));
if (ret < 0) {
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
- "%s: Failed to read flash status\n", __func__);
+ "%s: Error %d reading flash status\n", __func__, ret);
return ret;
}
@@ -60,28 +61,17 @@ static int rmi_f34v7_read_flash_status(struct f34_data *f34)
static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms)
{
- int count = 0;
- int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
+ unsigned long timeout;
- do {
- usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
-
- count++;
-
- rmi_f34v7_read_flash_status(f34);
-
- if ((f34->v7.command == v7_CMD_IDLE)
- && (f34->v7.flash_status == 0x00)) {
- rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
- "Idle status detected\n");
- return 0;
- }
- } while (count < timeout_count);
+ timeout = msecs_to_jiffies(timeout_ms);
- dev_err(&f34->fn->dev,
- "%s: Timed out waiting for idle status\n", __func__);
+ if (!wait_for_completion_timeout(&f34->v7.cmd_done, timeout)) {
+ dev_warn(&f34->fn->dev, "%s: Timed out waiting for idle status\n",
+ __func__);
+ return -ETIMEDOUT;
+ }
- return -ETIMEDOUT;
+ return 0;
}
static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
@@ -285,9 +275,10 @@ static int rmi_f34v7_write_partition_id(struct f34_data *f34, u8 cmd)
return 0;
}
-static int rmi_f34v7_read_f34v7_partition_table(struct f34_data *f34)
+static int rmi_f34v7_read_partition_table(struct f34_data *f34)
{
int ret;
+ unsigned long timeout;
u8 base;
__le16 length;
u16 block_number = 0;
@@ -320,6 +311,8 @@ static int rmi_f34v7_read_f34v7_partition_table(struct f34_data *f34)
return ret;
}
+ init_completion(&f34->v7.cmd_done);
+
ret = rmi_f34v7_write_command(f34, v7_CMD_READ_CONFIG);
if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to write command\n",
@@ -327,11 +320,15 @@ static int rmi_f34v7_read_f34v7_partition_table(struct f34_data *f34)
return ret;
}
- ret = rmi_f34v7_wait_for_idle(f34, WRITE_WAIT_MS);
- if (ret < 0) {
- dev_err(&f34->fn->dev, "%s: Failed to wait for idle status\n",
- __func__);
- return ret;
+ timeout = msecs_to_jiffies(F34_WRITE_WAIT_MS);
+ while (time_before(jiffies, timeout)) {
+ usleep_range(5000, 6000);
+ rmi_f34v7_read_flash_status(f34);
+
+ if (f34->v7.command == v7_CMD_IDLE &&
+ f34->v7.flash_status == 0x00) {
+ break;
+ }
}
ret = rmi_read_block(f34->fn->rmi_dev,
@@ -570,7 +567,7 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
f34->v7.read_config_buf_size = f34->v7.partition_table_bytes;
ptable = f34->v7.read_config_buf;
- ret = rmi_f34v7_read_f34v7_partition_table(f34);
+ ret = rmi_f34v7_read_partition_table(f34);
if (ret < 0) {
dev_err(&f34->fn->dev, "%s: Failed to read partition table\n",
__func__);
@@ -666,6 +663,8 @@ static int rmi_f34v7_erase_config(struct f34_data *f34)
dev_info(&f34->fn->dev, "Erasing config...\n");
+ init_completion(&f34->v7.cmd_done);
+
switch (f34->v7.config_area) {
case v7_UI_CONFIG_AREA:
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_CONFIG);
@@ -684,11 +683,11 @@ static int rmi_f34v7_erase_config(struct f34_data *f34)
break;
}
- ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
if (ret < 0)
return ret;
- return ret;
+ return 0;
}
static int rmi_f34v7_erase_guest_code(struct f34_data *f34)
@@ -697,11 +696,13 @@ static int rmi_f34v7_erase_guest_code(struct f34_data *f34)
dev_info(&f34->fn->dev, "Erasing guest code...\n");
+ init_completion(&f34->v7.cmd_done);
+
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_GUEST_CODE);
if (ret < 0)
return ret;
- ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
if (ret < 0)
return ret;
@@ -714,11 +715,13 @@ static int rmi_f34v7_erase_all(struct f34_data *f34)
dev_info(&f34->fn->dev, "Erasing firmware...\n");
+ init_completion(&f34->v7.cmd_done);
+
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_FIRMWARE);
if (ret < 0)
return ret;
- ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
if (ret < 0)
return ret;
@@ -743,8 +746,8 @@ static int rmi_f34v7_erase_all(struct f34_data *f34)
return 0;
}
-static int rmi_f34v7_read_f34v7_blocks(struct f34_data *f34, u16 block_cnt,
- u8 command)
+static int rmi_f34v7_read_blocks(struct f34_data *f34,
+ u16 block_cnt, u8 command)
{
int ret;
u8 base;
@@ -787,17 +790,15 @@ static int rmi_f34v7_read_f34v7_blocks(struct f34_data *f34, u16 block_cnt,
return ret;
}
+ init_completion(&f34->v7.cmd_done);
+
ret = rmi_f34v7_write_command(f34, command);
if (ret < 0)
return ret;
- ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
- if (ret < 0) {
- dev_err(&f34->fn->dev,
- "%s: Wait for idle failed (%d blks remaining)\n",
- __func__, remaining);
+ ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS);
+ if (ret < 0)
return ret;
- }
ret = rmi_read_block(f34->fn->rmi_dev,
base + f34->v7.off.payload,
@@ -853,6 +854,8 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
transfer = min(remaining, max_transfer);
put_unaligned_le16(transfer, &length);
+ init_completion(&f34->v7.cmd_done);
+
ret = rmi_write_block(f34->fn->rmi_dev,
base + f34->v7.off.transfer_length,
&length, sizeof(length));
@@ -877,13 +880,9 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
return ret;
}
- ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
- if (ret < 0) {
- dev_err(&f34->fn->dev,
- "%s: Failed wait for idle (%d blks remaining)\n",
- __func__, remaining);
+ ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS);
+ if (ret < 0)
return ret;
- }
block_ptr += (transfer * f34->v7.block_size);
remaining -= transfer;
@@ -945,6 +944,8 @@ static int rmi_f34v7_write_flash_config(struct f34_data *f34)
return -EINVAL;
}
+ init_completion(&f34->v7.cmd_done);
+
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_FLASH_CONFIG);
if (ret < 0)
return ret;
@@ -952,7 +953,7 @@ static int rmi_f34v7_write_flash_config(struct f34_data *f34)
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
"%s: Erase flash config command written\n", __func__);
- ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ ret = rmi_f34v7_wait_for_idle(f34, F34_WRITE_WAIT_MS);
if (ret < 0)
return ret;
@@ -981,7 +982,7 @@ static int rmi_f34v7_write_partition_table(struct f34_data *f34)
f34->v7.read_config_buf_size = f34->v7.config_size;
- ret = rmi_f34v7_read_f34v7_blocks(f34, block_count, v7_CMD_READ_CONFIG);
+ ret = rmi_f34v7_read_blocks(f34, block_count, v7_CMD_READ_CONFIG);
if (ret < 0)
return ret;
@@ -1287,6 +1288,8 @@ static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
{
int ret;
+ f34->fn->rmi_dev->driver->set_irq_bits(f34->fn->rmi_dev, f34->fn->irq_mask);
+
ret = rmi_f34v7_read_flash_status(f34);
if (ret < 0)
return ret;
@@ -1294,19 +1297,16 @@ static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
if (f34->v7.in_bl_mode)
return 0;
+ init_completion(&f34->v7.cmd_done);
+
ret = rmi_f34v7_write_command(f34, v7_CMD_ENABLE_FLASH_PROG);
if (ret < 0)
return ret;
- ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS);
if (ret < 0)
return ret;
- if (!f34->v7.in_bl_mode) {
- dev_err(&f34->fn->dev, "%s: BL mode not entered\n", __func__);
- return -EINVAL;
- }
-
return 0;
}
@@ -1314,6 +1314,8 @@ int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw)
{
int ret = 0;
+ f34->fn->rmi_dev->driver->set_irq_bits(f34->fn->rmi_dev, f34->fn->irq_mask);
+
f34->v7.config_area = v7_UI_CONFIG_AREA;
f34->v7.image = fw->data;
@@ -1376,8 +1378,13 @@ int rmi_f34v7_probe(struct f34_data *f34)
memset(&f34->v7.blkcount, 0x00, sizeof(f34->v7.blkcount));
memset(&f34->v7.phyaddr, 0x00, sizeof(f34->v7.phyaddr));
- rmi_f34v7_read_queries(f34);
- f34->v7.force_update = false;
+ init_completion(&f34->v7.cmd_done);
+
+ ret = rmi_f34v7_read_queries(f34);
+ if (ret < 0)
+ return ret;
+
+ f34->v7.force_update = true;
return 0;
}
diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c
index 082306d7c207..e28663ef9e5a 100644
--- a/drivers/input/rmi4/rmi_i2c.c
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -204,7 +204,7 @@ static int rmi_i2c_probe(struct i2c_client *client,
struct rmi_device_platform_data *client_pdata =
dev_get_platdata(&client->dev);
struct rmi_i2c_xport *rmi_i2c;
- int retval;
+ int error;
rmi_i2c = devm_kzalloc(&client->dev, sizeof(struct rmi_i2c_xport),
GFP_KERNEL);
@@ -220,30 +220,31 @@ static int rmi_i2c_probe(struct i2c_client *client,
rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n",
dev_name(&client->dev));
+
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev,
- "adapter does not support required functionality.\n");
+ "adapter does not support required functionality\n");
return -ENODEV;
}
rmi_i2c->supplies[0].supply = "vdd";
rmi_i2c->supplies[1].supply = "vio";
- retval = devm_regulator_bulk_get(&client->dev,
+ error = devm_regulator_bulk_get(&client->dev,
ARRAY_SIZE(rmi_i2c->supplies),
rmi_i2c->supplies);
- if (retval < 0)
- return retval;
+ if (error < 0)
+ return error;
- retval = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
+ error = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
rmi_i2c->supplies);
- if (retval < 0)
- return retval;
+ if (error < 0)
+ return error;
- retval = devm_add_action_or_reset(&client->dev,
+ error = devm_add_action_or_reset(&client->dev,
rmi_i2c_regulator_bulk_disable,
rmi_i2c);
- if (retval)
- return retval;
+ if (error)
+ return error;
of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms",
&rmi_i2c->startup_delay);
@@ -263,26 +264,26 @@ static int rmi_i2c_probe(struct i2c_client *client,
* Setting the page to zero will (a) make sure the PSR is in a
* known state, and (b) make sure we can talk to the device.
*/
- retval = rmi_set_page(rmi_i2c, 0);
- if (retval) {
- dev_err(&client->dev, "Failed to set page select to 0.\n");
- return retval;
+ error = rmi_set_page(rmi_i2c, 0);
+ if (error) {
+ dev_err(&client->dev, "Failed to set page select to 0\n");
+ return error;
}
- retval = rmi_register_transport_device(&rmi_i2c->xport);
- if (retval) {
- dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n",
- client->addr);
- return retval;
+ dev_info(&client->dev, "registering I2C-connected sensor\n");
+
+ error = rmi_register_transport_device(&rmi_i2c->xport);
+ if (error) {
+ dev_err(&client->dev, "failed to register sensor: %d\n", error);
+ return error;
}
- retval = devm_add_action_or_reset(&client->dev,
+
+ error = devm_add_action_or_reset(&client->dev,
rmi_i2c_unregister_transport,
rmi_i2c);
- if (retval)
- return retval;
+ if (error)
+ return error;
- dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n",
- client->addr);
return 0;
}
diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c
index 76752555d809..225025a0940c 100644
--- a/drivers/input/rmi4/rmi_smbus.c
+++ b/drivers/input/rmi4/rmi_smbus.c
@@ -53,6 +53,7 @@ static int rmi_smb_get_version(struct rmi_smb_xport *rmi_smb)
dev_err(&client->dev, "failed to get SMBus version number!\n");
return retval;
}
+
return retval + 1;
}
@@ -83,63 +84,56 @@ static int rmi_smb_get_command_code(struct rmi_transport_dev *xport,
{
struct rmi_smb_xport *rmi_smb =
container_of(xport, struct rmi_smb_xport, xport);
+ struct mapping_table_entry new_map;
int i;
- int retval;
- struct mapping_table_entry mapping_data[1];
+ int retval = 0;
mutex_lock(&rmi_smb->mappingtable_mutex);
+
for (i = 0; i < RMI_SMB2_MAP_SIZE; i++) {
- if (rmi_smb->mapping_table[i].rmiaddr == rmiaddr) {
+ struct mapping_table_entry *entry = &rmi_smb->mapping_table[i];
+
+ if (le16_to_cpu(entry->rmiaddr) == rmiaddr) {
if (isread) {
- if (rmi_smb->mapping_table[i].readcount
- == bytecount) {
- *commandcode = i;
- retval = 0;
+ if (entry->readcount == bytecount)
goto exit;
- }
} else {
- if (rmi_smb->mapping_table[i].flags &
- RMI_SMB2_MAP_FLAGS_WE) {
- *commandcode = i;
- retval = 0;
+ if (entry->flags & RMI_SMB2_MAP_FLAGS_WE) {
goto exit;
}
}
}
}
+
i = rmi_smb->table_index;
rmi_smb->table_index = (i + 1) % RMI_SMB2_MAP_SIZE;
/* constructs mapping table data entry. 4 bytes each entry */
- memset(mapping_data, 0, sizeof(mapping_data));
-
- mapping_data[0].rmiaddr = cpu_to_le16(rmiaddr);
- mapping_data[0].readcount = bytecount;
- mapping_data[0].flags = !isread ? RMI_SMB2_MAP_FLAGS_WE : 0;
-
- retval = smb_block_write(xport, i + 0x80, mapping_data,
- sizeof(mapping_data));
+ memset(&new_map, 0, sizeof(new_map));
+ new_map.rmiaddr = cpu_to_le16(rmiaddr);
+ new_map.readcount = bytecount;
+ new_map.flags = !isread ? RMI_SMB2_MAP_FLAGS_WE : 0;
+ retval = smb_block_write(xport, i + 0x80, &new_map, sizeof(new_map));
if (retval < 0) {
/*
* if not written to device mapping table
* clear the driver mapping table records
*/
- rmi_smb->mapping_table[i].rmiaddr = 0x0000;
- rmi_smb->mapping_table[i].readcount = 0;
- rmi_smb->mapping_table[i].flags = 0;
- goto exit;
+ memset(&new_map, 0, sizeof(new_map));
}
+
/* save to the driver level mapping table */
- rmi_smb->mapping_table[i].rmiaddr = rmiaddr;
- rmi_smb->mapping_table[i].readcount = bytecount;
- rmi_smb->mapping_table[i].flags = !isread ? RMI_SMB2_MAP_FLAGS_WE : 0;
- *commandcode = i;
+ rmi_smb->mapping_table[i] = new_map;
exit:
mutex_unlock(&rmi_smb->mappingtable_mutex);
- return retval;
+ if (retval < 0)
+ return retval;
+
+ *commandcode = i;
+ return 0;
}
static int rmi_smb_write_block(struct rmi_transport_dev *xport, u16 rmiaddr,
@@ -282,19 +276,24 @@ static int rmi_smb_probe(struct i2c_client *client,
{
struct rmi_device_platform_data *pdata = dev_get_platdata(&client->dev);
struct rmi_smb_xport *rmi_smb;
- int retval;
int smbus_version;
+ int error;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data, aborting\n");
+ return -ENOMEM;
+ }
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_HOST_NOTIFY)) {
dev_err(&client->dev,
- "adapter does not support required functionality.\n");
+ "adapter does not support required functionality\n");
return -ENODEV;
}
if (client->irq <= 0) {
- dev_err(&client->dev, "no IRQ provided, giving up.\n");
+ dev_err(&client->dev, "no IRQ provided, giving up\n");
return client->irq ? client->irq : -ENODEV;
}
@@ -303,12 +302,7 @@ static int rmi_smb_probe(struct i2c_client *client,
if (!rmi_smb)
return -ENOMEM;
- if (!pdata) {
- dev_err(&client->dev, "no platform data, aborting\n");
- return -ENOMEM;
- }
-
- rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n",
+ rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s\n",
dev_name(&client->dev));
rmi_smb->client = client;
@@ -321,34 +315,30 @@ static int rmi_smb_probe(struct i2c_client *client,
rmi_smb->xport.proto_name = "smb2";
rmi_smb->xport.ops = &rmi_smb_ops;
- retval = rmi_smb_get_version(rmi_smb);
- if (retval < 0)
- return retval;
+ smbus_version = rmi_smb_get_version(rmi_smb);
+ if (smbus_version < 0)
+ return smbus_version;
- smbus_version = retval;
rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Smbus version is %d",
smbus_version);
if (smbus_version != 2) {
- dev_err(&client->dev, "Unrecognized SMB version %d.\n",
+ dev_err(&client->dev, "Unrecognized SMB version %d\n",
smbus_version);
return -ENODEV;
}
i2c_set_clientdata(client, rmi_smb);
- retval = rmi_register_transport_device(&rmi_smb->xport);
- if (retval) {
- dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n",
- client->addr);
- i2c_set_clientdata(client, NULL);
- return retval;
+ dev_info(&client->dev, "registering SMbus-connected sensor\n");
+
+ error = rmi_register_transport_device(&rmi_smb->xport);
+ if (error) {
+ dev_err(&client->dev, "failed to register sensor: %d\n", error);
+ return error;
}
- dev_info(&client->dev, "registered rmi smb driver at %#04x.\n",
- client->addr);
return 0;
-
}
static int rmi_smb_remove(struct i2c_client *client)
diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c
index 69548d7d1f10..d97a85907ed6 100644
--- a/drivers/input/rmi4/rmi_spi.c
+++ b/drivers/input/rmi4/rmi_spi.c
@@ -370,7 +370,7 @@ static int rmi_spi_probe(struct spi_device *spi)
struct rmi_spi_xport *rmi_spi;
struct rmi_device_platform_data *pdata;
struct rmi_device_platform_data *spi_pdata = spi->dev.platform_data;
- int retval;
+ int error;
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX)
return -EINVAL;
@@ -383,9 +383,9 @@ static int rmi_spi_probe(struct spi_device *spi)
pdata = &rmi_spi->xport.pdata;
if (spi->dev.of_node) {
- retval = rmi_spi_of_probe(spi, pdata);
- if (retval)
- return retval;
+ error = rmi_spi_of_probe(spi, pdata);
+ if (error)
+ return error;
} else if (spi_pdata) {
*pdata = *spi_pdata;
}
@@ -396,10 +396,10 @@ static int rmi_spi_probe(struct spi_device *spi)
if (pdata->spi_data.mode)
spi->mode = pdata->spi_data.mode;
- retval = spi_setup(spi);
- if (retval < 0) {
+ error = spi_setup(spi);
+ if (error < 0) {
dev_err(&spi->dev, "spi_setup failed!\n");
- return retval;
+ return error;
}
pdata->irq = spi->irq;
@@ -413,32 +413,34 @@ static int rmi_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, rmi_spi);
- retval = rmi_spi_manage_pools(rmi_spi, RMI_SPI_DEFAULT_XFER_BUF_SIZE);
- if (retval)
- return retval;
+ error = rmi_spi_manage_pools(rmi_spi, RMI_SPI_DEFAULT_XFER_BUF_SIZE);
+ if (error)
+ return error;
/*
* Setting the page to zero will (a) make sure the PSR is in a
* known state, and (b) make sure we can talk to the device.
*/
- retval = rmi_set_page(rmi_spi, 0);
- if (retval) {
+ error = rmi_set_page(rmi_spi, 0);
+ if (error) {
dev_err(&spi->dev, "Failed to set page select to 0.\n");
- return retval;
+ return error;
}
- retval = rmi_register_transport_device(&rmi_spi->xport);
- if (retval) {
- dev_err(&spi->dev, "failed to register transport.\n");
- return retval;
+ dev_info(&spi->dev, "registering SPI-connected sensor\n");
+
+ error = rmi_register_transport_device(&rmi_spi->xport);
+ if (error) {
+ dev_err(&spi->dev, "failed to register sensor: %d\n", error);
+ return error;
}
- retval = devm_add_action_or_reset(&spi->dev,
+
+ error = devm_add_action_or_reset(&spi->dev,
rmi_spi_unregister_transport,
rmi_spi);
- if (retval)
- return retval;
+ if (error)
+ return error;
- dev_info(&spi->dev, "registered RMI SPI driver\n");
return 0;
}
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 312bd6ca9198..09720d950686 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -620,6 +620,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "20046"),
},
},
+ {
+ /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"),
+ },
+ },
{ }
};
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index 1ca7f551e2da..30d6230d48f7 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -285,8 +285,8 @@ static int serio_queue_event(void *object, struct module *owner,
}
if (!try_module_get(owner)) {
- pr_warning("Can't get module reference, dropping event %d\n",
- event_type);
+ pr_warn("Can't get module reference, dropping event %d\n",
+ event_type);
kfree(event);
retval = -EINVAL;
goto out;
@@ -823,8 +823,8 @@ static void serio_attach_driver(struct serio_driver *drv)
error = driver_attach(&drv->driver);
if (error)
- pr_warning("driver_attach() failed for %s with error %d\n",
- drv->driver.name, error);
+ pr_warn("driver_attach() failed for %s with error %d\n",
+ drv->driver.name, error);
}
int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
@@ -953,12 +953,24 @@ static int serio_suspend(struct device *dev)
static int serio_resume(struct device *dev)
{
struct serio *serio = to_serio_port(dev);
+ int error = -ENOENT;
- /*
- * Driver reconnect can take a while, so better let kseriod
- * deal with it.
- */
- serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
+ mutex_lock(&serio->drv_mutex);
+ if (serio->drv && serio->drv->fast_reconnect) {
+ error = serio->drv->fast_reconnect(serio);
+ if (error && error != -ENOENT)
+ dev_warn(dev, "fast reconnect failed with error %d\n",
+ error);
+ }
+ mutex_unlock(&serio->drv_mutex);
+
+ if (error) {
+ /*
+ * Driver reconnect can take a while, so better let
+ * kseriod deal with it.
+ */
+ serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
+ }
return 0;
}
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
index e7409c45bdd0..12a3ad83296d 100644
--- a/drivers/input/sparse-keymap.c
+++ b/drivers/input/sparse-keymap.c
@@ -160,12 +160,12 @@ static int sparse_keymap_setkeycode(struct input_dev *dev,
* @keymap: Keymap in form of array of &key_entry structures ending
* with %KE_END type entry
* @setup: Function that can be used to adjust keymap entries
- * depending on device's deeds, may be %NULL
+ * depending on device's needs, may be %NULL
*
* The function calculates size and allocates copy of the original
* keymap after which sets up input device event bits appropriately.
- * Before destroying input device allocated keymap should be freed
- * with a call to sparse_keymap_free().
+ * The allocated copy of the keymap is automatically freed when it
+ * is no longer needed.
*/
int sparse_keymap_setup(struct input_dev *dev,
const struct key_entry *keymap,
@@ -180,19 +180,18 @@ int sparse_keymap_setup(struct input_dev *dev,
for (e = keymap; e->type != KE_END; e++)
map_size++;
- map = kcalloc(map_size, sizeof(struct key_entry), GFP_KERNEL);
+ map = devm_kmemdup(&dev->dev, keymap, map_size * sizeof(*map),
+ GFP_KERNEL);
if (!map)
return -ENOMEM;
- memcpy(map, keymap, map_size * sizeof(struct key_entry));
-
for (i = 0; i < map_size; i++) {
entry = &map[i];
if (setup) {
error = setup(dev, entry);
if (error)
- goto err_out;
+ return error;
}
switch (entry->type) {
@@ -221,10 +220,6 @@ int sparse_keymap_setup(struct input_dev *dev,
dev->setkeycode = sparse_keymap_setkeycode;
return 0;
-
- err_out:
- kfree(map);
- return error;
}
EXPORT_SYMBOL(sparse_keymap_setup);
@@ -232,29 +227,13 @@ EXPORT_SYMBOL(sparse_keymap_setup);
* sparse_keymap_free - free memory allocated for sparse keymap
* @dev: Input device using sparse keymap
*
- * This function is used to free memory allocated by sparse keymap
+ * This function used to free memory allocated by sparse keymap
* in an input device that was set up by sparse_keymap_setup().
- * NOTE: It is safe to cal this function while input device is
- * still registered (however the drivers should care not to try to
- * use freed keymap and thus have to shut off interrupts/polling
- * before freeing the keymap).
+ * Since sparse_keymap_setup() now uses a managed allocation for the
+ * keymap copy, use of this function is deprecated.
*/
void sparse_keymap_free(struct input_dev *dev)
{
- unsigned long flags;
-
- /*
- * Take event lock to prevent racing with input_get_keycode()
- * and input_set_keycode() if we are called while input device
- * is still registered.
- */
- spin_lock_irqsave(&dev->event_lock, flags);
-
- kfree(dev->keycode);
- dev->keycode = NULL;
- dev->keycodemax = 0;
-
- spin_unlock_irqrestore(&dev->event_lock, flags);
}
EXPORT_SYMBOL(sparse_keymap_free);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 033599777651..cf26ca49ae6d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -73,6 +73,7 @@ config TOUCHSCREEN_AD7879
config TOUCHSCREEN_AD7879_I2C
tristate "support I2C bus connection"
depends on TOUCHSCREEN_AD7879 && I2C
+ select REGMAP_I2C
help
Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus.
@@ -82,6 +83,7 @@ config TOUCHSCREEN_AD7879_I2C
config TOUCHSCREEN_AD7879_SPI
tristate "support SPI bus connection"
depends on TOUCHSCREEN_AD7879 && SPI_MASTER
+ select REGMAP_SPI
help
Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
@@ -91,11 +93,11 @@ config TOUCHSCREEN_AD7879_SPI
module will be called ad7879-spi.
config TOUCHSCREEN_AR1021_I2C
- tristate "Microchip AR1021 i2c touchscreen"
+ tristate "Microchip AR1020/1021 i2c touchscreen"
depends on I2C && OF
help
- Say Y here if you have the Microchip AR1021 touchscreen controller
- chip in your system.
+ Say Y here if you have the Microchip AR1020 or AR1021 touchscreen
+ controller chip in your system.
If unsure, say N.
@@ -829,6 +831,16 @@ config TOUCHSCREEN_USB_COMPOSITE
To compile this driver as a module, choose M here: the
module will be called usbtouchscreen.
+config TOUCHSCREEN_MXS_LRADC
+ tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
+ depends on MFD_MXS_LRADC
+ help
+ Say Y here if you have a touchscreen connected to the low-resolution
+ analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
+
+ To compile this driver as a module, choose M here: the module will be
+ called mxs-lradc-ts.
+
config TOUCHSCREEN_MX25
tristate "Freescale i.MX25 touchscreen input driver"
depends on MFD_MX25_TSADC
@@ -1023,6 +1035,16 @@ config TOUCHSCREEN_TSC2007
To compile this driver as a module, choose M here: the
module will be called tsc2007.
+config TOUCHSCREEN_TSC2007_IIO
+ bool "IIO interface for external ADC input and temperature"
+ depends on TOUCHSCREEN_TSC2007
+ depends on IIO=y || IIO=TOUCHSCREEN_TSC2007
+ help
+ Saying Y here adds an iio interface to the tsc2007 which
+ provides values for the AUX input (used for e.g. battery
+ or ambient light monitoring), temperature and raw input
+ values.
+
config TOUCHSCREEN_W90X900
tristate "W90P910 touchscreen driver"
depends on ARCH_W90X900
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index b622e5344137..18e476948e44 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.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_MXS_LRADC) += mxs-lradc-ts.o
obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
@@ -79,6 +80,8 @@ obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
+tsc2007-y := tsc2007_core.o
+tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO) += tsc2007_iio.o
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
index 58f72e0246ab..49b902b10c5f 100644
--- a/drivers/input/touchscreen/ad7879-i2c.c
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -12,53 +12,22 @@
#include <linux/types.h>
#include <linux/of.h>
#include <linux/pm.h>
+#include <linux/regmap.h>
#include "ad7879.h"
#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
-/* All registers are word-sized.
- * AD7879 uses a high-byte first convention.
- */
-static int ad7879_i2c_read(struct device *dev, u8 reg)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int ad7879_i2c_multi_read(struct device *dev,
- u8 first_reg, u8 count, u16 *buf)
-{
- struct i2c_client *client = to_i2c_client(dev);
- u8 idx;
-
- i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf);
-
- for (idx = 0; idx < count; ++idx)
- buf[idx] = swab16(buf[idx]);
-
- return 0;
-}
-
-static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- return i2c_smbus_write_word_swapped(client, reg, val);
-}
-
-static const struct ad7879_bus_ops ad7879_i2c_bus_ops = {
- .bustype = BUS_I2C,
- .read = ad7879_i2c_read,
- .multi_read = ad7879_i2c_multi_read,
- .write = ad7879_i2c_write,
+static const struct regmap_config ad7879_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = 15,
};
static int ad7879_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct ad7879 *ts;
+ struct regmap *regmap;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
@@ -66,23 +35,12 @@ static int ad7879_i2c_probe(struct i2c_client *client,
return -EIO;
}
- ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq,
- &ad7879_i2c_bus_ops);
- if (IS_ERR(ts))
- return PTR_ERR(ts);
-
- i2c_set_clientdata(client, ts);
-
- return 0;
-}
-
-static int ad7879_i2c_remove(struct i2c_client *client)
-{
- struct ad7879 *ts = i2c_get_clientdata(client);
-
- ad7879_remove(ts);
+ regmap = devm_regmap_init_i2c(client, &ad7879_i2c_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
- return 0;
+ return ad7879_probe(&client->dev, regmap, client->irq,
+ BUS_I2C, AD7879_DEVID);
}
static const struct i2c_device_id ad7879_id[] = {
@@ -107,12 +65,11 @@ static struct i2c_driver ad7879_i2c_driver = {
.of_match_table = of_match_ptr(ad7879_i2c_dt_ids),
},
.probe = ad7879_i2c_probe,
- .remove = ad7879_i2c_remove,
.id_table = ad7879_id,
};
module_i2c_driver(ad7879_i2c_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
index d42b6b9af191..3457a5626d75 100644
--- a/drivers/input/touchscreen/ad7879-spi.c
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -11,110 +11,28 @@
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/regmap.h>
#include "ad7879.h"
#define AD7879_DEVID 0x7A /* AD7879/AD7889 */
#define MAX_SPI_FREQ_HZ 5000000
-#define AD7879_CMD_MAGIC 0xE000
-#define AD7879_CMD_READ (1 << 10)
-#define AD7879_CMD(reg) (AD7879_CMD_MAGIC | ((reg) & 0xF))
-#define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
-#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ)
-/*
- * ad7879_read/write are only used for initial setup and for sysfs controls.
- * The main traffic is done in ad7879_collect().
- */
-
-static int ad7879_spi_xfer(struct spi_device *spi,
- u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf)
-{
- struct spi_message msg;
- struct spi_transfer *xfers;
- void *spi_data;
- u16 *command;
- u16 *_rx_buf = _rx_buf; /* shut gcc up */
- u8 idx;
- int ret;
-
- xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL);
- if (!spi_data)
- return -ENOMEM;
-
- spi_message_init(&msg);
-
- command = spi_data;
- command[0] = cmd;
- if (count == 1) {
- /* ad7879_spi_{read,write} gave us buf on stack */
- command[1] = *tx_buf;
- tx_buf = &command[1];
- _rx_buf = rx_buf;
- rx_buf = &command[2];
- }
-
- ++xfers;
- xfers[0].tx_buf = command;
- xfers[0].len = 2;
- spi_message_add_tail(&xfers[0], &msg);
- ++xfers;
-
- for (idx = 0; idx < count; ++idx) {
- if (rx_buf)
- xfers[idx].rx_buf = &rx_buf[idx];
- if (tx_buf)
- xfers[idx].tx_buf = &tx_buf[idx];
- xfers[idx].len = 2;
- spi_message_add_tail(&xfers[idx], &msg);
- }
-
- ret = spi_sync(spi, &msg);
-
- if (count == 1)
- _rx_buf[0] = command[2];
-
- kfree(spi_data);
-
- return ret;
-}
-
-static int ad7879_spi_multi_read(struct device *dev,
- u8 first_reg, u8 count, u16 *buf)
-{
- struct spi_device *spi = to_spi_device(dev);
+#define AD7879_CMD_MAGIC 0xE0
+#define AD7879_CMD_READ BIT(2)
- return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf);
-}
-
-static int ad7879_spi_read(struct device *dev, u8 reg)
-{
- struct spi_device *spi = to_spi_device(dev);
- u16 ret, dummy;
-
- return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret;
-}
-
-static int ad7879_spi_write(struct device *dev, u8 reg, u16 val)
-{
- struct spi_device *spi = to_spi_device(dev);
- u16 dummy;
-
- return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy);
-}
-
-static const struct ad7879_bus_ops ad7879_spi_bus_ops = {
- .bustype = BUS_SPI,
- .read = ad7879_spi_read,
- .multi_read = ad7879_spi_multi_read,
- .write = ad7879_spi_write,
+static const struct regmap_config ad7879_spi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .max_register = 15,
+ .read_flag_mask = AD7879_CMD_MAGIC | AD7879_CMD_READ,
+ .write_flag_mask = AD7879_CMD_MAGIC,
};
static int ad7879_spi_probe(struct spi_device *spi)
{
- struct ad7879 *ts;
- int err;
+ struct regmap *regmap;
/* don't exceed max specified SPI CLK frequency */
if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
@@ -122,29 +40,11 @@ static int ad7879_spi_probe(struct spi_device *spi)
return -EINVAL;
}
- spi->bits_per_word = 16;
- err = spi_setup(spi);
- if (err) {
- dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n");
- return err;
- }
-
- ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops);
- if (IS_ERR(ts))
- return PTR_ERR(ts);
-
- spi_set_drvdata(spi, ts);
-
- return 0;
-}
-
-static int ad7879_spi_remove(struct spi_device *spi)
-{
- struct ad7879 *ts = spi_get_drvdata(spi);
-
- ad7879_remove(ts);
+ regmap = devm_regmap_init_spi(spi, &ad7879_spi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
- return 0;
+ return ad7879_probe(&spi->dev, regmap, spi->irq, BUS_SPI, AD7879_DEVID);
}
#ifdef CONFIG_OF
@@ -162,12 +62,11 @@ static struct spi_driver ad7879_spi_driver = {
.of_match_table = of_match_ptr(ad7879_spi_dt_ids),
},
.probe = ad7879_spi_probe,
- .remove = ad7879_spi_remove,
};
module_spi_driver(ad7879_spi_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:ad7879");
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index e16a44667da7..196028c45210 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -26,9 +26,9 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/input/touchscreen.h>
@@ -106,8 +106,7 @@ enum {
#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
struct ad7879 {
- const struct ad7879_bus_ops *bops;
-
+ struct regmap *regmap;
struct device *dev;
struct input_dev *input;
struct timer_list timer;
@@ -137,17 +136,32 @@ struct ad7879 {
static int ad7879_read(struct ad7879 *ts, u8 reg)
{
- return ts->bops->read(ts->dev, reg);
-}
+ unsigned int val;
+ int error;
-static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf)
-{
- return ts->bops->multi_read(ts->dev, first_reg, count, buf);
+ error = regmap_read(ts->regmap, reg, &val);
+ if (error) {
+ dev_err(ts->dev, "failed to read register %#02x: %d\n",
+ reg, error);
+ return error;
+ }
+
+ return val;
}
static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val)
{
- return ts->bops->write(ts->dev, reg, val);
+ int error;
+
+ error = regmap_write(ts->regmap, reg, val);
+ if (error) {
+ dev_err(ts->dev,
+ "failed to write %#04x to register %#02x: %d\n",
+ val, reg, error);
+ return error;
+ }
+
+ return 0;
}
static int ad7879_report(struct ad7879 *ts)
@@ -234,7 +248,8 @@ static irqreturn_t ad7879_irq(int irq, void *handle)
{
struct ad7879 *ts = handle;
- ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data);
+ regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
+ ts->conversion_data, AD7879_NR_SENSE);
if (!ad7879_report(ts))
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
@@ -440,23 +455,34 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip,
static int ad7879_gpio_add(struct ad7879 *ts,
const struct ad7879_platform_data *pdata)
{
+ bool gpio_export;
+ int gpio_base;
int ret = 0;
+ if (pdata) {
+ gpio_export = pdata->gpio_export;
+ gpio_base = pdata->gpio_base;
+ } else {
+ gpio_export = device_property_read_bool(ts->dev,
+ "gpio-controller");
+ gpio_base = -1;
+ }
+
mutex_init(&ts->mutex);
- if (pdata->gpio_export) {
+ if (gpio_export) {
ts->gc.direction_input = ad7879_gpio_direction_input;
ts->gc.direction_output = ad7879_gpio_direction_output;
ts->gc.get = ad7879_gpio_get_value;
ts->gc.set = ad7879_gpio_set_value;
ts->gc.can_sleep = 1;
- ts->gc.base = pdata->gpio_base;
+ ts->gc.base = gpio_base;
ts->gc.ngpio = 1;
ts->gc.label = "AD7879-GPIO";
ts->gc.owner = THIS_MODULE;
ts->gc.parent = ts->dev;
- ret = gpiochip_add_data(&ts->gc, ts);
+ ret = devm_gpiochip_add_data(ts->dev, &ts->gc, ts);
if (ret)
dev_err(ts->dev, "failed to register gpio %d\n",
ts->gc.base);
@@ -464,25 +490,12 @@ static int ad7879_gpio_add(struct ad7879 *ts,
return ret;
}
-
-static void ad7879_gpio_remove(struct ad7879 *ts)
-{
- const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
-
- if (pdata && pdata->gpio_export)
- gpiochip_remove(&ts->gc);
-
-}
#else
-static inline int ad7879_gpio_add(struct ad7879 *ts,
- const struct ad7879_platform_data *pdata)
+static int ad7879_gpio_add(struct ad7879 *ts,
+ const struct ad7879_platform_data *pdata)
{
return 0;
}
-
-static inline void ad7879_gpio_remove(struct ad7879 *ts)
-{
-}
#endif
static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts)
@@ -511,8 +524,15 @@ static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts)
return 0;
}
-struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
- const struct ad7879_bus_ops *bops)
+static void ad7879_cleanup_sysfs(void *_ts)
+{
+ struct ad7879 *ts = _ts;
+
+ sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
+}
+
+int ad7879_probe(struct device *dev, struct regmap *regmap,
+ int irq, u16 bustype, u8 devid)
{
struct ad7879_platform_data *pdata = dev_get_platdata(dev);
struct ad7879 *ts;
@@ -520,14 +540,14 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
int err;
u16 revid;
- if (!irq) {
+ if (irq <= 0) {
dev_err(dev, "No IRQ specified\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
if (pdata) {
/* Platform data use swapped axis (backward compatibility) */
@@ -540,23 +560,22 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
ts->averaging = pdata->averaging;
ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
ts->median = pdata->median;
- } else if (dev->of_node) {
- ad7879_parse_dt(dev, ts);
} else {
- dev_err(dev, "No platform data\n");
- return ERR_PTR(-EINVAL);
+ err = ad7879_parse_dt(dev, ts);
+ if (err)
+ return err;
}
input_dev = devm_input_allocate_device(dev);
if (!input_dev) {
dev_err(dev, "Failed to allocate input device\n");
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
- ts->bops = bops;
ts->dev = dev;
ts->input = input_dev;
ts->irq = irq;
+ ts->regmap = regmap;
setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
@@ -564,20 +583,14 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
input_dev->name = "AD7879 Touchscreen";
input_dev->phys = ts->phys;
input_dev->dev.parent = dev;
- input_dev->id.bustype = bops->bustype;
+ input_dev->id.bustype = bustype;
input_dev->open = ad7879_open;
input_dev->close = ad7879_close;
input_set_drvdata(input_dev, ts);
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(ABS_X, input_dev->absbit);
- __set_bit(ABS_Y, input_dev->absbit);
- __set_bit(ABS_PRESSURE, input_dev->absbit);
-
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
if (pdata) {
input_set_abs_params(input_dev, ABS_X,
@@ -595,17 +608,18 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
} else {
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+ input_set_capability(input_dev, EV_ABS, ABS_PRESSURE);
touchscreen_parse_properties(input_dev, false, NULL);
if (!input_abs_get_max(input_dev, ABS_PRESSURE)) {
dev_err(dev, "Touchscreen pressure is not specified\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
}
err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
if (err < 0) {
dev_err(dev, "Failed to write %s\n", input_dev->name);
- return ERR_PTR(err);
+ return err;
}
revid = ad7879_read(ts, AD7879_REG_REVID);
@@ -614,7 +628,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
if (input_dev->id.product != devid) {
dev_err(dev, "Failed to probe %s (%x vs %x)\n",
input_dev->name, devid, revid);
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
}
ts->cmd_crtl3 = AD7879_YPLUS_BIT |
@@ -639,43 +653,33 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
dev_name(dev), ts);
if (err) {
dev_err(dev, "Failed to request IRQ: %d\n", err);
- return ERR_PTR(err);
+ return err;
}
__ad7879_disable(ts);
err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
if (err)
- goto err_out;
+ return err;
- if (pdata) {
- err = ad7879_gpio_add(ts, pdata);
- if (err)
- goto err_remove_attr;
- }
+ err = devm_add_action_or_reset(dev, ad7879_cleanup_sysfs, ts);
+ if (err)
+ return err;
+
+ err = ad7879_gpio_add(ts, pdata);
+ if (err)
+ return err;
err = input_register_device(input_dev);
if (err)
- goto err_remove_gpio;
+ return err;
- return ts;
+ dev_set_drvdata(dev, ts);
-err_remove_gpio:
- ad7879_gpio_remove(ts);
-err_remove_attr:
- sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
-err_out:
- return ERR_PTR(err);
+ return 0;
}
EXPORT_SYMBOL(ad7879_probe);
-void ad7879_remove(struct ad7879 *ts)
-{
- ad7879_gpio_remove(ts);
- sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
-}
-EXPORT_SYMBOL(ad7879_remove);
-
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h
index 6fd13c48d373..7e43066a4b68 100644
--- a/drivers/input/touchscreen/ad7879.h
+++ b/drivers/input/touchscreen/ad7879.h
@@ -11,20 +11,12 @@
#include <linux/types.h>
-struct ad7879;
struct device;
-
-struct ad7879_bus_ops {
- u16 bustype;
- int (*read)(struct device *dev, u8 reg);
- int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf);
- int (*write)(struct device *dev, u8 reg, u16 val);
-};
+struct regmap;
extern const struct dev_pm_ops ad7879_pm_ops;
-struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
- const struct ad7879_bus_ops *bops);
-void ad7879_remove(struct ad7879 *);
+int ad7879_probe(struct device *dev, struct regmap *regmap,
+ int irq, u16 bustype, u8 devid);
#endif
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index f5793e3d945f..735a0be1ad95 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -871,7 +871,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
msecs_to_jiffies(TS_POLL_PERIOD));
}
- if (ts->pendown) {
+ if (ts->pendown && !ts->stopped) {
struct input_dev *input = ts->input;
input_report_key(input, BTN_TOUCH, 0);
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
index 6562b17117f7..f9dcbd63e598 100644
--- a/drivers/input/touchscreen/ar1021_i2c.c
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -1,11 +1,12 @@
/*
- * Microchip AR1021 driver for I2C
+ * Microchip AR1020 and AR1021 driver for I2C
*
* Author: Christian Gmeiner <christian.gmeiner@gmail.com>
*
* License: GPLv2 as published by the FSF.
*/
+#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/of.h>
@@ -18,6 +19,10 @@
#define AR1021_MAX_X 4095
#define AR1021_MAX_Y 4095
+#define AR1021_CMD 0x55
+
+#define AR1021_CMD_ENABLE_TOUCH 0x12
+
struct ar1021_i2c {
struct i2c_client *client;
struct input_dev *input;
@@ -33,12 +38,12 @@ static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
int retval;
retval = i2c_master_recv(ar1021->client,
- ar1021->data, sizeof(ar1021->data));
+ ar1021->data, sizeof(ar1021->data));
if (retval != sizeof(ar1021->data))
goto out;
/* sync bit set ? */
- if ((data[0] & 0x80) == 0)
+ if (!(data[0] & BIT(7)))
goto out;
button = data[0] & BIT(0);
@@ -56,8 +61,19 @@ out:
static int ar1021_i2c_open(struct input_dev *dev)
{
+ static const u8 cmd_enable_touch[] = {
+ AR1021_CMD,
+ 0x01, /* number of bytes after this */
+ AR1021_CMD_ENABLE_TOUCH
+ };
struct ar1021_i2c *ar1021 = input_get_drvdata(dev);
struct i2c_client *client = ar1021->client;
+ int error;
+
+ error = i2c_master_send(ar1021->client, cmd_enable_touch,
+ sizeof(cmd_enable_touch));
+ if (error < 0)
+ return error;
enable_irq(client->irq);
@@ -73,7 +89,7 @@ static void ar1021_i2c_close(struct input_dev *dev)
}
static int ar1021_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct ar1021_i2c *ar1021;
struct input_dev *input;
@@ -109,7 +125,7 @@ static int ar1021_i2c_probe(struct i2c_client *client,
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, ar1021_i2c_irq,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ IRQF_ONESHOT,
"ar1021_i2c", ar1021);
if (error) {
dev_err(&client->dev,
@@ -151,7 +167,7 @@ static int __maybe_unused ar1021_i2c_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
static const struct i2c_device_id ar1021_i2c_id[] = {
- { "MICROCHIP_AR1021_I2C", 0 },
+ { "ar1021", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
@@ -175,5 +191,5 @@ static struct i2c_driver ar1021_i2c_driver = {
module_i2c_driver(ar1021_i2c_driver);
MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
-MODULE_DESCRIPTION("Microchip AR1021 I2C Driver");
+MODULE_DESCRIPTION("Microchip AR1020 and AR1021 I2C Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 16023867b9da..2facad75eb6d 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -31,9 +31,9 @@
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/timer.h>
-#include <linux/gpio.h>
-#include <linux/input/eeti_ts.h>
+#include <linux/gpio/consumer.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
static bool flip_x;
module_param(flip_x, bool, 0644);
@@ -43,54 +43,31 @@ static bool flip_y;
module_param(flip_y, bool, 0644);
MODULE_PARM_DESC(flip_y, "flip y coordinate");
-struct eeti_ts_priv {
+struct eeti_ts {
struct i2c_client *client;
struct input_dev *input;
- struct work_struct work;
- struct mutex mutex;
- int irq_gpio, irq, irq_active_high;
+ struct gpio_desc *attn_gpio;
+ bool running;
};
#define EETI_TS_BITDEPTH (11)
#define EETI_MAXVAL ((1 << (EETI_TS_BITDEPTH + 1)) - 1)
-#define REPORT_BIT_PRESSED (1 << 0)
-#define REPORT_BIT_AD0 (1 << 1)
-#define REPORT_BIT_AD1 (1 << 2)
-#define REPORT_BIT_HAS_PRESSURE (1 << 6)
+#define REPORT_BIT_PRESSED BIT(0)
+#define REPORT_BIT_AD0 BIT(1)
+#define REPORT_BIT_AD1 BIT(2)
+#define REPORT_BIT_HAS_PRESSURE BIT(6)
#define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH)
-static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)
+static void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf)
{
- return gpio_get_value(priv->irq_gpio) == priv->irq_active_high;
-}
-
-static void eeti_ts_read(struct work_struct *work)
-{
- char buf[6];
- unsigned int x, y, res, pressed, to = 100;
- struct eeti_ts_priv *priv =
- container_of(work, struct eeti_ts_priv, work);
+ unsigned int res;
+ u16 x, y;
- mutex_lock(&priv->mutex);
-
- while (eeti_ts_irq_active(priv) && --to)
- i2c_master_recv(priv->client, buf, sizeof(buf));
-
- if (!to) {
- dev_err(&priv->client->dev,
- "unable to clear IRQ - line stuck?\n");
- goto out;
- }
-
- /* drop non-report packets */
- if (!(buf[0] & 0x80))
- goto out;
-
- pressed = buf[0] & REPORT_BIT_PRESSED;
res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1));
- x = buf[2] | (buf[1] << 8);
- y = buf[4] | (buf[3] << 8);
+
+ x = get_unaligned_be16(&buf[1]);
+ y = get_unaligned_be16(&buf[3]);
/* fix the range to 11 bits */
x >>= res - EETI_TS_BITDEPTH;
@@ -103,65 +80,78 @@ static void eeti_ts_read(struct work_struct *work)
y = EETI_MAXVAL - y;
if (buf[0] & REPORT_BIT_HAS_PRESSURE)
- input_report_abs(priv->input, ABS_PRESSURE, buf[5]);
+ input_report_abs(eeti->input, ABS_PRESSURE, buf[5]);
- input_report_abs(priv->input, ABS_X, x);
- input_report_abs(priv->input, ABS_Y, y);
- input_report_key(priv->input, BTN_TOUCH, !!pressed);
- input_sync(priv->input);
-
-out:
- mutex_unlock(&priv->mutex);
+ input_report_abs(eeti->input, ABS_X, x);
+ input_report_abs(eeti->input, ABS_Y, y);
+ input_report_key(eeti->input, BTN_TOUCH, buf[0] & REPORT_BIT_PRESSED);
+ input_sync(eeti->input);
}
static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
{
- struct eeti_ts_priv *priv = dev_id;
+ struct eeti_ts *eeti = dev_id;
+ int len;
+ int error;
+ char buf[6];
- /* postpone I2C transactions as we are atomic */
- schedule_work(&priv->work);
+ do {
+ len = i2c_master_recv(eeti->client, buf, sizeof(buf));
+ if (len != sizeof(buf)) {
+ error = len < 0 ? len : -EIO;
+ dev_err(&eeti->client->dev,
+ "failed to read touchscreen data: %d\n",
+ error);
+ break;
+ }
+
+ if (buf[0] & 0x80) {
+ /* Motion packet */
+ eeti_ts_report_event(eeti, buf);
+ }
+ } while (eeti->running &&
+ eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio));
return IRQ_HANDLED;
}
-static void eeti_ts_start(struct eeti_ts_priv *priv)
+static void eeti_ts_start(struct eeti_ts *eeti)
{
- enable_irq(priv->irq);
-
- /* Read the events once to arm the IRQ */
- eeti_ts_read(&priv->work);
+ eeti->running = true;
+ wmb();
+ enable_irq(eeti->client->irq);
}
-static void eeti_ts_stop(struct eeti_ts_priv *priv)
+static void eeti_ts_stop(struct eeti_ts *eeti)
{
- disable_irq(priv->irq);
- cancel_work_sync(&priv->work);
+ eeti->running = false;
+ wmb();
+ disable_irq(eeti->client->irq);
}
static int eeti_ts_open(struct input_dev *dev)
{
- struct eeti_ts_priv *priv = input_get_drvdata(dev);
+ struct eeti_ts *eeti = input_get_drvdata(dev);
- eeti_ts_start(priv);
+ eeti_ts_start(eeti);
return 0;
}
static void eeti_ts_close(struct input_dev *dev)
{
- struct eeti_ts_priv *priv = input_get_drvdata(dev);
+ struct eeti_ts *eeti = input_get_drvdata(dev);
- eeti_ts_stop(priv);
+ eeti_ts_stop(eeti);
}
static int eeti_ts_probe(struct i2c_client *client,
- const struct i2c_device_id *idp)
+ const struct i2c_device_id *idp)
{
- struct eeti_ts_platform_data *pdata = dev_get_platdata(&client->dev);
- struct eeti_ts_priv *priv;
+ struct device *dev = &client->dev;
+ struct eeti_ts *eeti;
struct input_dev *input;
- unsigned int irq_flags;
- int err = -ENOMEM;
+ int error;
/*
* In contrast to what's described in the datasheet, there seems
@@ -170,21 +160,19 @@ static int eeti_ts_probe(struct i2c_client *client,
* for interrupts to occur.
*/
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&client->dev, "failed to allocate driver data\n");
+ eeti = devm_kzalloc(dev, sizeof(*eeti), GFP_KERNEL);
+ if (!eeti) {
+ dev_err(dev, "failed to allocate driver data\n");
return -ENOMEM;
}
- mutex_init(&priv->mutex);
- input = input_allocate_device();
+ input = devm_input_allocate_device(dev);
if (!input) {
- dev_err(&client->dev, "Failed to allocate input device.\n");
- goto err1;
+ dev_err(dev, "Failed to allocate input device.\n");
+ return -ENOMEM;
}
- input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_capability(input, EV_KEY, BTN_TOUCH);
input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0);
input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0);
@@ -192,71 +180,38 @@ static int eeti_ts_probe(struct i2c_client *client,
input->name = client->name;
input->id.bustype = BUS_I2C;
- input->dev.parent = &client->dev;
input->open = eeti_ts_open;
input->close = eeti_ts_close;
- priv->client = client;
- priv->input = input;
- priv->irq_gpio = pdata->irq_gpio;
- priv->irq = gpio_to_irq(pdata->irq_gpio);
-
- err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
- if (err < 0)
- goto err1;
-
- priv->irq_active_high = pdata->irq_active_high;
-
- irq_flags = priv->irq_active_high ?
- IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ eeti->client = client;
+ eeti->input = input;
- INIT_WORK(&priv->work, eeti_ts_read);
- i2c_set_clientdata(client, priv);
- input_set_drvdata(input, priv);
+ eeti->attn_gpio = devm_gpiod_get_optional(dev, "attn", GPIOD_IN);
+ if (IS_ERR(eeti->attn_gpio))
+ return PTR_ERR(eeti->attn_gpio);
- err = input_register_device(input);
- if (err)
- goto err2;
+ i2c_set_clientdata(client, eeti);
+ input_set_drvdata(input, eeti);
- err = request_irq(priv->irq, eeti_ts_isr, irq_flags,
- client->name, priv);
- if (err) {
- dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
- goto err3;
+ error = devm_request_threaded_irq(dev, client->irq,
+ NULL, eeti_ts_isr,
+ IRQF_ONESHOT,
+ client->name, eeti);
+ if (error) {
+ dev_err(dev, "Unable to request touchscreen IRQ: %d\n",
+ error);
+ return error;
}
/*
* Disable the device for now. It will be enabled once the
* input device is opened.
*/
- eeti_ts_stop(priv);
-
- return 0;
-
-err3:
- input_unregister_device(input);
- input = NULL; /* so we dont try to free it below */
-err2:
- gpio_free(pdata->irq_gpio);
-err1:
- input_free_device(input);
- kfree(priv);
- return err;
-}
-
-static int eeti_ts_remove(struct i2c_client *client)
-{
- struct eeti_ts_priv *priv = i2c_get_clientdata(client);
-
- free_irq(priv->irq, priv);
- /*
- * eeti_ts_stop() leaves IRQ disabled. We need to re-enable it
- * so that device still works if we reload the driver.
- */
- enable_irq(priv->irq);
+ eeti_ts_stop(eeti);
- input_unregister_device(priv->input);
- kfree(priv);
+ error = input_register_device(input);
+ if (error)
+ return error;
return 0;
}
@@ -264,18 +219,18 @@ static int eeti_ts_remove(struct i2c_client *client)
static int __maybe_unused eeti_ts_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
- struct eeti_ts_priv *priv = i2c_get_clientdata(client);
- struct input_dev *input_dev = priv->input;
+ struct eeti_ts *eeti = i2c_get_clientdata(client);
+ struct input_dev *input_dev = eeti->input;
mutex_lock(&input_dev->mutex);
if (input_dev->users)
- eeti_ts_stop(priv);
+ eeti_ts_stop(eeti);
mutex_unlock(&input_dev->mutex);
if (device_may_wakeup(&client->dev))
- enable_irq_wake(priv->irq);
+ enable_irq_wake(client->irq);
return 0;
}
@@ -283,16 +238,16 @@ static int __maybe_unused eeti_ts_suspend(struct device *dev)
static int __maybe_unused eeti_ts_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
- struct eeti_ts_priv *priv = i2c_get_clientdata(client);
- struct input_dev *input_dev = priv->input;
+ struct eeti_ts *eeti = i2c_get_clientdata(client);
+ struct input_dev *input_dev = eeti->input;
if (device_may_wakeup(&client->dev))
- disable_irq_wake(priv->irq);
+ disable_irq_wake(client->irq);
mutex_lock(&input_dev->mutex);
if (input_dev->users)
- eeti_ts_start(priv);
+ eeti_ts_start(eeti);
mutex_unlock(&input_dev->mutex);
@@ -313,7 +268,6 @@ static struct i2c_driver eeti_ts_driver = {
.pm = &eeti_ts_pm,
},
.probe = eeti_ts_probe,
- .remove = eeti_ts_remove,
.id_table = eeti_ts_id,
};
diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c
index 7098e0a47019..ee82a975bfd2 100644
--- a/drivers/input/touchscreen/imx6ul_tsc.c
+++ b/drivers/input/touchscreen/imx6ul_tsc.c
@@ -337,11 +337,20 @@ static int imx6ul_tsc_open(struct input_dev *input_dev)
dev_err(tsc->dev,
"Could not prepare or enable the tsc clock: %d\n",
err);
- clk_disable_unprepare(tsc->adc_clk);
- return err;
+ goto disable_adc_clk;
}
- return imx6ul_tsc_init(tsc);
+ err = imx6ul_tsc_init(tsc);
+ if (err)
+ goto disable_tsc_clk;
+
+ return 0;
+
+disable_tsc_clk:
+ clk_disable_unprepare(tsc->tsc_clk);
+disable_adc_clk:
+ clk_disable_unprepare(tsc->adc_clk);
+ return err;
}
static void imx6ul_tsc_close(struct input_dev *input_dev)
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c
index e0baa7de4102..4eb31ec10b18 100644
--- a/drivers/input/touchscreen/lpc32xx_ts.c
+++ b/drivers/input/touchscreen/lpc32xx_ts.c
@@ -142,11 +142,14 @@ static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
clk_disable_unprepare(tsc->clk);
}
-static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
+static int lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
{
u32 tmp;
+ int err;
- clk_prepare_enable(tsc->clk);
+ err = clk_prepare_enable(tsc->clk);
+ if (err)
+ return err;
tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
@@ -184,15 +187,15 @@ static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
/* Enable automatic ts event capture */
tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
+
+ return 0;
}
static int lpc32xx_ts_open(struct input_dev *dev)
{
struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
- lpc32xx_setup_tsc(tsc);
-
- return 0;
+ return lpc32xx_setup_tsc(tsc);
}
static void lpc32xx_ts_close(struct input_dev *dev)
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
index a595ae5284e3..72ca3efb5781 100644
--- a/drivers/input/touchscreen/max11801_ts.c
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -224,9 +224,16 @@ static const struct i2c_device_id max11801_ts_id[] = {
};
MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
+static const struct of_device_id max11801_ts_dt_ids[] = {
+ { .compatible = "maxim,max11801" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max11801_ts_dt_ids);
+
static struct i2c_driver max11801_ts_driver = {
.driver = {
.name = "max11801_ts",
+ .of_match_table = max11801_ts_dt_ids,
},
.id_table = max11801_ts_id,
.probe = max11801_ts_probe,
diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c
index 703d7f983d0a..05108c2fea93 100644
--- a/drivers/input/touchscreen/melfas_mip4.c
+++ b/drivers/input/touchscreen/melfas_mip4.c
@@ -253,10 +253,21 @@ static int mip4_get_fw_version(struct mip4_ts *ts)
*/
static int mip4_query_device(struct mip4_ts *ts)
{
+ union i2c_smbus_data dummy;
int error;
u8 cmd[2];
u8 buf[14];
+ /*
+ * Make sure there is something at this address as we do not
+ * consider subsequent failures as fatal.
+ */
+ if (i2c_smbus_xfer(ts->client->adapter, ts->client->addr,
+ 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+ dev_err(&ts->client->dev, "nothing at this address\n");
+ return -ENXIO;
+ }
+
/* Product name */
cmd[0] = MIP4_R0_INFO;
cmd[1] = MIP4_R1_INFO_PRODUCT_NAME;
diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c
new file mode 100644
index 000000000000..58c016cd6809
--- /dev/null
+++ b/drivers/input/touchscreen/mxs-lradc-ts.c
@@ -0,0 +1,714 @@
+/*
+ * Freescale MXS LRADC touchscreen driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
+ *
+ * Authors:
+ * Marek Vasut <marex@denx.de>
+ * Ksenija Stanojevic <ksenija.stanojevic@gmail.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.
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mxs-lradc.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+const char *mxs_lradc_ts_irq_names[] = {
+ "mxs-lradc-touchscreen",
+ "mxs-lradc-channel6",
+ "mxs-lradc-channel7",
+};
+
+/*
+ * Touchscreen handling
+ */
+enum mxs_lradc_ts_plate {
+ LRADC_TOUCH = 0,
+ LRADC_SAMPLE_X,
+ LRADC_SAMPLE_Y,
+ LRADC_SAMPLE_PRESSURE,
+ LRADC_SAMPLE_VALID,
+};
+
+struct mxs_lradc_ts {
+ struct mxs_lradc *lradc;
+ struct device *dev;
+
+ void __iomem *base;
+ /*
+ * When the touchscreen is enabled, we give it two private virtual
+ * channels: #6 and #7. This means that only 6 virtual channels (instead
+ * of 8) will be available for buffered capture.
+ */
+#define TOUCHSCREEN_VCHANNEL1 7
+#define TOUCHSCREEN_VCHANNEL2 6
+
+ struct input_dev *ts_input;
+
+ enum mxs_lradc_ts_plate cur_plate; /* state machine */
+ bool ts_valid;
+ unsigned int ts_x_pos;
+ unsigned int ts_y_pos;
+ unsigned int ts_pressure;
+
+ /* handle touchscreen's physical behaviour */
+ /* samples per coordinate */
+ unsigned int over_sample_cnt;
+ /* time clocks between samples */
+ unsigned int over_sample_delay;
+ /* time in clocks to wait after the plates where switched */
+ unsigned int settling_delay;
+ spinlock_t lock;
+};
+
+struct state_info {
+ u32 mask;
+ u32 bit;
+ u32 x_plate;
+ u32 y_plate;
+ u32 pressure;
+};
+
+static struct state_info info[] = {
+ {LRADC_CTRL0_MX23_PLATE_MASK, LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE,
+ LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM,
+ LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM,
+ LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM},
+ {LRADC_CTRL0_MX28_PLATE_MASK, LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE,
+ LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW,
+ LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW,
+ LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW}
+};
+
+static bool mxs_lradc_check_touch_event(struct mxs_lradc_ts *ts)
+{
+ return !!(readl(ts->base + LRADC_STATUS) &
+ LRADC_STATUS_TOUCH_DETECT_RAW);
+}
+
+static void mxs_lradc_map_ts_channel(struct mxs_lradc_ts *ts, unsigned int vch,
+ unsigned int ch)
+{
+ writel(LRADC_CTRL4_LRADCSELECT_MASK(vch),
+ ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
+ writel(LRADC_CTRL4_LRADCSELECT(vch, ch),
+ ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
+}
+
+static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned int ch)
+{
+ /*
+ * prepare for oversampling conversion
+ *
+ * from the datasheet:
+ * "The ACCUMULATE bit in the appropriate channel register
+ * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+ * otherwise, the IRQs will not fire."
+ */
+ writel(LRADC_CH_ACCUMULATE |
+ LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
+ ts->base + LRADC_CH(ch));
+
+ /* from the datasheet:
+ * "Software must clear this register in preparation for a
+ * multi-cycle accumulation.
+ */
+ writel(LRADC_CH_VALUE_MASK,
+ ts->base + LRADC_CH(ch) + STMP_OFFSET_REG_CLR);
+
+ /*
+ * prepare the delay/loop unit according to the oversampling count
+ *
+ * from the datasheet:
+ * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
+ * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
+ * the LRADC will not trigger the delay group."
+ */
+ writel(LRADC_DELAY_TRIGGER(1 << ch) | LRADC_DELAY_TRIGGER_DELAYS(0) |
+ LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
+ LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
+ ts->base + LRADC_DELAY(3));
+
+ writel(LRADC_CTRL1_LRADC_IRQ(ch),
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ /*
+ * after changing the touchscreen plates setting
+ * the signals need some initial time to settle. Start the
+ * SoC's delay unit and start the conversion later
+ * and automatically.
+ */
+ writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) |
+ LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay),
+ ts->base + LRADC_DELAY(2));
+}
+
+/*
+ * Pressure detection is special:
+ * We want to do both required measurements for the pressure detection in
+ * one turn. Use the hardware features to chain both conversions and let the
+ * hardware report one interrupt if both conversions are done
+ */
+static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts,
+ unsigned int ch1, unsigned int ch2)
+{
+ u32 reg;
+
+ /*
+ * prepare for oversampling conversion
+ *
+ * from the datasheet:
+ * "The ACCUMULATE bit in the appropriate channel register
+ * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+ * otherwise, the IRQs will not fire."
+ */
+ reg = LRADC_CH_ACCUMULATE |
+ LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
+ writel(reg, ts->base + LRADC_CH(ch1));
+ writel(reg, ts->base + LRADC_CH(ch2));
+
+ /* from the datasheet:
+ * "Software must clear this register in preparation for a
+ * multi-cycle accumulation.
+ */
+ writel(LRADC_CH_VALUE_MASK,
+ ts->base + LRADC_CH(ch1) + STMP_OFFSET_REG_CLR);
+ writel(LRADC_CH_VALUE_MASK,
+ ts->base + LRADC_CH(ch2) + STMP_OFFSET_REG_CLR);
+
+ /* prepare the delay/loop unit according to the oversampling count */
+ writel(LRADC_DELAY_TRIGGER(1 << ch1) | LRADC_DELAY_TRIGGER(1 << ch2) |
+ LRADC_DELAY_TRIGGER_DELAYS(0) |
+ LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
+ LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
+ ts->base + LRADC_DELAY(3));
+
+ writel(LRADC_CTRL1_LRADC_IRQ(ch2),
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ /*
+ * after changing the touchscreen plates setting
+ * the signals need some initial time to settle. Start the
+ * SoC's delay unit and start the conversion later
+ * and automatically.
+ */
+ writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) |
+ LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay),
+ ts->base + LRADC_DELAY(2));
+}
+
+static unsigned int mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
+ unsigned int channel)
+{
+ u32 reg;
+ unsigned int num_samples, val;
+
+ reg = readl(ts->base + LRADC_CH(channel));
+ if (reg & LRADC_CH_ACCUMULATE)
+ num_samples = ts->over_sample_cnt;
+ else
+ num_samples = 1;
+
+ val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
+ return val / num_samples;
+}
+
+static unsigned int mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
+ unsigned int ch1, unsigned int ch2)
+{
+ u32 reg, mask;
+ unsigned int pressure, m1, m2;
+
+ mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
+ reg = readl(ts->base + LRADC_CTRL1) & mask;
+
+ while (reg != mask) {
+ reg = readl(ts->base + LRADC_CTRL1) & mask;
+ dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
+ }
+
+ m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
+ m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
+
+ if (m2 == 0) {
+ dev_warn(ts->dev, "Cannot calculate pressure\n");
+ return 1 << (LRADC_RESOLUTION - 1);
+ }
+
+ /* simply scale the value from 0 ... max ADC resolution */
+ pressure = m1;
+ pressure *= (1 << LRADC_RESOLUTION);
+ pressure /= m2;
+
+ dev_dbg(ts->dev, "Pressure = %u\n", pressure);
+ return pressure;
+}
+
+#define TS_CH_XP 2
+#define TS_CH_YP 3
+#define TS_CH_XM 4
+#define TS_CH_YM 5
+
+/*
+ * YP(open)--+-------------+
+ * | |--+
+ * | | |
+ * YM(-)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(weak+) XM(open)
+ *
+ * "weak+" means 200k Ohm VDDIO
+ * (-) means GND
+ */
+static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
+{
+ struct mxs_lradc *lradc = ts->lradc;
+
+ /*
+ * In order to detect a touch event the 'touch detect enable' bit
+ * enables:
+ * - a weak pullup to the X+ connector
+ * - a strong ground at the Y- connector
+ */
+ writel(info[lradc->soc].mask,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+ writel(info[lradc->soc].bit,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+}
+
+/*
+ * YP(meas)--+-------------+
+ * | |--+
+ * | | |
+ * YM(open)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(+) XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
+{
+ struct mxs_lradc *lradc = ts->lradc;
+
+ writel(info[lradc->soc].mask,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+ writel(info[lradc->soc].x_plate,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+
+ ts->cur_plate = LRADC_SAMPLE_X;
+ mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
+ mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
+}
+
+/*
+ * YP(+)--+-------------+
+ * | |--+
+ * | | |
+ * YM(-)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(open) XM(meas)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
+{
+ struct mxs_lradc *lradc = ts->lradc;
+
+ writel(info[lradc->soc].mask,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+ writel(info[lradc->soc].y_plate,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+
+ ts->cur_plate = LRADC_SAMPLE_Y;
+ mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
+ mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
+}
+
+/*
+ * YP(+)--+-------------+
+ * | |--+
+ * | | |
+ * YM(meas)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(meas) XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
+{
+ struct mxs_lradc *lradc = ts->lradc;
+
+ writel(info[lradc->soc].mask,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+ writel(info[lradc->soc].pressure,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+
+ ts->cur_plate = LRADC_SAMPLE_PRESSURE;
+ mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
+ mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
+ mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
+ TOUCHSCREEN_VCHANNEL1);
+}
+
+static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
+{
+ mxs_lradc_setup_touch_detection(ts);
+
+ ts->cur_plate = LRADC_TOUCH;
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ | LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+}
+
+static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
+{
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+ /*
+ * start with the Y-pos, because it uses nearly the same plate
+ * settings like the touch detection
+ */
+ mxs_lradc_prepare_y_pos(ts);
+}
+
+static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
+{
+ input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
+ input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
+ input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
+ input_report_key(ts->ts_input, BTN_TOUCH, 1);
+ input_sync(ts->ts_input);
+}
+
+static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
+{
+ mxs_lradc_setup_touch_detection(ts);
+ ts->cur_plate = LRADC_SAMPLE_VALID;
+ /*
+ * start a dummy conversion to burn time to settle the signals
+ * note: we are not interested in the conversion's value
+ */
+ writel(0, ts->base + LRADC_CH(TOUCHSCREEN_VCHANNEL1));
+ writel(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
+ LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10),
+ ts->base + LRADC_DELAY(2));
+}
+
+/*
+ * in order to avoid false measurements, report only samples where
+ * the surface is still touched after the position measurement
+ */
+static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
+{
+ /* if it is still touched, report the sample */
+ if (valid && mxs_lradc_check_touch_event(ts)) {
+ ts->ts_valid = true;
+ mxs_lradc_report_ts_event(ts);
+ }
+
+ /* if it is even still touched, continue with the next measurement */
+ if (mxs_lradc_check_touch_event(ts)) {
+ mxs_lradc_prepare_y_pos(ts);
+ return;
+ }
+
+ if (ts->ts_valid) {
+ /* signal the release */
+ ts->ts_valid = false;
+ input_report_key(ts->ts_input, BTN_TOUCH, 0);
+ input_sync(ts->ts_input);
+ }
+
+ /* if it is released, wait for the next touch via IRQ */
+ ts->cur_plate = LRADC_TOUCH;
+ writel(0, ts->base + LRADC_DELAY(2));
+ writel(0, ts->base + LRADC_DELAY(3));
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+}
+
+/* touchscreen's state machine */
+static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
+{
+ switch (ts->cur_plate) {
+ case LRADC_TOUCH:
+ if (mxs_lradc_check_touch_event(ts))
+ mxs_lradc_start_touch_event(ts);
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ return;
+
+ case LRADC_SAMPLE_Y:
+ ts->ts_y_pos =
+ mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
+ mxs_lradc_prepare_x_pos(ts);
+ return;
+
+ case LRADC_SAMPLE_X:
+ ts->ts_x_pos =
+ mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
+ mxs_lradc_prepare_pressure(ts);
+ return;
+
+ case LRADC_SAMPLE_PRESSURE:
+ ts->ts_pressure =
+ mxs_lradc_read_ts_pressure(ts,
+ TOUCHSCREEN_VCHANNEL2,
+ TOUCHSCREEN_VCHANNEL1);
+ mxs_lradc_complete_touch_event(ts);
+ return;
+
+ case LRADC_SAMPLE_VALID:
+ mxs_lradc_finish_touch_event(ts, 1);
+ break;
+ }
+}
+
+/* IRQ Handling */
+static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
+{
+ struct mxs_lradc_ts *ts = data;
+ struct mxs_lradc *lradc = ts->lradc;
+ unsigned long reg = readl(ts->base + LRADC_CTRL1);
+ u32 clr_irq = mxs_lradc_irq_mask(lradc);
+ const u32 ts_irq_mask =
+ LRADC_CTRL1_TOUCH_DETECT_IRQ |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
+ unsigned long flags;
+
+ if (!(reg & mxs_lradc_irq_mask(lradc)))
+ return IRQ_NONE;
+
+ if (reg & ts_irq_mask) {
+ spin_lock_irqsave(&ts->lock, flags);
+ mxs_lradc_handle_touch(ts);
+ spin_unlock_irqrestore(&ts->lock, flags);
+ /* Make sure we don't clear the next conversion's interrupt. */
+ clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
+ writel(reg & clr_irq,
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mxs_lradc_ts_open(struct input_dev *dev)
+{
+ struct mxs_lradc_ts *ts = input_get_drvdata(dev);
+
+ /* Enable the touch-detect circuitry. */
+ mxs_lradc_enable_touch_detection(ts);
+
+ return 0;
+}
+
+static void mxs_lradc_ts_stop(struct mxs_lradc_ts *ts)
+{
+ int i;
+ struct mxs_lradc *lradc = ts->lradc;
+
+ /* stop all interrupts from firing */
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2),
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ /* Power-down touchscreen touch-detect circuitry. */
+ writel(info[lradc->soc].mask,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+
+ writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+ ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
+ writel(0, ts->base + LRADC_DELAY(i));
+}
+
+static void mxs_lradc_ts_close(struct input_dev *dev)
+{
+ struct mxs_lradc_ts *ts = input_get_drvdata(dev);
+
+ mxs_lradc_ts_stop(ts);
+}
+
+static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
+{
+ struct mxs_lradc *lradc = ts->lradc;
+
+ /* Configure the touchscreen type */
+ if (lradc->soc == IMX28_LRADC) {
+ writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+
+ if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
+ writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+ ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+ }
+}
+
+static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
+{
+ struct input_dev *input = ts->ts_input;
+ struct device *dev = ts->dev;
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = "mxs-lradc-ts";
+ input->id.bustype = BUS_HOST;
+ input->open = mxs_lradc_ts_open;
+ input->close = mxs_lradc_ts_close;
+
+ __set_bit(INPUT_PROP_DIRECT, input->propbit);
+ input_set_capability(input, EV_KEY, BTN_TOUCH);
+ input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
+ 0, 0);
+
+ ts->ts_input = input;
+ input_set_drvdata(input, ts);
+
+ return input_register_device(input);
+}
+
+static int mxs_lradc_ts_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->parent->of_node;
+ struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
+ struct mxs_lradc_ts *ts;
+ struct resource *iores;
+ int ret, irq, virq, i;
+ u32 ts_wires = 0, adapt;
+
+ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ts);
+
+ ts->lradc = lradc;
+ ts->dev = dev;
+ spin_lock_init(&ts->lock);
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ts->base = devm_ioremap(dev, iores->start, resource_size(iores));
+ if (IS_ERR(ts->base))
+ return PTR_ERR(ts->base);
+
+ ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
+ &ts_wires);
+ if (ret)
+ return ret;
+
+ if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
+ ts->over_sample_cnt = 4;
+ } else {
+ if (adapt >= 1 && adapt <= 32) {
+ ts->over_sample_cnt = adapt;
+ } else {
+ dev_err(ts->dev, "Invalid sample count (%u)\n",
+ adapt);
+ return -EINVAL;
+ }
+ }
+
+ if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
+ ts->over_sample_delay = 2;
+ } else {
+ if (adapt >= 2 && adapt <= LRADC_DELAY_DELAY_MASK + 1) {
+ ts->over_sample_delay = adapt;
+ } else {
+ dev_err(ts->dev, "Invalid sample delay (%u)\n",
+ adapt);
+ return -EINVAL;
+ }
+ }
+
+ if (of_property_read_u32(node, "fsl,settling", &adapt)) {
+ ts->settling_delay = 10;
+ } else {
+ if (adapt >= 1 && adapt <= LRADC_DELAY_DELAY_MASK) {
+ ts->settling_delay = adapt;
+ } else {
+ dev_err(ts->dev, "Invalid settling delay (%u)\n",
+ adapt);
+ return -EINVAL;
+ }
+ }
+
+ ret = stmp_reset_block(ts->base);
+ if (ret)
+ return ret;
+
+ mxs_lradc_ts_hw_init(ts);
+
+ for (i = 0; i < 3; i++) {
+ irq = platform_get_irq_byname(pdev, mxs_lradc_ts_irq_names[i]);
+ if (irq < 0)
+ return irq;
+
+ virq = irq_of_parse_and_map(node, irq);
+
+ mxs_lradc_ts_stop(ts);
+
+ ret = devm_request_irq(dev, virq,
+ mxs_lradc_ts_handle_irq,
+ 0, mxs_lradc_ts_irq_names[i], ts);
+ if (ret)
+ return ret;
+ }
+
+ return mxs_lradc_ts_register(ts);
+}
+
+static struct platform_driver mxs_lradc_ts_driver = {
+ .driver = {
+ .name = "mxs-lradc-ts",
+ },
+ .probe = mxs_lradc_ts_probe,
+};
+module_platform_driver(mxs_lradc_ts_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Freescale MXS LRADC touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxs-lradc-ts");
diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
index 404830a4a366..813dd68a5c82 100644
--- a/drivers/input/touchscreen/silead.c
+++ b/drivers/input/touchscreen/silead.c
@@ -580,12 +580,25 @@ static const struct acpi_device_id silead_ts_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match);
#endif
+#ifdef CONFIG_OF
+static const struct of_device_id silead_ts_of_match[] = {
+ { .compatible = "silead,gsl1680" },
+ { .compatible = "silead,gsl1688" },
+ { .compatible = "silead,gsl3670" },
+ { .compatible = "silead,gsl3675" },
+ { .compatible = "silead,gsl3692" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, silead_ts_of_match);
+#endif
+
static struct i2c_driver silead_ts_driver = {
.probe = silead_ts_probe,
.id_table = silead_ts_id,
.driver = {
.name = SILEAD_TS_NAME,
.acpi_match_table = ACPI_PTR(silead_ts_acpi_match),
+ .of_match_table = of_match_ptr(silead_ts_of_match),
.pm = &silead_ts_pm,
},
};
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index 4c0eecae065c..128e5bd74720 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -369,7 +369,7 @@ static void sur40_poll(struct input_polled_dev *polldev)
* packet ID will usually increase in the middle of a series
* instead of at the end.
*/
- if (packet_id != header->packet_id)
+ if (packet_id != le32_to_cpu(header->packet_id))
dev_dbg(sur40->dev, "packet ID mismatch\n");
packet_blobs = result / sizeof(struct sur40_blob);
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
index a340bfccdfb6..75170a7439b1 100644
--- a/drivers/input/touchscreen/tps6507x-ts.c
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -226,7 +226,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
*/
init_data = tps_board->tps6507x_ts_init_data;
- tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
+ tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL);
if (!tsc) {
dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
return -ENOMEM;
@@ -240,11 +240,10 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
snprintf(tsc->phys, sizeof(tsc->phys),
"%s/input0", dev_name(tsc->dev));
- poll_dev = input_allocate_polled_device();
+ poll_dev = devm_input_allocate_polled_device(&pdev->dev);
if (!poll_dev) {
dev_err(tsc->dev, "Failed to allocate polled input device.\n");
- error = -ENOMEM;
- goto err_free_mem;
+ return -ENOMEM;
}
tsc->poll_dev = poll_dev;
@@ -274,32 +273,11 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
error = tps6507x_adc_standby(tsc);
if (error)
- goto err_free_polled_dev;
+ return error;
error = input_register_polled_device(poll_dev);
if (error)
- goto err_free_polled_dev;
-
- platform_set_drvdata(pdev, tsc);
-
- return 0;
-
-err_free_polled_dev:
- input_free_polled_device(poll_dev);
-err_free_mem:
- kfree(tsc);
- return error;
-}
-
-static int tps6507x_ts_remove(struct platform_device *pdev)
-{
- struct tps6507x_ts *tsc = platform_get_drvdata(pdev);
- struct input_polled_dev *poll_dev = tsc->poll_dev;
-
- input_unregister_polled_device(poll_dev);
- input_free_polled_device(poll_dev);
-
- kfree(tsc);
+ return error;
return 0;
}
@@ -309,7 +287,6 @@ static struct platform_driver tps6507x_ts_driver = {
.name = "tps6507x-ts",
},
.probe = tps6507x_ts_probe,
- .remove = tps6507x_ts_remove,
};
module_platform_driver(tps6507x_ts_driver);
diff --git a/drivers/input/touchscreen/tsc2007.h b/drivers/input/touchscreen/tsc2007.h
new file mode 100644
index 000000000000..30fdf5b04a6b
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2007.h
@@ -0,0 +1,101 @@
+
+/*
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ * Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ * - ads7846.c
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _TSC2007_H
+#define _TSC2007_H
+
+#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
+#define TSC2007_MEASURE_AUX (0x2 << 4)
+#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
+#define TSC2007_ACTIVATE_XN (0x8 << 4)
+#define TSC2007_ACTIVATE_YN (0x9 << 4)
+#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
+#define TSC2007_SETUP (0xb << 4)
+#define TSC2007_MEASURE_X (0xc << 4)
+#define TSC2007_MEASURE_Y (0xd << 4)
+#define TSC2007_MEASURE_Z1 (0xe << 4)
+#define TSC2007_MEASURE_Z2 (0xf << 4)
+
+#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
+#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
+
+#define TSC2007_12BIT (0x0 << 1)
+#define TSC2007_8BIT (0x1 << 1)
+
+#define MAX_12BIT ((1 << 12) - 1)
+
+#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
+
+#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
+#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
+#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
+#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
+#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
+
+struct ts_event {
+ u16 x;
+ u16 y;
+ u16 z1, z2;
+};
+
+struct tsc2007 {
+ struct input_dev *input;
+ char phys[32];
+
+ struct i2c_client *client;
+
+ u16 model;
+ u16 x_plate_ohms;
+ u16 max_rt;
+ unsigned long poll_period; /* in jiffies */
+ int fuzzx;
+ int fuzzy;
+ int fuzzz;
+
+ unsigned int gpio;
+ int irq;
+
+ wait_queue_head_t wait;
+ bool stopped;
+
+ int (*get_pendown_state)(struct device *);
+ void (*clear_penirq)(void);
+
+ struct mutex mlock;
+};
+
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd);
+u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc);
+bool tsc2007_is_pen_down(struct tsc2007 *ts);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2007_IIO)
+/* defined in tsc2007_iio.c */
+int tsc2007_iio_configure(struct tsc2007 *ts);
+#else
+static inline int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+ return 0;
+}
+#endif /* CONFIG_TOUCHSCREEN_TSC2007_IIO */
+
+#endif /* _TSC2007_H */
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007_core.c
index 5d0cd51c6f41..fc7384936011 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007_core.c
@@ -27,70 +27,10 @@
#include <linux/i2c.h>
#include <linux/i2c/tsc2007.h>
#include <linux/of_device.h>
-#include <linux/of.h>
#include <linux/of_gpio.h>
+#include "tsc2007.h"
-#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
-#define TSC2007_MEASURE_AUX (0x2 << 4)
-#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
-#define TSC2007_ACTIVATE_XN (0x8 << 4)
-#define TSC2007_ACTIVATE_YN (0x9 << 4)
-#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
-#define TSC2007_SETUP (0xb << 4)
-#define TSC2007_MEASURE_X (0xc << 4)
-#define TSC2007_MEASURE_Y (0xd << 4)
-#define TSC2007_MEASURE_Z1 (0xe << 4)
-#define TSC2007_MEASURE_Z2 (0xf << 4)
-
-#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
-#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
-
-#define TSC2007_12BIT (0x0 << 1)
-#define TSC2007_8BIT (0x1 << 1)
-
-#define MAX_12BIT ((1 << 12) - 1)
-
-#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
-
-#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
-#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
-#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
-#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
-#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
-
-struct ts_event {
- u16 x;
- u16 y;
- u16 z1, z2;
-};
-
-struct tsc2007 {
- struct input_dev *input;
- char phys[32];
-
- struct i2c_client *client;
-
- u16 model;
- u16 x_plate_ohms;
- u16 max_rt;
- unsigned long poll_period; /* in jiffies */
- int fuzzx;
- int fuzzy;
- int fuzzz;
-
- unsigned gpio;
- int irq;
-
- wait_queue_head_t wait;
- bool stopped;
-
- int (*get_pendown_state)(struct device *);
- void (*clear_penirq)(void);
-};
-
-static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
{
s32 data;
u16 val;
@@ -128,7 +68,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
tsc2007_xfer(tsc, PWRDOWN);
}
-static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
+u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc)
{
u32 rt = 0;
@@ -137,7 +77,7 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
tc->x = 0;
if (likely(tc->x && tc->z1)) {
- /* compute touch pressure resistance using equation #1 */
+ /* compute touch resistance using equation #1 */
rt = tc->z2 - tc->z1;
rt *= tc->x;
rt *= tsc->x_plate_ohms;
@@ -148,7 +88,7 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
return rt;
}
-static bool tsc2007_is_pen_down(struct tsc2007 *ts)
+bool tsc2007_is_pen_down(struct tsc2007 *ts)
{
/*
* NOTE: We can't rely on the pressure to determine the pen down
@@ -180,9 +120,12 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
while (!ts->stopped && tsc2007_is_pen_down(ts)) {
/* pen is down, continue with the measurement */
+
+ mutex_lock(&ts->mlock);
tsc2007_read_values(ts, &tc);
+ mutex_unlock(&ts->mlock);
- rt = tsc2007_calculate_pressure(ts, &tc);
+ rt = tsc2007_calculate_resistance(ts, &tc);
if (!rt && !ts->get_pendown_state) {
/*
@@ -195,9 +138,11 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
if (rt <= ts->max_rt) {
dev_dbg(&ts->client->dev,
- "DOWN point(%4d,%4d), pressure (%4u)\n",
+ "DOWN point(%4d,%4d), resistance (%4u)\n",
tc.x, tc.y, rt);
+ rt = ts->max_rt - rt;
+
input_report_key(input, BTN_TOUCH, 1);
input_report_abs(input, ABS_X, tc.x);
input_report_abs(input, ABS_Y, tc.y);
@@ -375,7 +320,8 @@ static void tsc2007_call_exit_platform_hw(void *data)
static int tsc2007_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
+ const struct tsc2007_platform_data *pdata =
+ dev_get_platdata(&client->dev);
struct tsc2007 *ts;
struct input_dev *input_dev;
int err;
@@ -404,7 +350,9 @@ static int tsc2007_probe(struct i2c_client *client,
ts->client = client;
ts->irq = client->irq;
ts->input = input_dev;
+
init_waitqueue_head(&ts->wait);
+ mutex_init(&ts->mlock);
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
@@ -418,8 +366,7 @@ static int tsc2007_probe(struct i2c_client *client,
input_set_drvdata(input_dev, ts);
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
@@ -455,6 +402,14 @@ static int tsc2007_probe(struct i2c_client *client,
tsc2007_stop(ts);
+ /* power down the chip (TSC2007_SETUP does not ACK on I2C) */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to setup chip: %d\n", err);
+ return err; /* chip does not respond */
+ }
+
err = input_register_device(input_dev);
if (err) {
dev_err(&client->dev,
@@ -462,6 +417,13 @@ static int tsc2007_probe(struct i2c_client *client,
return err;
}
+ err = tsc2007_iio_configure(ts);
+ if (err) {
+ dev_err(&client->dev,
+ "Failed to register with IIO: %d\n", err);
+ return err;
+ }
+
return 0;
}
diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c
new file mode 100644
index 000000000000..27b25a9fce83
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2007_iio.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG
+ * Nikolaus Schaller <hns@goldelico.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include "tsc2007.h"
+
+struct tsc2007_iio {
+ struct tsc2007 *ts;
+};
+
+#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
+{ \
+ .datasheet_name = _name, \
+ .type = _type, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(_chan_info), \
+ .indexed = 1, \
+ .channel = _chan, \
+}
+
+static const struct iio_chan_spec tsc2007_iio_channel[] = {
+ TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
+ TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
+ TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
+};
+
+static int tsc2007_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct tsc2007_iio *iio = iio_priv(indio_dev);
+ struct tsc2007 *tsc = iio->ts;
+ int adc_chan = chan->channel;
+ int ret = 0;
+
+ if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
+ return -EINVAL;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ mutex_lock(&tsc->mlock);
+
+ switch (chan->channel) {
+ case 0:
+ *val = tsc2007_xfer(tsc, READ_X);
+ break;
+ case 1:
+ *val = tsc2007_xfer(tsc, READ_Y);
+ break;
+ case 2:
+ *val = tsc2007_xfer(tsc, READ_Z1);
+ break;
+ case 3:
+ *val = tsc2007_xfer(tsc, READ_Z2);
+ break;
+ case 4:
+ *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
+ break;
+ case 5: {
+ struct ts_event tc;
+
+ tc.x = tsc2007_xfer(tsc, READ_X);
+ tc.z1 = tsc2007_xfer(tsc, READ_Z1);
+ tc.z2 = tsc2007_xfer(tsc, READ_Z2);
+ *val = tsc2007_calculate_resistance(tsc, &tc);
+ break;
+ }
+ case 6:
+ *val = tsc2007_is_pen_down(tsc);
+ break;
+ case 7:
+ *val = tsc2007_xfer(tsc,
+ (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
+ break;
+ case 8:
+ *val = tsc2007_xfer(tsc,
+ (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
+ break;
+ }
+
+ /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+ tsc2007_xfer(tsc, PWRDOWN);
+
+ mutex_unlock(&tsc->mlock);
+
+ ret = IIO_VAL_INT;
+
+ return ret;
+}
+
+static const struct iio_info tsc2007_iio_info = {
+ .read_raw = tsc2007_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+ struct iio_dev *indio_dev;
+ struct tsc2007_iio *iio;
+ int error;
+
+ indio_dev = devm_iio_device_alloc(&ts->client->dev, sizeof(*iio));
+ if (!indio_dev) {
+ dev_err(&ts->client->dev, "iio_device_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ iio = iio_priv(indio_dev);
+ iio->ts = ts;
+
+ indio_dev->name = "tsc2007";
+ indio_dev->dev.parent = &ts->client->dev;
+ indio_dev->info = &tsc2007_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = tsc2007_iio_channel;
+ indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
+
+ error = devm_iio_device_register(&ts->client->dev, indio_dev);
+ if (error) {
+ dev_err(&ts->client->dev,
+ "iio_device_register() failed: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}