From 2526b028584e38e8becb690bbe8b3a74bbfe36b1 Mon Sep 17 00:00:00 2001
From: Danct12 <danct12@disroot.org>
Date: Tue, 14 May 2019 17:33:56 +0700
Subject: [PATCH] input: touchscreen: gslx680_ts_acpi: Add driver from onitake.

---
 drivers/input/touchscreen/Kconfig           |  10 +
 drivers/input/touchscreen/Makefile          |   1 +
 drivers/input/touchscreen/gslx680_ts_acpi.c | 775 ++++++++++++++++++++
 3 files changed, 786 insertions(+)
 create mode 100644 drivers/input/touchscreen/gslx680_ts_acpi.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7a4884ad198b..9cf92795bf87 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1312,4 +1312,14 @@ config TOUCHSCREEN_ROHM_BU21023
 	  To compile this driver as a module, choose M here: the
 	  module will be called bu21023_ts.
 
+config TOUCHSCREEN_GSLX680
+    tristate "Silead GSLX680 Touch Screen Driver"
+    depends on I2C
+    help
+	  Say Y here if you have a touchscreen using Silead GSL1680/GSL3680.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gslx680_ts_acpi.
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index fcc7605fba8d..2b77586bef53 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -110,3 +110,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
 obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW)	+= raspberrypi-ts.o
+obj-$(CONFIG_TOUCHSCREEN_GSLX680)	+= gslx680_ts_acpi.o
diff --git a/drivers/input/touchscreen/gslx680_ts_acpi.c b/drivers/input/touchscreen/gslx680_ts_acpi.c
new file mode 100644
index 000000000000..748cedb2f13b
--- /dev/null
+++ b/drivers/input/touchscreen/gslx680_ts_acpi.c
@@ -0,0 +1,775 @@
+/*
+ * Silead GSL1680/3680 touchscreen driver
+ *
+ * Copyright (c) 2015 Gregor Riepl <onitake@gmail.com>
+ *
+ * This driver is based on gslx680_ts.c and elan_ts.c
+ * Copyright (c) 2012 Shanghai Basewin
+ *   Guan Yuwei<guanyuwei@basewin.com>
+ * Copyright (c) 2013 Joe Burmeister
+ *   Joe Burmeister<joe.a.burmeister@googlemail.com>
+ * Copyright (C) 2014 Elan Microelectronics Corporation.
+ * Scott Liu <scott.liu@emc.com.tw>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/version.h>
+
+/* Device and driver information */
+#define DEVICE_NAME "gslx680"
+#define DRIVER_VERSION "0.2.1"
+
+/* Hardware API constants */
+#define GSL_DATA_REG 0x80
+#define GSL_STATUS_REG 0xe0
+#define GSL_PAGE_REG 0xf0
+#define GSL_TOUCH_STATUS_REG 0xbc
+#define GSL_PAGE_SIZE 128
+/* Why 126? */
+#define GSL_MAX_READ 126
+/* Actually 129: 1 command byte and 128 data bytes
+ * this was 125 originally - using 128 to allow firmware transfers
+ */
+#define GSL_MAX_WRITE 128
+#define GSL_STATUS_FW 0x80
+#define GSL_STATUS_TOUCH 0x00
+
+#define GSL_PWR_GPIO "power"
+
+#define GSL_MAX_CONTACTS 10
+#define GSL_MAX_AXIS 0xfff
+#define GSL_DMAX 0
+#define GSL_JITTER 0
+#define GSL_DEADZONE 0
+
+#define GSL_PACKET_SIZE ( \
+	GSL_MAX_CONTACTS * sizeof(struct gsl_ts_packet_touch) + \
+	sizeof(struct gsl_ts_packet_header) \
+)
+
+#define GSL_FW_NAME_MAXLEN 32
+#define GSL_FW_NAME_DEFAULT "silead_ts.fw"
+#define GSL_FW_VERSION 1
+#define GSL_FW_ID_LIT(a, b, c, d) ( \
+	((a) & 0xff) | \
+	(((b) & 0xff) << 8) | \
+	(((c) & 0xff) << 16) | \
+	(((d) & 0xff) << 24) \
+)
+#define GSL_FW_MAGIC GSL_FW_ID_LIT('G', 'S', 'L', 'X')
+#define GSL_FW_MODEL_LIT(m) GSL_FW_ID_LIT( \
+	(m)/1000%10+'0', \
+	(m)/100%10+'0', \
+	(m)/10%10+'0', \
+	(m)%10+'0' \
+)
+#define GSL_FW_MODEL_1680 GSL_FW_MODEL_LIT(1680)
+
+/* Module Parameters (optional) */
+static char *gsl_fw_name = NULL;
+module_param_named(fw_name, gsl_fw_name, charp, 0);
+
+/* Driver state */
+enum gsl_ts_state {
+	GSL_TS_INIT,
+	GSL_TS_SHUTDOWN,
+	GSL_TS_GREEN,
+};
+
+/* Driver instance data structure */
+struct gsl_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct gpio_desc *gpio;
+	char fw_name[GSL_FW_NAME_MAXLEN];
+	
+	enum gsl_ts_state state;
+
+	bool wake_irq_enabled;
+
+	unsigned int x_max;
+	unsigned int y_max;
+	unsigned int multi_touches;
+	bool x_reversed;
+	bool y_reversed;
+	bool xy_swapped;
+	bool soft_tracking;
+	int jitter;
+	int deadzone;
+};
+
+/* Firmware header */
+struct gsl_ts_fw_header {
+	u32 magic;
+	u32 model;
+	u16 version;
+	u16 touches;
+	u16 width;
+	u16 height;
+	u8 swapped;
+	u8 xflipped;
+	u8 yflipped;
+	u8 tracking;
+	u32 pages;
+} __packed;
+/* Firmware data page */
+struct gsl_ts_fw_page {
+	u16 address;
+	u16 size;
+	u8 data[128];
+} __packed;
+
+/* TODO use get_unaligned_le16 instead of packed structures */
+/* Hardware touch event data header */
+struct gsl_ts_packet_header {
+	u8 num_fingers;
+	u8 reserved;
+	u16 time_stamp; /* little-endian */
+} __packed;
+/* Hardware touch event data per finger */
+struct gsl_ts_packet_touch {
+	u16 y_z; /* little endian, lower 12 bits = y, upper 4 bits = pressure (?) */
+	u16 x_id; /* little endian, lower 12 bits = x, upper 4 bits = id */
+} __packed;
+
+
+static int gsl_ts_init(struct gsl_ts_data *ts, const struct firmware *fw)
+{
+	struct gsl_ts_fw_header *header;
+	u32 magic;
+	u16 version;
+
+	dev_dbg(&ts->client->dev, "%s: initialising driver state\n", __func__);
+
+	ts->wake_irq_enabled = false;
+	ts->state = GSL_TS_INIT;
+
+	if (fw->size < sizeof(struct gsl_ts_fw_header)) {
+		dev_err(&ts->client->dev, "%s: invalid firmware: file too small.\n", __func__);
+		return -EINVAL;
+	}
+
+	header = (struct gsl_ts_fw_header *) fw->data;
+	magic = le32_to_cpu(header->magic);
+	if (magic != GSL_FW_MAGIC) {
+		dev_err(&ts->client->dev, "%s: invalid firmware: invalid magic 0x%08x.\n", __func__, magic);
+		return -EINVAL;
+	}
+
+	version = le16_to_cpu(header->version);
+	if (version != GSL_FW_VERSION) {
+		dev_err(&ts->client->dev, "%s: invalid firmware: unsupported version %d.\n", __func__, version);
+		return -EINVAL;
+	}
+
+	ts->x_max = le16_to_cpu(header->width);
+	ts->y_max = le16_to_cpu(header->height);
+	ts->multi_touches = le16_to_cpu(header->touches);
+	if (ts->x_max == 0 || ts->y_max == 0 || ts->multi_touches == 0) {
+		dev_err(&ts->client->dev, "%s: invalid firmware: panel width, height or number of touch points is zero.\n", __func__);
+		return -EINVAL;
+	}
+	if (ts->x_max > GSL_MAX_AXIS || ts->y_max > GSL_MAX_AXIS || ts->multi_touches > GSL_MAX_CONTACTS) {
+		dev_err(&ts->client->dev, "%s: invalid firmware: maximum panel width, height or number of touch points exceeded.\n", __func__);
+		return -EINVAL;
+	}
+
+	ts->x_reversed = header->xflipped ? 1 : 0;
+	ts->y_reversed = header->yflipped ? 1 : 0;
+	ts->xy_swapped = header->swapped ? 1 : 0;
+	ts->soft_tracking = header->tracking ? 1 : 0;
+	ts->jitter = GSL_JITTER;
+	ts->deadzone = GSL_DEADZONE;
+
+	return 0;
+}
+
+static int gsl_ts_write(struct i2c_client *client, u8 reg, const u8 *pdata, int datalen)
+{
+	u8 buf[GSL_PAGE_SIZE + 1];
+	unsigned int bytelen = 0;
+
+	if (datalen > GSL_MAX_WRITE) {
+		dev_err(&client->dev, "%s: data transfer too large (%d > %d)\n", __func__, datalen, GSL_MAX_WRITE);
+		return -EINVAL;
+	}
+
+	buf[0] = reg;
+	bytelen++;
+
+	if (datalen != 0 && pdata != NULL) {
+		memcpy(&buf[bytelen], pdata, datalen);
+		bytelen += datalen;
+	}
+
+	return i2c_master_send(client, buf, bytelen);
+}
+
+static int gsl_ts_read(struct i2c_client *client, u8 reg, u8 *pdata, unsigned int datalen)
+{
+	int ret = 0;
+
+	if (datalen > GSL_MAX_READ) {
+		dev_err(&client->dev, "%s: data transfer too large (%d > %d)\n", __func__, datalen, GSL_MAX_READ);
+		return -EINVAL;
+	}
+
+	ret = gsl_ts_write(client, reg, NULL, 0);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: sending register location failed\n", __func__);
+		return ret;
+	}
+
+	return i2c_master_recv(client, pdata, datalen);
+}
+
+static int gsl_ts_startup_chip(struct i2c_client *client)
+{
+	int rc;
+	u8 tmp = 0x00;
+
+	dev_dbg(&client->dev, "%s: starting up\n", __func__);
+	rc = gsl_ts_write(client, 0xe0, &tmp, 1);
+	usleep_range(10000, 20000);
+
+	return rc;
+}
+
+static int gsl_ts_reset_chip(struct i2c_client *client)
+{
+	int rc;
+	u8 arg[4] = { 0x00, 0x00, 0x00, 0x00 };
+
+	dev_dbg(&client->dev, "%s: resetting\n", __func__);
+
+	arg[0] = 0x88;
+	rc = gsl_ts_write(client, 0xe0, arg, 1);
+	if (rc < 0) {
+		dev_err(&client->dev, "%s: gsl_ts_write 1 fail!\n", __func__);
+		return rc;
+	}
+	usleep_range(10000, 20000);
+
+	arg[0] = 0x04;
+	rc = gsl_ts_write(client, 0xe4, arg, 1);
+	if (rc < 0) {
+		dev_err(&client->dev, "%s: gsl_ts_write 2 fail!\n", __func__);
+		return rc;
+	}
+	usleep_range(10000, 20000);
+
+	arg[0] = 0x00;
+	rc = gsl_ts_write(client, 0xbc, arg, 4);
+	if (rc < 0) {
+		dev_err(&client->dev, "%s: gsl_ts_write 3 fail!\n", __func__);
+		return rc;
+	}
+	usleep_range(10000, 20000);
+
+	return 0;
+}
+
+static int gsl_ts_write_fw(struct gsl_ts_data *ts, const struct firmware *fw)
+{
+	int rc = 0;
+	struct i2c_client *client = ts->client;
+	const struct gsl_ts_fw_header *header;
+	const struct gsl_ts_fw_page *page;
+	u32 pages, address;
+	u16 size;
+	size_t i;
+
+	dev_dbg(&client->dev, "%s: sending firmware\n", __func__);
+
+	header = (const struct gsl_ts_fw_header *) fw->data;
+	pages = le32_to_cpu(header->pages);
+	if (fw->size < sizeof(struct gsl_ts_fw_header) + pages * sizeof(struct gsl_ts_fw_page)) {
+		dev_err(&client->dev, "%s: firmware page data too small.\n", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; rc >= 0 && i < pages; i++) {
+		page = (const struct gsl_ts_fw_page *) &fw->data[sizeof(struct gsl_ts_fw_header) + i * sizeof(struct gsl_ts_fw_page)];
+		/* The controller expects a little endian address */
+		address = cpu_to_le32(le16_to_cpu(page->address));
+		size = le16_to_cpu(page->size);
+		rc = gsl_ts_write(client, GSL_PAGE_REG, (u8 *) &address, sizeof(address));
+		if (rc < 0) {
+			dev_err(&client->dev, "%s: error setting page register. (page = 0x%x)\n", __func__, le32_to_cpu(address));
+		} else {
+			rc = gsl_ts_write(client, 0, page->data, size);
+			if (rc < 0) {
+				dev_err(&client->dev, "%s: error writing page data. (page = 0x%x)\n", __func__, le32_to_cpu(address));
+			}
+		}
+	}
+
+	if (rc < 0) {
+		return rc;
+	}
+	return 0;
+}
+
+static void gsl_ts_mt_event(struct gsl_ts_data *ts, u8 *buf)
+{
+	int rc;
+	struct input_dev *input = ts->input;
+	struct device *dev = &ts->client->dev;
+	struct gsl_ts_packet_header *header;
+	struct gsl_ts_packet_touch *touch;
+	u8 i;
+	u16 touches, tseq, x, y, id, pressure;
+	struct input_mt_pos positions[GSL_MAX_CONTACTS];
+	int slots[GSL_MAX_CONTACTS];
+
+	header = (struct gsl_ts_packet_header *) buf;
+	touches = header->num_fingers;
+	tseq = le16_to_cpu(header->time_stamp);
+	/* time_stamp is 0 on zero-touch events, seems to wrap around 21800 */
+	dev_vdbg(dev, "%s: got touch events for %u fingers @%u\n", __func__, touches, tseq);
+
+	if (touches > GSL_MAX_CONTACTS) {
+		touches = GSL_MAX_CONTACTS;
+	}
+
+	for (i = 0; i < touches; i++) {
+		touch = (struct gsl_ts_packet_touch *) &buf[sizeof(*header) + i * sizeof(*touch)];
+		y = le16_to_cpu(touch->y_z);
+		x = le16_to_cpu(touch->x_id);
+		id = x >> 12;
+		x &= 0xfff;
+		pressure = y >> 12;
+		y &= 0xfff;
+
+		if (ts->xy_swapped) {
+			swap(x, y);
+		}
+		if (ts->x_reversed) {
+			x = ts->x_max - x;
+		}
+		if (ts->y_reversed) {
+			y = ts->y_max - y;
+		}
+
+		dev_vdbg(dev, "%s: touch event %u: x=%u y=%u id=0x%x p=%u\n", __func__, i, x, y, id, pressure);
+
+		positions[i].x = x;
+		positions[i].y = y;
+		if (!ts->soft_tracking) {
+			slots[i] = id;
+		}
+	}
+	if (ts->soft_tracking) {
+		/* This platform does not support finger tracking.
+		 * Use the input core finger tracker instead.
+		 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+		rc = input_mt_assign_slots(input, slots, positions, touches);
+#else
+		rc = input_mt_assign_slots(input, slots, positions, touches, GSL_DMAX);
+#endif
+		if (rc < 0) {
+			dev_err(dev, "%s: input_mt_assign_slots returned %d\n", __func__, rc);
+			return;
+		}
+	}
+
+	for (i = 0; i < touches; i++) {
+		input_mt_slot(input, slots[i]);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+		input_report_abs(input, ABS_MT_POSITION_X, positions[i].x);
+		input_report_abs(input, ABS_MT_POSITION_Y, positions[i].y);
+	}
+
+	input_mt_sync_frame(input);
+	input_sync(input);
+}
+
+static irqreturn_t gsl_ts_irq(int irq, void *arg)
+{
+	int rc;
+	struct gsl_ts_data *ts = (struct gsl_ts_data *) arg;
+	struct i2c_client *client = ts->client;
+	struct device *dev = &client->dev;
+	u8 status[4] = { 0, 0, 0, 0 };
+	u8 event[GSL_PACKET_SIZE];
+
+	dev_dbg(&client->dev, "%s: IRQ received\n", __func__);
+
+	if (ts->state == GSL_TS_SHUTDOWN) {
+		dev_warn(&client->dev, "%s: device supended, not handling interrupt\n", __func__);
+		return IRQ_HANDLED;
+	}
+
+	rc = gsl_ts_read(client, GSL_STATUS_REG, status, sizeof(status));
+	if (rc < 0) {
+		dev_err(dev, "%s: error reading chip status\n", __func__);
+		return IRQ_HANDLED;
+	}
+
+	if (status[0] == GSL_STATUS_FW) {
+		/* TODO: Send firmware here instead of during init */
+		dev_info(dev, "%s: device waiting for firmware\n", __func__);
+
+	} else if (status[0] == GSL_STATUS_TOUCH) {
+		dev_vdbg(dev, "%s: touch event\n", __func__);
+
+		rc = gsl_ts_read(client, GSL_DATA_REG, event, sizeof(event));
+		if (rc < 0) {
+			dev_err(dev, "%s: touch data read failed\n", __func__);
+			return IRQ_HANDLED;
+		}
+		if (event[0] == 0xff) {
+			dev_warn(dev, "%s: ignoring invalid touch record (0xff)\n", __func__);
+			return IRQ_HANDLED;
+		}
+
+		rc = gsl_ts_read(client, GSL_TOUCH_STATUS_REG, status, sizeof(status));
+		if (rc < 0) {
+			dev_err(dev, "%s: reading touch status register failed\n", __func__);
+			return IRQ_HANDLED;
+		}
+
+		if ((status[0] | status[1] | status[2] | status[3]) == 0) {
+			gsl_ts_mt_event(ts, event);
+
+		} else {
+			dev_warn(dev, "%s: device seems to be stuck, resetting\n", __func__);
+
+			rc = gsl_ts_reset_chip(ts->client);
+			if (rc < 0) {
+				dev_err(dev, "%s: reset_chip failed\n", __func__);
+				return IRQ_HANDLED;
+			}
+			rc = gsl_ts_startup_chip(ts->client);
+			if (rc < 0) {
+				dev_err(dev, "%s: startup_chip failed\n", __func__);
+				return IRQ_HANDLED;
+			}
+		}
+	} else {
+		dev_warn(&client->dev, "%s: IRQ received, unknown status 0x%02x\n", __func__, status[0]);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void gsl_ts_power(struct i2c_client *client, bool turnoff)
+{
+	struct gsl_ts_data *data = i2c_get_clientdata(client);
+#ifdef CONFIG_ACPI
+	int error;
+#endif
+
+	if (data) {
+		if (data->gpio) {
+			gpiod_set_value_cansleep(data->gpio, turnoff ? 0 : 1);
+#ifdef CONFIG_ACPI
+		} else {
+			error = acpi_bus_set_power(ACPI_HANDLE(&client->dev), turnoff ? ACPI_STATE_D3 : ACPI_STATE_D0);
+			if (error) {
+				dev_warn(&client->dev, "%s: error changing power state: %d\n", __func__, error);
+			}
+#endif
+		}
+		usleep_range(20000, 50000);
+	}
+}
+
+static int gsl_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct gsl_ts_data *ts;
+	const struct firmware *fw = NULL;
+	unsigned long irqflags;
+	int error;
+	bool acpipower;
+
+	dev_warn(&client->dev, "%s: got a device named %s at address 0x%x, IRQ %d, flags 0x%x\n", __func__, client->name, client->addr, client->irq, client->flags);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "%s: i2c check functionality error\n", __func__);
+		error = -ENXIO;
+		goto release;
+	}
+
+	if (client->irq <= 0) {
+		dev_err(&client->dev, "%s: missing IRQ configuration\n", __func__);
+		error = -ENODEV;
+		goto release;
+	}
+	
+	ts = devm_kzalloc(&client->dev, sizeof(struct gsl_ts_data), GFP_KERNEL);
+	if (!ts) {
+		error = -ENOMEM;
+		goto release;
+	}
+	
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+	
+	if (gsl_fw_name != NULL) {
+		strncpy(ts->fw_name, gsl_fw_name, sizeof(ts->fw_name));
+	} else {
+		strncpy(ts->fw_name, GSL_FW_NAME_DEFAULT, sizeof(ts->fw_name));
+	}
+	error = request_firmware(&fw, ts->fw_name, &ts->client->dev);
+	if (error < 0) {
+		dev_err(&client->dev, "%s: failed to load firmware: %d\n", __func__, error);
+		goto release;
+	}
+
+	error = gsl_ts_init(ts, fw);
+	if (error < 0) {
+		dev_err(&client->dev, "%s: failed to initialize: %d\n", __func__, error);
+		goto release;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "%s: failed to allocate input device\n", __func__);
+		error = -ENOMEM;
+		goto release;
+	}
+
+	ts->input->name = "Silead GSLx680 Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+	ts->input->phys = "input/ts";
+
+	input_set_capability(ts->input, EV_ABS, ABS_X);
+	input_set_capability(ts->input, EV_ABS, ABS_Y);
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, ts->jitter, ts->deadzone);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, ts->jitter, ts->deadzone);
+
+	input_mt_init_slots(ts->input, ts->multi_touches, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev, "%s: unable to register input device: %d\n", __func__, error);
+		goto release;
+	}
+
+	/* Try to use ACPI power methods first */
+	acpipower = false;
+#ifdef CONFIG_ACPI
+	if (ACPI_COMPANION(&client->dev)) {
+		/* Wake the device up with a power on reset */
+		if (acpi_bus_set_power(ACPI_HANDLE(&client->dev), ACPI_STATE_D3)) {
+			dev_warn(&client->dev, "%s: failed to wake up device through ACPI: %d, using GPIO controls instead\n", __func__, error);
+		} else {
+			acpipower = true;
+		}
+	}
+#endif
+	/* Not available, use GPIO settings from DSDT/DT instead */
+	if (!acpipower) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+		ts->gpio = devm_gpiod_get_index(&client->dev, GSL_PWR_GPIO, 0);
+#else
+		ts->gpio = devm_gpiod_get_index(&client->dev, GSL_PWR_GPIO, 0, GPIOD_OUT_LOW);
+#endif
+		if (IS_ERR(ts->gpio)) {
+			dev_err(&client->dev, "%s: error obtaining power pin GPIO resource\n", __func__);
+			error = PTR_ERR(ts->gpio);
+			goto release;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+		error = gpiod_direction_output(ts->gpio, 0);
+		if (error < 0) {
+			dev_err(&client->dev, "%s: error setting GPIO pin direction\n", __func__);
+			goto release;
+		}
+#endif
+	} else {
+		ts->gpio = NULL;
+	}
+
+	/* Enable power */
+	gsl_ts_power(client, false);
+
+	/* Execute the controller startup sequence */
+	error = gsl_ts_reset_chip(client);
+	if (error < 0) {
+		dev_err(&client->dev, "%s: chip reset failed\n", __func__);
+		goto release;
+	}
+	error = gsl_ts_write_fw(ts, fw);
+	if (error < 0) {
+		dev_err(&client->dev, "%s: firmware transfer failed\n", __func__);
+		goto release;
+	}
+	error = gsl_ts_startup_chip(client);
+	if (error < 0) {
+		dev_err(&client->dev, "%s: chip startup failed\n", __func__);
+		goto release;
+	}
+
+	/*
+	 * Systems using device tree should set up interrupt via DTS,
+	 * the rest will use the default falling edge interrupts.
+	 */
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	/* Set up interrupt handler - do we still need to account for shared interrupts? */
+	error = devm_request_threaded_irq(
+		&client->dev,
+		client->irq,
+		NULL,
+		gsl_ts_irq,
+		irqflags | IRQF_ONESHOT,
+		client->name,
+		ts
+	);
+	if (error) {
+		dev_err(&client->dev, "%s: failed to register interrupt\n", __func__);
+		goto release;
+	}
+
+	/*
+	 * Systems using device tree should set up wakeup via DTS,
+	 * the rest will configure device as wakeup source by default.
+	 */
+	if (!client->dev.of_node) {
+		device_init_wakeup(&client->dev, true);
+	}
+
+	ts->state = GSL_TS_GREEN;
+
+release:
+	if (fw) {
+		release_firmware(fw);
+	}
+
+	if (error < 0) {
+		return error;
+	}
+	return 0;
+}
+
+int gsl_ts_remove(struct i2c_client *client) {
+	/* Power the device off */
+	gsl_ts_power(client, true);
+	return 0;
+}
+
+static int __maybe_unused gsl_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct gsl_ts_data *ts = i2c_get_clientdata(client);
+
+	dev_warn(&client->dev, "%s: suspending device\n", __func__);
+
+	disable_irq(client->irq);
+
+	gsl_ts_reset_chip(client);
+	usleep_range(10000, 20000);
+
+	gsl_ts_power(client, true);
+
+	if (device_may_wakeup(dev)) {
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	}
+
+	ts->state = GSL_TS_SHUTDOWN;
+
+	return 0;
+}
+
+static int __maybe_unused gsl_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct gsl_ts_data *ts = i2c_get_clientdata(client);
+
+	dev_warn(&client->dev, "%s: resuming device\n", __func__);
+
+	if (device_may_wakeup(dev) && ts->wake_irq_enabled) {
+		disable_irq_wake(client->irq);
+	}
+
+	gsl_ts_power(client, false);
+
+	gsl_ts_reset_chip(client);
+	gsl_ts_startup_chip(client);
+
+	enable_irq(client->irq);
+
+	ts->state = GSL_TS_GREEN;
+
+	return 0;
+}
+
+static const struct i2c_device_id gsl_ts_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, gsl_ts_i2c_id);
+
+#ifdef CONFIG_PM
+static SIMPLE_DEV_PM_OPS(gsl_ts_pm_ops, gsl_ts_suspend, gsl_ts_resume);
+#endif
+
+#ifdef CONFIG_ACPI
+/* GSL3680 ACPI IDs are untested */
+static const struct acpi_device_id gsl_ts_acpi_match[] = {
+	{ "MSSL1680", 0 },
+	{ "MSSL3680", 0 },
+	{ "PNP1680", 0 },
+	{ "PNP3680", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, gsl_ts_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+/* These should work, but more testing is needed */
+static const struct of_device_id gsl_ts_of_match[] = {
+	{ .compatible = "silead,gsl1680" },
+	{ .compatible = "silead,gsl3680" },
+	{ .compatible = "silead,gslx680" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, gsl_ts_of_match);
+#endif
+
+static struct i2c_driver gslx680_ts_driver = {
+	.probe = gsl_ts_probe,
+	.remove = gsl_ts_remove,
+	.id_table = gsl_ts_i2c_id,
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &gsl_ts_pm_ops,
+#endif
+#ifdef CONFIG_ACPI
+		.acpi_match_table = ACPI_PTR(gsl_ts_acpi_match),
+#endif
+#ifdef CONFIG_OF
+		.of_match_table = of_match_ptr(gsl_ts_of_match),
+#endif
+	},
+};
+module_i2c_driver(gslx680_ts_driver);
+
+MODULE_DESCRIPTION("GSLX680 touchscreen controller driver");
+MODULE_AUTHOR("Gregor Riepl <onitake@gmail.com>");
+MODULE_PARM_DESC(fw_name, "firmware file name (default: " GSL_FW_NAME_DEFAULT ")");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
-- 
2.21.0