Update Linux to v5.10.109
Sourced from [1]
[1] https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.109.tar.xz
Change-Id: I19bca9fc6762d4e63bcf3e4cba88bbe560d9c76c
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1e812a1..cc18f54 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -202,6 +202,18 @@
To compile this driver as a module, choose M here: the
module will be called chipone_icn8505.
+config TOUCHSCREEN_CY8CTMA140
+ tristate "cy8ctma140 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a Cypress CY8CTMA140 capacitive
+ touchscreen also just known as "TMA140"
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cy8ctma140.
+
config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen"
depends on I2C
@@ -634,7 +646,7 @@
depends on SH_HP6XX && SH_ADC
help
Say Y here if you have a HP Jornada 620/660/680/690 and want to
- support the built-in touchscreen.
+ support the built-in touchscreen.
To compile this driver as a module, choose M here: the
module will be called hp680_ts_input.
@@ -701,7 +713,6 @@
config TOUCHSCREEN_RASPBERRYPI_FW
tristate "Raspberry Pi's firmware base touch screen support"
depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST)
- select INPUT_POLLDEV
help
Say Y here if you have the official Raspberry Pi 7 inch screen on
your system.
@@ -1039,7 +1050,6 @@
depends on HAS_IOMEM && OF
depends on SOC_IMX51 || COMPILE_TEST
select MFD_SYSCON
- select INPUT_POLLDEV
help
Say Y here if you have a touchscreen on a TS-4800 board.
@@ -1211,7 +1221,6 @@
tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
depends on USB && MEDIA_USB_SUPPORT && HAS_DMA
depends on VIDEO_V4L2
- select INPUT_POLLDEV
select VIDEOBUF2_DMA_SG
help
Say Y here if you want support for the Samsung SUR40 touchscreen
@@ -1247,7 +1256,6 @@
config TOUCHSCREEN_TPS6507X
tristate "TPS6507x based touchscreens"
depends on I2C
- select INPUT_POLLDEV
help
Say Y here if you have a TPS6507x based touchscreen
controller.
@@ -1315,4 +1323,16 @@
To compile this driver as a module, choose M here: the
module will be called iqs5xx.
+config TOUCHSCREEN_ZINITIX
+ tristate "Zinitix touchscreen support"
+ depends on I2C
+ help
+ Say Y here if you have a touchscreen using Zinitix bt541,
+ or something similar enough.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zinitix.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 94c6162..6233541 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -22,6 +22,7 @@
obj-$(CONFIG_TOUCHSCREEN_BU21029) += bu21029_ts.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o
+obj-$(CONFIG_TOUCHSCREEN_CY8CTMA140) += cy8ctma140.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
@@ -110,3 +111,4 @@
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o
obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o
+obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index d247d0a..ff97897 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -358,7 +358,8 @@
req->xfer[1].len = 2;
/* for 1uF, settle for 800 usec; no cap, 100 usec. */
- req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+ req->xfer[1].delay.value = ts->vref_delay_usecs;
+ req->xfer[1].delay.unit = SPI_DELAY_UNIT_USECS;
spi_message_add_tail(&req->xfer[1], &req->msg);
/* Enable reference voltage */
@@ -1028,7 +1029,8 @@
* have had enough time to stabilize.
*/
if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
+ x->delay.value = pdata->settle_delay_usecs;
+ x->delay.unit = SPI_DELAY_UNIT_USECS;
x++;
x->tx_buf = &packet->read_y;
@@ -1071,7 +1073,8 @@
/* ... maybe discard first sample ... */
if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
+ x->delay.value = pdata->settle_delay_usecs;
+ x->delay.unit = SPI_DELAY_UNIT_USECS;
x++;
x->tx_buf = &packet->read_x;
@@ -1104,7 +1107,8 @@
/* ... maybe discard first sample ... */
if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
+ x->delay.value = pdata->settle_delay_usecs;
+ x->delay.unit = SPI_DELAY_UNIT_USECS;
x++;
x->tx_buf = &packet->read_z1;
@@ -1135,7 +1139,8 @@
/* ... maybe discard first sample ... */
if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
+ x->delay.value = pdata->settle_delay_usecs;
+ x->delay.unit = SPI_DELAY_UNIT_USECS;
x++;
x->tx_buf = &packet->read_z2;
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
index 28644f3..c0d5c24 100644
--- a/drivers/input/touchscreen/ar1021_i2c.c
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -13,7 +13,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
-#define AR1021_TOCUH_PKG_SIZE 5
+#define AR1021_TOUCH_PKG_SIZE 5
#define AR1021_MAX_X 4095
#define AR1021_MAX_Y 4095
@@ -25,7 +25,7 @@
struct ar1021_i2c {
struct i2c_client *client;
struct input_dev *input;
- u8 data[AR1021_TOCUH_PKG_SIZE];
+ u8 data[AR1021_TOUCH_PKG_SIZE];
};
static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index ae60442..8df402a 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/slab.h>
@@ -129,6 +130,7 @@
/* MXT_SPT_COMMSCONFIG_T18 */
#define MXT_COMMS_CTRL 0
#define MXT_COMMS_CMD 1
+#define MXT_COMMS_RETRIGEN BIT(6)
/* MXT_DEBUG_DIAGNOSTIC_T37 */
#define MXT_DIAGNOSTIC_PAGEUP 0x01
@@ -308,6 +310,7 @@
struct t7_config t7_cfg;
struct mxt_dbg dbg;
struct gpio_desc *reset_gpio;
+ bool use_retrigen_workaround;
/* Cached parameters from object table */
u16 T5_address;
@@ -318,6 +321,7 @@
u16 T71_address;
u8 T9_reportid_min;
u8 T9_reportid_max;
+ u16 T18_address;
u8 T19_reportid;
u16 T44_address;
u8 T100_reportid_min;
@@ -473,7 +477,7 @@
bootloader = appmode - 0x24;
break;
}
- /* Fall through - for normal case */
+ fallthrough; /* for normal case */
case 0x4c:
case 0x4d:
case 0x5a:
@@ -822,8 +826,7 @@
* have happened.
*/
if (status & MXT_T9_RELEASE) {
- input_mt_report_slot_state(input_dev,
- MT_TOOL_FINGER, 0);
+ input_mt_report_slot_inactive(input_dev);
mxt_input_sync(data);
}
@@ -839,7 +842,7 @@
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
} else {
/* Touch no longer active, close out slot */
- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+ input_mt_report_slot_inactive(input_dev);
}
data->update_input = true;
@@ -947,7 +950,7 @@
dev_dbg(dev, "[%u] release\n", id);
/* close out slot */
- input_mt_report_slot_state(input_dev, 0, 0);
+ input_mt_report_slot_inactive(input_dev);
}
data->update_input = true;
@@ -1191,9 +1194,11 @@
enable_irq(data->irq);
- error = mxt_process_messages_until_invalid(data);
- if (error)
- return error;
+ if (data->use_retrigen_workaround) {
+ error = mxt_process_messages_until_invalid(data);
+ if (error)
+ return error;
+ }
return 0;
}
@@ -1283,6 +1288,38 @@
return crc;
}
+static int mxt_check_retrigen(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ int val;
+ struct irq_data *irqd;
+
+ data->use_retrigen_workaround = false;
+
+ irqd = irq_get_irq_data(data->irq);
+ if (!irqd)
+ return -EINVAL;
+
+ if (irqd_is_level_type(irqd))
+ return 0;
+
+ if (data->T18_address) {
+ error = __mxt_read_reg(client,
+ data->T18_address + MXT_COMMS_CTRL,
+ 1, &val);
+ if (error)
+ return error;
+
+ if (val & MXT_COMMS_RETRIGEN)
+ return 0;
+ }
+
+ dev_warn(&client->dev, "Enabling RETRIGEN workaround\n");
+ data->use_retrigen_workaround = true;
+ return 0;
+}
+
static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
{
struct device *dev = &data->client->dev;
@@ -1562,6 +1599,10 @@
mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
+ ret = mxt_check_retrigen(data);
+ if (ret)
+ goto release_mem;
+
ret = mxt_soft_reset(data);
if (ret)
goto release_mem;
@@ -1605,6 +1646,7 @@
data->T71_address = 0;
data->T9_reportid_min = 0;
data->T9_reportid_max = 0;
+ data->T18_address = 0;
data->T19_reportid = 0;
data->T44_address = 0;
data->T100_reportid_min = 0;
@@ -1679,6 +1721,9 @@
object->num_report_ids - 1;
data->num_touchids = object->num_report_ids;
break;
+ case MXT_SPT_COMMSCONFIG_T18:
+ data->T18_address = object->start_address;
+ break;
case MXT_SPT_MESSAGECOUNT_T44:
data->T44_address = object->start_address;
break;
@@ -1794,7 +1839,7 @@
if (error) {
dev_err(&client->dev, "Error %d parsing object table\n", error);
mxt_free_object_table(data);
- goto err_free_mem;
+ return error;
}
data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START);
@@ -2138,6 +2183,10 @@
msleep(MXT_FW_RESET_TIME);
}
+ error = mxt_check_retrigen(data);
+ if (error)
+ return error;
+
error = mxt_acquire_irq(data);
if (error)
return error;
diff --git a/drivers/input/touchscreen/chipone_icn8505.c b/drivers/input/touchscreen/chipone_icn8505.c
index c768186..f9ca550 100644
--- a/drivers/input/touchscreen/chipone_icn8505.c
+++ b/drivers/input/touchscreen/chipone_icn8505.c
@@ -288,7 +288,7 @@
* we may need it at resume. Having loaded it once will make the
* firmware class code cache it at suspend/resume.
*/
- error = request_firmware(&fw, icn8505->firmware_name, dev);
+ error = firmware_request_platform(&fw, icn8505->firmware_name, dev);
if (error) {
dev_err(dev, "Firmware request error %d\n", error);
return error;
diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c
index 0e40897..aa82972 100644
--- a/drivers/input/touchscreen/colibri-vf50-ts.c
+++ b/drivers/input/touchscreen/colibri-vf50-ts.c
@@ -9,7 +9,6 @@
#include <linux/delay.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/consumer.h>
#include <linux/iio/types.h>
diff --git a/drivers/input/touchscreen/cy8ctma140.c b/drivers/input/touchscreen/cy8ctma140.c
new file mode 100644
index 0000000..a9be291
--- /dev/null
+++ b/drivers/input/touchscreen/cy8ctma140.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Cypress CY8CTMA140 (TMA140) touchscreen
+ * (C) 2020 Linus Walleij <linus.walleij@linaro.org>
+ * (C) 2007 Cypress
+ * (C) 2007 Google, Inc.
+ *
+ * Inspired by the tma140_skomer.c driver in the Samsung GT-S7710 code
+ * drop. The GT-S7710 is codenamed "Skomer", the code also indicates
+ * that the same touchscreen was used in a product called "Lucas".
+ *
+ * The code drop for GT-S7710 also contains a firmware downloader and
+ * 15 (!) versions of the firmware drop from Cypress. But here we assume
+ * the firmware got downloaded to the touchscreen flash successfully and
+ * just use it to read the fingers. The shipped vendor driver does the
+ * same.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/input/mt.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#define CY8CTMA140_NAME "cy8ctma140"
+
+#define CY8CTMA140_MAX_FINGERS 4
+
+#define CY8CTMA140_GET_FINGERS 0x00
+#define CY8CTMA140_GET_FW_INFO 0x19
+
+/* This message also fits some bytes for touchkeys, if used */
+#define CY8CTMA140_PACKET_SIZE 31
+
+#define CY8CTMA140_INVALID_BUFFER_BIT 5
+
+struct cy8ctma140 {
+ struct input_dev *input;
+ struct touchscreen_properties props;
+ struct device *dev;
+ struct i2c_client *client;
+ struct regulator_bulk_data regulators[2];
+ u8 prev_fingers;
+ u8 prev_f1id;
+ u8 prev_f2id;
+};
+
+static void cy8ctma140_report(struct cy8ctma140 *ts, u8 *data, int n_fingers)
+{
+ static const u8 contact_offsets[] = { 0x03, 0x09, 0x10, 0x16 };
+ u8 *buf;
+ u16 x, y;
+ u8 w;
+ u8 id;
+ int slot;
+ int i;
+
+ for (i = 0; i < n_fingers; i++) {
+ buf = &data[contact_offsets[i]];
+
+ /*
+ * Odd contacts have contact ID in the lower nibble of
+ * the preceding byte, whereas even contacts have it in
+ * the upper nibble of the following byte.
+ */
+ id = i % 2 ? buf[-1] & 0x0f : buf[5] >> 4;
+ slot = input_mt_get_slot_by_key(ts->input, id);
+ if (slot < 0)
+ continue;
+
+ x = get_unaligned_be16(buf);
+ y = get_unaligned_be16(buf + 2);
+ w = buf[4];
+
+ dev_dbg(ts->dev, "finger %d: ID %02x (%d, %d) w: %d\n",
+ slot, id, x, y, w);
+
+ input_mt_slot(ts->input, slot);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
+ touchscreen_report_pos(ts->input, &ts->props, x, y, true);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, w);
+ }
+
+ input_mt_sync_frame(ts->input);
+ input_sync(ts->input);
+}
+
+static irqreturn_t cy8ctma140_irq_thread(int irq, void *d)
+{
+ struct cy8ctma140 *ts = d;
+ u8 cmdbuf[] = { CY8CTMA140_GET_FINGERS };
+ u8 buf[CY8CTMA140_PACKET_SIZE];
+ struct i2c_msg msg[] = {
+ {
+ .addr = ts->client->addr,
+ .flags = 0,
+ .len = sizeof(cmdbuf),
+ .buf = cmdbuf,
+ }, {
+ .addr = ts->client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ },
+ };
+ u8 n_fingers;
+ int ret;
+
+ ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ if (ret < 0)
+ dev_err(ts->dev, "error reading message: %d\n", ret);
+ else
+ dev_err(ts->dev, "wrong number of messages\n");
+ goto out;
+ }
+
+ if (buf[1] & BIT(CY8CTMA140_INVALID_BUFFER_BIT)) {
+ dev_dbg(ts->dev, "invalid event\n");
+ goto out;
+ }
+
+ n_fingers = buf[2] & 0x0f;
+ if (n_fingers > CY8CTMA140_MAX_FINGERS) {
+ dev_err(ts->dev, "unexpected number of fingers: %d\n",
+ n_fingers);
+ goto out;
+ }
+
+ cy8ctma140_report(ts, buf, n_fingers);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int cy8ctma140_init(struct cy8ctma140 *ts)
+{
+ u8 addr[1];
+ u8 buf[5];
+ int ret;
+
+ addr[0] = CY8CTMA140_GET_FW_INFO;
+ ret = i2c_master_send(ts->client, addr, 1);
+ if (ret < 0) {
+ dev_err(ts->dev, "error sending FW info message\n");
+ return ret;
+ }
+ ret = i2c_master_recv(ts->client, buf, 5);
+ if (ret < 0) {
+ dev_err(ts->dev, "error receiving FW info message\n");
+ return ret;
+ }
+ if (ret != 5) {
+ dev_err(ts->dev, "got only %d bytes\n", ret);
+ return -EIO;
+ }
+
+ dev_dbg(ts->dev, "vendor %c%c, HW ID %.2d, FW ver %.4d\n",
+ buf[0], buf[1], buf[3], buf[4]);
+
+ return 0;
+}
+
+static int cy8ctma140_power_up(struct cy8ctma140 *ts)
+{
+ int error;
+
+ error = regulator_bulk_enable(ARRAY_SIZE(ts->regulators),
+ ts->regulators);
+ if (error) {
+ dev_err(ts->dev, "failed to enable regulators\n");
+ return error;
+ }
+
+ msleep(250);
+
+ return 0;
+}
+
+static void cy8ctma140_power_down(struct cy8ctma140 *ts)
+{
+ regulator_bulk_disable(ARRAY_SIZE(ts->regulators),
+ ts->regulators);
+}
+
+/* Called from the registered devm action */
+static void cy8ctma140_power_off_action(void *d)
+{
+ struct cy8ctma140 *ts = d;
+
+ cy8ctma140_power_down(ts);
+}
+
+static int cy8ctma140_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cy8ctma140 *ts;
+ struct input_dev *input;
+ struct device *dev = &client->dev;
+ int error;
+
+ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ ts->dev = dev;
+ ts->client = client;
+ ts->input = input;
+
+ input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
+ input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
+ /* One byte for width 0..255 so this is the limit */
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ /*
+ * This sets up event max/min capabilities and fuzz.
+ * Some DT properties are compulsory so we do not need
+ * to provide defaults for X/Y max or pressure max.
+ *
+ * We just initialize a very simple MT touchscreen here,
+ * some devices use the capability of this touchscreen to
+ * provide touchkeys, and in that case this needs to be
+ * extended to handle touchkey input.
+ *
+ * The firmware takes care of finger tracking and dropping
+ * invalid ranges.
+ */
+ touchscreen_parse_properties(input, true, &ts->props);
+ input_abs_set_fuzz(input, ABS_MT_POSITION_X, 0);
+ input_abs_set_fuzz(input, ABS_MT_POSITION_Y, 0);
+
+ error = input_mt_init_slots(input, CY8CTMA140_MAX_FINGERS,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error)
+ return error;
+
+ input->name = CY8CTMA140_NAME;
+ input->id.bustype = BUS_I2C;
+ input_set_drvdata(input, ts);
+
+ /*
+ * VCPIN is the analog voltage supply
+ * VDD is the digital voltage supply
+ * since the voltage range of VDD overlaps that of VCPIN,
+ * many designs to just supply both with a single voltage
+ * source of ~3.3 V.
+ */
+ ts->regulators[0].supply = "vcpin";
+ ts->regulators[1].supply = "vdd";
+ error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->regulators),
+ ts->regulators);
+ if (error) {
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get regulators %d\n",
+ error);
+ return error;
+ }
+
+ error = cy8ctma140_power_up(ts);
+ if (error)
+ return error;
+
+ error = devm_add_action_or_reset(dev, cy8ctma140_power_off_action, ts);
+ if (error) {
+ dev_err(dev, "failed to install power off handler\n");
+ return error;
+ }
+
+ error = devm_request_threaded_irq(dev, client->irq,
+ NULL, cy8ctma140_irq_thread,
+ IRQF_ONESHOT, CY8CTMA140_NAME, ts);
+ if (error) {
+ dev_err(dev, "irq %d busy? error %d\n", client->irq, error);
+ return error;
+ }
+
+ error = cy8ctma140_init(ts);
+ if (error)
+ return error;
+
+ error = input_register_device(input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, ts);
+
+ return 0;
+}
+
+static int __maybe_unused cy8ctma140_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cy8ctma140 *ts = i2c_get_clientdata(client);
+
+ if (!device_may_wakeup(&client->dev))
+ cy8ctma140_power_down(ts);
+
+ return 0;
+}
+
+static int __maybe_unused cy8ctma140_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cy8ctma140 *ts = i2c_get_clientdata(client);
+ int error;
+
+ if (!device_may_wakeup(&client->dev)) {
+ error = cy8ctma140_power_up(ts);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cy8ctma140_pm, cy8ctma140_suspend, cy8ctma140_resume);
+
+static const struct i2c_device_id cy8ctma140_idtable[] = {
+ { CY8CTMA140_NAME, 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, cy8ctma140_idtable);
+
+static const struct of_device_id cy8ctma140_of_match[] = {
+ { .compatible = "cypress,cy8ctma140", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cy8ctma140_of_match);
+
+static struct i2c_driver cy8ctma140_driver = {
+ .driver = {
+ .name = CY8CTMA140_NAME,
+ .pm = &cy8ctma140_pm,
+ .of_match_table = cy8ctma140_of_match,
+ },
+ .id_table = cy8ctma140_idtable,
+ .probe = cy8ctma140_probe,
+};
+module_i2c_driver(cy8ctma140_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("CY8CTMA140 TouchScreen Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
index 6bcffc9..02a73d9 100644
--- a/drivers/input/touchscreen/cyttsp4_core.c
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -744,8 +744,7 @@
for (t = 0; t < max_slots; t++) {
input_mt_slot(md->input, t);
- input_mt_report_slot_state(md->input,
- MT_TOOL_FINGER, false);
+ input_mt_report_slot_inactive(md->input);
}
}
@@ -845,7 +844,7 @@
if (ids[t])
continue;
input_mt_slot(input, t);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ input_mt_report_slot_inactive(input);
}
input_sync(input);
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
index 3f5d463..697aa2c 100644
--- a/drivers/input/touchscreen/cyttsp_core.c
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -340,7 +340,7 @@
continue;
input_mt_slot(input, i);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ input_mt_report_slot_inactive(input);
}
input_sync(input);
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index b41b97c..6ff81d4 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -13,20 +13,22 @@
* http://www.glyn.com/Products/Displays
*/
-#include <linux/module.h>
-#include <linux/ratelimit.h>
-#include <linux/irq.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/input.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <linux/gpio/consumer.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
#include <asm/unaligned.h>
#define WORK_REGISTER_THRESHOLD 0x00
@@ -36,6 +38,9 @@
#define WORK_REGISTER_NUM_X 0x33
#define WORK_REGISTER_NUM_Y 0x34
+#define PMOD_REGISTER_ACTIVE 0x00
+#define PMOD_REGISTER_HIBERNATE 0x03
+
#define M09_REGISTER_THRESHOLD 0x80
#define M09_REGISTER_GAIN 0x92
#define M09_REGISTER_OFFSET 0x93
@@ -51,6 +56,7 @@
#define WORK_REGISTER_OPMODE 0x3c
#define FACTORY_REGISTER_OPMODE 0x01
+#define PMOD_REGISTER_OPMODE 0xa5
#define TOUCH_EVENT_DOWN 0x00
#define TOUCH_EVENT_UP 0x01
@@ -63,6 +69,12 @@
#define EDT_RAW_DATA_RETRIES 100
#define EDT_RAW_DATA_DELAY 1000 /* usec */
+enum edt_pmode {
+ EDT_PMODE_NOT_SUPPORTED,
+ EDT_PMODE_HIBERNATE,
+ EDT_PMODE_POWEROFF,
+};
+
enum edt_ver {
EDT_M06,
EDT_M09,
@@ -88,6 +100,7 @@
struct touchscreen_properties prop;
u16 num_x;
u16 num_y;
+ struct regulator *vcc;
struct gpio_desc *reset_gpio;
struct gpio_desc *wake_gpio;
@@ -100,6 +113,7 @@
struct mutex mutex;
bool factory_mode;
+ enum edt_pmode suspend_mode;
int threshold;
int gain;
int offset;
@@ -274,7 +288,7 @@
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
wrbuf, 0, NULL);
- /* fallthrough */
+
case EDT_M09:
case EDT_M12:
case EV_FT:
@@ -316,7 +330,6 @@
}
break;
- /* fallthrough */
case EDT_M09:
case EDT_M12:
case EV_FT:
@@ -524,6 +537,29 @@
.attrs = edt_ft5x06_attrs,
};
+static void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
+ tsdata->threshold);
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
+ tsdata->gain);
+ if (reg_addr->reg_offset != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
+ tsdata->offset);
+ if (reg_addr->reg_offset_x != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
+ tsdata->offset_x);
+ if (reg_addr->reg_offset_y != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
+ tsdata->offset_y);
+ if (reg_addr->reg_report_rate != NO_REGISTER)
+ edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
+ tsdata->report_rate);
+
+}
+
#ifdef CONFIG_DEBUG_FS
static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
{
@@ -589,7 +625,6 @@
{
struct i2c_client *client = tsdata->client;
int retries = EDT_SWITCH_MODE_RETRIES;
- struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
int ret;
int error;
@@ -621,24 +656,7 @@
kfree(tsdata->raw_buffer);
tsdata->raw_buffer = NULL;
- /* restore parameters */
- edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
- tsdata->threshold);
- edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
- tsdata->gain);
- if (reg_addr->reg_offset != NO_REGISTER)
- edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
- tsdata->offset);
- if (reg_addr->reg_offset_x != NO_REGISTER)
- edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
- tsdata->offset_x);
- if (reg_addr->reg_offset_y != NO_REGISTER)
- edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
- tsdata->offset_y);
- if (reg_addr->reg_report_rate != NO_REGISTER)
- edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
- tsdata->report_rate);
-
+ edt_ft5x06_restore_reg_parameters(tsdata);
enable_irq(client->irq);
return 0;
@@ -759,9 +777,8 @@
.read = edt_ft5x06_debugfs_raw_data_read,
};
-static void
-edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
- const char *debugfs_name)
+static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+ const char *debugfs_name)
{
tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
@@ -774,8 +791,7 @@
tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
}
-static void
-edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
{
debugfs_remove_recursive(tsdata->debug_dir);
kfree(tsdata->raw_buffer);
@@ -783,14 +799,17 @@
#else
-static inline void
-edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
- const char *debugfs_name)
+static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+ return -ENOSYS;
+}
+
+static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+ const char *debugfs_name)
{
}
-static inline void
-edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
{
}
@@ -1042,6 +1061,13 @@
}
}
+static void edt_ft5x06_disable_regulator(void *arg)
+{
+ struct edt_ft5x06_ts_data *data = arg;
+
+ regulator_disable(data->vcc);
+}
+
static int edt_ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1071,6 +1097,27 @@
tsdata->max_support_points = chip_data->max_support_points;
+ tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
+ if (IS_ERR(tsdata->vcc)) {
+ error = PTR_ERR(tsdata->vcc);
+ if (error != -EPROBE_DEFER)
+ dev_err(&client->dev,
+ "failed to request regulator: %d\n", error);
+ return error;
+ }
+
+ error = regulator_enable(tsdata->vcc);
+ if (error < 0) {
+ dev_err(&client->dev, "failed to enable vcc: %d\n", error);
+ return error;
+ }
+
+ error = devm_add_action_or_reset(&client->dev,
+ edt_ft5x06_disable_regulator,
+ tsdata);
+ if (error)
+ return error;
+
tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
"reset", GPIOD_OUT_HIGH);
if (IS_ERR(tsdata->reset_gpio)) {
@@ -1089,6 +1136,19 @@
return error;
}
+ /*
+ * Check which sleep modes we can support. Power-off requieres the
+ * reset-pin to ensure correct power-down/power-up behaviour. Start with
+ * the EDT_PMODE_POWEROFF test since this is the deepest possible sleep
+ * mode.
+ */
+ if (tsdata->reset_gpio)
+ tsdata->suspend_mode = EDT_PMODE_POWEROFF;
+ else if (tsdata->wake_gpio)
+ tsdata->suspend_mode = EDT_PMODE_HIBERNATE;
+ else
+ tsdata->suspend_mode = EDT_PMODE_NOT_SUPPORTED;
+
if (tsdata->wake_gpio) {
usleep_range(5000, 6000);
gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
@@ -1183,7 +1243,6 @@
return error;
edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
- device_init_wakeup(&client->dev, 1);
dev_dbg(&client->dev,
"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
@@ -1206,9 +1265,39 @@
static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct gpio_desc *reset_gpio = tsdata->reset_gpio;
+ int ret;
if (device_may_wakeup(dev))
- enable_irq_wake(client->irq);
+ return 0;
+
+ if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
+ return 0;
+
+ /* Enter hibernate mode. */
+ ret = edt_ft5x06_register_write(tsdata, PMOD_REGISTER_OPMODE,
+ PMOD_REGISTER_HIBERNATE);
+ if (ret)
+ dev_warn(dev, "Failed to set hibernate mode\n");
+
+ if (tsdata->suspend_mode == EDT_PMODE_HIBERNATE)
+ return 0;
+
+ /*
+ * Power-off according the datasheet. Cut the power may leaf the irq
+ * line in an undefined state depending on the host pull resistor
+ * settings. Disable the irq to avoid adjusting each host till the
+ * device is back in a full functional state.
+ */
+ disable_irq(tsdata->client->irq);
+
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ usleep_range(1000, 2000);
+
+ ret = regulator_disable(tsdata->vcc);
+ if (ret)
+ dev_warn(dev, "Failed to disable vcc\n");
return 0;
}
@@ -1216,11 +1305,54 @@
static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ int ret = 0;
if (device_may_wakeup(dev))
- disable_irq_wake(client->irq);
+ return 0;
- return 0;
+ if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
+ return 0;
+
+ if (tsdata->suspend_mode == EDT_PMODE_POWEROFF) {
+ struct gpio_desc *reset_gpio = tsdata->reset_gpio;
+
+ /*
+ * We can't check if the regulator is a dummy or a real
+ * regulator. So we need to specify the 5ms reset time (T_rst)
+ * here instead of the 100us T_rtp time. We also need to wait
+ * 300ms in case it was a real supply and the power was cutted
+ * of. Toggle the reset pin is also a way to exit the hibernate
+ * mode.
+ */
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ usleep_range(5000, 6000);
+
+ ret = regulator_enable(tsdata->vcc);
+ if (ret) {
+ dev_err(dev, "Failed to enable vcc\n");
+ return ret;
+ }
+
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ msleep(300);
+
+ edt_ft5x06_restore_reg_parameters(tsdata);
+ enable_irq(tsdata->client->irq);
+
+ if (tsdata->factory_mode)
+ ret = edt_ft5x06_factory_mode(tsdata);
+ } else {
+ struct gpio_desc *wake_gpio = tsdata->wake_gpio;
+
+ gpiod_set_value_cansleep(wake_gpio, 0);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(wake_gpio, 1);
+ }
+
+
+ return ret;
}
static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
@@ -1265,6 +1397,7 @@
.name = "edt_ft5x06",
.of_match_table = edt_ft5x06_of_match,
.pm = &edt_ft5x06_ts_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = edt_ft5x06_ts_id,
.probe = edt_ft5x06_ts_probe,
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index a51e7c8..c09aefa 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -19,6 +19,7 @@
*/
+#include <linux/bits.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
@@ -32,6 +33,7 @@
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
@@ -60,8 +62,10 @@
#define CMD_HEADER_WRITE 0x54
#define CMD_HEADER_READ 0x53
#define CMD_HEADER_6B_READ 0x5B
+#define CMD_HEADER_ROM_READ 0x96
#define CMD_HEADER_RESP 0x52
#define CMD_HEADER_6B_RESP 0x9B
+#define CMD_HEADER_ROM_RESP 0x95
#define CMD_HEADER_HELLO 0x55
#define CMD_HEADER_REK 0x66
@@ -72,6 +76,7 @@
#define FW_POS_STATE 1
#define FW_POS_TOTAL 2
#define FW_POS_XY 3
+#define FW_POS_TOOL_TYPE 33
#define FW_POS_CHECKSUM 34
#define FW_POS_WIDTH 35
#define FW_POS_PRESSURE 45
@@ -86,6 +91,7 @@
/* FW read command, 0x53 0x?? 0x0, 0x01 */
#define E_ELAN_INFO_FW_VER 0x00
#define E_ELAN_INFO_BC_VER 0x10
+#define E_ELAN_INFO_REK 0xD0
#define E_ELAN_INFO_TEST_VER 0xE0
#define E_ELAN_INFO_FW_ID 0xF0
#define E_INFO_OSR 0xD6
@@ -103,6 +109,19 @@
#define ELAN_POWERON_DELAY_USEC 500
#define ELAN_RESET_DELAY_MSEC 20
+/* FW boot code version */
+#define BC_VER_H_BYTE_FOR_EKTH3900x1_I2C 0x72
+#define BC_VER_H_BYTE_FOR_EKTH3900x2_I2C 0x82
+#define BC_VER_H_BYTE_FOR_EKTH3900x3_I2C 0x92
+#define BC_VER_H_BYTE_FOR_EKTH5312x1_I2C 0x6D
+#define BC_VER_H_BYTE_FOR_EKTH5312x2_I2C 0x6E
+#define BC_VER_H_BYTE_FOR_EKTH5312cx1_I2C 0x77
+#define BC_VER_H_BYTE_FOR_EKTH5312cx2_I2C 0x78
+#define BC_VER_H_BYTE_FOR_EKTH5312x1_I2C_USB 0x67
+#define BC_VER_H_BYTE_FOR_EKTH5312x2_I2C_USB 0x68
+#define BC_VER_H_BYTE_FOR_EKTH5312cx1_I2C_USB 0x74
+#define BC_VER_H_BYTE_FOR_EKTH5312cx2_I2C_USB 0x75
+
enum elants_state {
ELAN_STATE_NORMAL,
ELAN_WAIT_QUEUE_HEADER,
@@ -129,10 +148,12 @@
u8 bc_version;
u8 iap_version;
u16 hw_version;
+ u8 major_res;
unsigned int x_res; /* resolution in units/mm */
unsigned int y_res;
unsigned int x_max;
unsigned int y_max;
+ struct touchscreen_properties prop;
enum elants_state state;
enum elants_iap_mode iap_mode;
@@ -186,7 +207,8 @@
static int elants_i2c_execute_command(struct i2c_client *client,
const u8 *cmd, size_t cmd_size,
- u8 *resp, size_t resp_size)
+ u8 *resp, size_t resp_size,
+ int retries, const char *cmd_name)
{
struct i2c_msg msgs[2];
int ret;
@@ -201,31 +223,60 @@
expected_response = CMD_HEADER_6B_RESP;
break;
+ case CMD_HEADER_ROM_READ:
+ expected_response = CMD_HEADER_ROM_RESP;
+ break;
+
default:
- dev_err(&client->dev, "%s: invalid command %*ph\n",
- __func__, (int)cmd_size, cmd);
+ dev_err(&client->dev, "(%s): invalid command: %*ph\n",
+ cmd_name, (int)cmd_size, cmd);
return -EINVAL;
}
- msgs[0].addr = client->addr;
- msgs[0].flags = client->flags & I2C_M_TEN;
- msgs[0].len = cmd_size;
- msgs[0].buf = (u8 *)cmd;
+ for (;;) {
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = cmd_size;
+ msgs[0].buf = (u8 *)cmd;
- msgs[1].addr = client->addr;
- msgs[1].flags = client->flags & I2C_M_TEN;
- msgs[1].flags |= I2C_M_RD;
- msgs[1].len = resp_size;
- msgs[1].buf = resp;
+ msgs[1].addr = client->addr;
+ msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+ msgs[1].flags |= I2C_M_RD;
+ msgs[1].len = resp_size;
+ msgs[1].buf = resp;
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret < 0)
- return ret;
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0) {
+ if (--retries > 0) {
+ dev_dbg(&client->dev,
+ "(%s) I2C transfer failed: %pe (retrying)\n",
+ cmd_name, ERR_PTR(ret));
+ continue;
+ }
- if (ret != ARRAY_SIZE(msgs) || resp[FW_HDR_TYPE] != expected_response)
- return -EIO;
+ dev_err(&client->dev,
+ "(%s) I2C transfer failed: %pe\n",
+ cmd_name, ERR_PTR(ret));
+ return ret;
+ }
- return 0;
+ if (ret != ARRAY_SIZE(msgs) ||
+ resp[FW_HDR_TYPE] != expected_response) {
+ if (--retries > 0) {
+ dev_dbg(&client->dev,
+ "(%s) unexpected response: %*ph (retrying)\n",
+ cmd_name, ret, resp);
+ continue;
+ }
+
+ dev_err(&client->dev,
+ "(%s) unexpected response: %*ph\n",
+ cmd_name, ret, resp);
+ return -EIO;
+ }
+
+ return 0;
+ }
}
static int elants_i2c_calibrate(struct elants_data *ts)
@@ -298,27 +349,21 @@
static int elants_i2c_query_hw_version(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
- int error, retry_cnt;
+ int retry_cnt = MAX_RETRIES;
const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_ID, 0x00, 0x01 };
u8 resp[HEADER_SIZE];
+ int error;
- for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ while (retry_cnt--) {
error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (!error) {
- ts->hw_version = elants_i2c_parse_version(resp);
- if (ts->hw_version != 0xffff)
- return 0;
- }
+ resp, sizeof(resp), 1,
+ "read fw id");
+ if (error)
+ return error;
- dev_dbg(&client->dev, "read fw id error=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
- }
-
- if (error) {
- dev_err(&client->dev,
- "Failed to read fw id: %d\n", error);
- return error;
+ ts->hw_version = elants_i2c_parse_version(resp);
+ if (ts->hw_version != 0xffff)
+ return 0;
}
dev_err(&client->dev, "Invalid fw id: %#04x\n", ts->hw_version);
@@ -329,26 +374,27 @@
static int elants_i2c_query_fw_version(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
- int error, retry_cnt;
+ int retry_cnt = MAX_RETRIES;
const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_VER, 0x00, 0x01 };
u8 resp[HEADER_SIZE];
+ int error;
- for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+ while (retry_cnt--) {
error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (!error) {
- ts->fw_version = elants_i2c_parse_version(resp);
- if (ts->fw_version != 0x0000 &&
- ts->fw_version != 0xffff)
- return 0;
- }
+ resp, sizeof(resp), 1,
+ "read fw version");
+ if (error)
+ return error;
- dev_dbg(&client->dev, "read fw version error=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
+ ts->fw_version = elants_i2c_parse_version(resp);
+ if (ts->fw_version != 0x0000 && ts->fw_version != 0xffff)
+ return 0;
+
+ dev_dbg(&client->dev, "(read fw version) resp %*phC\n",
+ (int)sizeof(resp), resp);
}
- dev_err(&client->dev,
- "Failed to read fw version or fw version is invalid\n");
+ dev_err(&client->dev, "Invalid fw ver: %#04x\n", ts->fw_version);
return -EINVAL;
}
@@ -356,30 +402,24 @@
static int elants_i2c_query_test_version(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
- int error, retry_cnt;
+ int error;
u16 version;
const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_TEST_VER, 0x00, 0x01 };
u8 resp[HEADER_SIZE];
- for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
- error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (!error) {
- version = elants_i2c_parse_version(resp);
- ts->test_version = version >> 8;
- ts->solution_version = version & 0xff;
-
- return 0;
- }
-
- dev_dbg(&client->dev,
- "read test version error rc=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp), MAX_RETRIES,
+ "read test version");
+ if (error) {
+ dev_err(&client->dev, "Failed to read test version\n");
+ return error;
}
- dev_err(&client->dev, "Failed to read test version\n");
+ version = elants_i2c_parse_version(resp);
+ ts->test_version = version >> 8;
+ ts->solution_version = version & 0xff;
- return -EINVAL;
+ return 0;
}
static int elants_i2c_query_bc_version(struct elants_data *ts)
@@ -391,13 +431,10 @@
int error;
error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev,
- "read BC version error=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
+ resp, sizeof(resp), 1,
+ "read BC version");
+ if (error)
return error;
- }
version = elants_i2c_parse_version(resp);
ts->bc_version = version >> 8;
@@ -429,49 +466,43 @@
error = elants_i2c_execute_command(client,
get_resolution_cmd,
sizeof(get_resolution_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get resolution command failed: %d\n",
- error);
+ resp, sizeof(resp), 1,
+ "get resolution");
+ if (error)
return error;
- }
rows = resp[2] + resp[6] + resp[10];
cols = resp[3] + resp[7] + resp[11];
+ /* Get report resolution value of ABS_MT_TOUCH_MAJOR */
+ ts->major_res = resp[16];
+
/* Process mm_to_pixel information */
error = elants_i2c_execute_command(client,
get_osr_cmd, sizeof(get_osr_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get osr command failed: %d\n",
- error);
+ resp, sizeof(resp), 1, "get osr");
+ if (error)
return error;
- }
osr = resp[3];
error = elants_i2c_execute_command(client,
get_physical_scan_cmd,
sizeof(get_physical_scan_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get physical scan command failed: %d\n",
- error);
+ resp, sizeof(resp), 1,
+ "get physical scan");
+ if (error)
return error;
- }
phy_x = get_unaligned_be16(&resp[2]);
error = elants_i2c_execute_command(client,
get_physical_drive_cmd,
sizeof(get_physical_drive_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get physical drive command failed: %d\n",
- error);
+ resp, sizeof(resp), 1,
+ "get physical drive");
+ if (error)
return error;
- }
phy_y = get_unaligned_be16(&resp[2]);
@@ -557,6 +588,8 @@
/* hw version is available even if device in recovery state */
error2 = elants_i2c_query_hw_version(ts);
+ if (!error2)
+ error2 = elants_i2c_query_bc_version(ts);
if (!error)
error = error2;
@@ -565,8 +598,6 @@
if (!error)
error = elants_i2c_query_test_version(ts);
if (!error)
- error = elants_i2c_query_bc_version(ts);
- if (!error)
error = elants_i2c_query_ts_info(ts);
if (error)
@@ -614,39 +645,124 @@
return error;
}
+static int elants_i2c_validate_remark_id(struct elants_data *ts,
+ const struct firmware *fw)
+{
+ struct i2c_client *client = ts->client;
+ int error;
+ const u8 cmd[] = { CMD_HEADER_ROM_READ, 0x80, 0x1F, 0x00, 0x00, 0x21 };
+ u8 resp[6] = { 0 };
+ u16 ts_remark_id = 0;
+ u16 fw_remark_id = 0;
+
+ /* Compare TS Remark ID and FW Remark ID */
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp),
+ 1, "read Remark ID");
+ if (error)
+ return error;
+
+ ts_remark_id = get_unaligned_be16(&resp[3]);
+
+ fw_remark_id = get_unaligned_le16(&fw->data[fw->size - 4]);
+
+ if (fw_remark_id != ts_remark_id) {
+ dev_err(&client->dev,
+ "Remark ID Mismatched: ts_remark_id=0x%04x, fw_remark_id=0x%04x.\n",
+ ts_remark_id, fw_remark_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool elants_i2c_should_check_remark_id(struct elants_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ const u8 bootcode_version = ts->iap_version;
+ bool check;
+
+ /* I2C eKTH3900 and eKTH5312 are NOT support Remark ID */
+ if ((bootcode_version == BC_VER_H_BYTE_FOR_EKTH3900x1_I2C) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH3900x2_I2C) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH3900x3_I2C) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312x1_I2C) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312x2_I2C) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312cx1_I2C) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312cx2_I2C) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312x1_I2C_USB) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312x2_I2C_USB) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312cx1_I2C_USB) ||
+ (bootcode_version == BC_VER_H_BYTE_FOR_EKTH5312cx2_I2C_USB)) {
+ dev_dbg(&client->dev,
+ "eKTH3900/eKTH5312(0x%02x) are not support remark id\n",
+ bootcode_version);
+ check = false;
+ } else if (bootcode_version >= 0x60) {
+ check = true;
+ } else {
+ check = false;
+ }
+
+ return check;
+}
+
static int elants_i2c_do_update_firmware(struct i2c_client *client,
const struct firmware *fw,
bool force)
{
+ struct elants_data *ts = i2c_get_clientdata(client);
const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 };
const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 };
const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc };
- const u8 close_idle[] = {0x54, 0x2c, 0x01, 0x01};
+ const u8 close_idle[] = { 0x54, 0x2c, 0x01, 0x01 };
u8 buf[HEADER_SIZE];
u16 send_id;
int page, n_fw_pages;
int error;
+ bool check_remark_id = elants_i2c_should_check_remark_id(ts);
/* Recovery mode detection! */
if (force) {
dev_dbg(&client->dev, "Recovery mode procedure\n");
+
+ if (check_remark_id) {
+ error = elants_i2c_validate_remark_id(ts, fw);
+ if (error)
+ return error;
+ }
+
error = elants_i2c_send(client, enter_iap2, sizeof(enter_iap2));
+ if (error) {
+ dev_err(&client->dev, "failed to enter IAP mode: %d\n",
+ error);
+ return error;
+ }
} else {
/* Start IAP Procedure */
dev_dbg(&client->dev, "Normal IAP procedure\n");
+
/* Close idle mode */
error = elants_i2c_send(client, close_idle, sizeof(close_idle));
if (error)
dev_err(&client->dev, "Failed close idle: %d\n", error);
msleep(60);
+
elants_i2c_sw_reset(client);
msleep(20);
- error = elants_i2c_send(client, enter_iap, sizeof(enter_iap));
- }
- if (error) {
- dev_err(&client->dev, "failed to enter IAP mode: %d\n", error);
- return error;
+ if (check_remark_id) {
+ error = elants_i2c_validate_remark_id(ts, fw);
+ if (error)
+ return error;
+ }
+
+ error = elants_i2c_send(client, enter_iap, sizeof(enter_iap));
+ if (error) {
+ dev_err(&client->dev, "failed to enter IAP mode: %d\n",
+ error);
+ return error;
+ }
}
msleep(20);
@@ -782,6 +898,7 @@
{
struct input_dev *input = ts->input;
unsigned int n_fingers;
+ unsigned int tool_type;
u16 finger_state;
int i;
@@ -792,6 +909,10 @@
dev_dbg(&ts->client->dev,
"n_fingers: %u, state: %04x\n", n_fingers, finger_state);
+ /* Note: all fingers have the same tool type */
+ tool_type = buf[FW_POS_TOOL_TYPE] & BIT(0) ?
+ MT_TOOL_FINGER : MT_TOOL_PALM;
+
for (i = 0; i < MAX_CONTACT_NUM && n_fingers; i++) {
if (finger_state & 1) {
unsigned int x, y, p, w;
@@ -807,9 +928,8 @@
i, x, y, p, w);
input_mt_slot(input, i);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
- input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
- input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+ input_mt_report_slot_state(input, tool_type, true);
+ touchscreen_report_pos(input, &ts->prop, x, y, true);
input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, w);
@@ -884,7 +1004,7 @@
break;
ts->state = ELAN_STATE_NORMAL;
- /* fall through */
+ fallthrough;
case ELAN_STATE_NORMAL:
@@ -950,7 +1070,7 @@
*/
static ssize_t calibrate_store(struct device *dev,
struct device_attribute *attr,
- const char *buf, size_t count)
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct elants_data *ts = i2c_get_clientdata(client);
@@ -996,8 +1116,28 @@
"Normal" : "Recovery");
}
+static ssize_t show_calibration_count(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_REK, 0x00, 0x01 };
+ u8 resp[HEADER_SIZE];
+ u16 rek_count;
+ int error;
+
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp), 1,
+ "read ReK status");
+ if (error)
+ return sprintf(buf, "%d\n", error);
+
+ rek_count = get_unaligned_be16(&resp[2]);
+ return sprintf(buf, "0x%04x\n", rek_count);
+}
+
static DEVICE_ATTR_WO(calibrate);
static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL);
+static DEVICE_ATTR(calibration_count, S_IRUGO, show_calibration_count, NULL);
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
struct elants_version_attribute {
@@ -1053,6 +1193,7 @@
&dev_attr_calibrate.attr,
&dev_attr_update_fw.attr,
&dev_attr_iap_mode.attr,
+ &dev_attr_calibration_count.attr,
&elants_ver_attr_fw_version.dattr.attr,
&elants_ver_attr_hw_version.dattr.attr,
@@ -1262,18 +1403,21 @@
ts->input->name = "Elan Touchscreen";
ts->input->id.bustype = BUS_I2C;
- __set_bit(BTN_TOUCH, ts->input->keybit);
- __set_bit(EV_ABS, ts->input->evbit);
- __set_bit(EV_KEY, ts->input->evbit);
-
- /* Single touch input params setup */
- input_set_abs_params(ts->input, ABS_X, 0, ts->x_max, 0, 0);
- input_set_abs_params(ts->input, ABS_Y, 0, ts->y_max, 0, 0);
- input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
- input_abs_set_res(ts->input, ABS_X, ts->x_res);
- input_abs_set_res(ts->input, ABS_Y, ts->y_res);
-
/* Multitouch input params setup */
+
+ input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_PALM, 0, 0);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
+ if (ts->major_res > 0)
+ input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, ts->major_res);
+
+ touchscreen_parse_properties(ts->input, true, &ts->prop);
+
error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
@@ -1282,13 +1426,6 @@
return error;
}
- input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0);
- input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0);
- input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
- input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
- input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
- input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
-
error = input_register_device(ts->input);
if (error) {
dev_err(&client->dev,
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
index e396857..9617323 100644
--- a/drivers/input/touchscreen/elo.c
+++ b/drivers/input/touchscreen/elo.c
@@ -350,7 +350,7 @@
case 1: /* 6-byte protocol */
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
- /* fall through */
+ fallthrough;
case 2: /* 4-byte protocol */
input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c
index e007e2e..a6597f0 100644
--- a/drivers/input/touchscreen/exc3000.c
+++ b/drivers/input/touchscreen/exc3000.c
@@ -8,7 +8,9 @@
*/
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
@@ -16,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/sizes.h>
#include <linux/timer.h>
#include <asm/unaligned.h>
@@ -23,15 +26,59 @@
#define EXC3000_SLOTS_PER_FRAME 5
#define EXC3000_LEN_FRAME 66
#define EXC3000_LEN_POINT 10
-#define EXC3000_MT_EVENT 6
+
+#define EXC3000_LEN_MODEL_NAME 16
+#define EXC3000_LEN_FW_VERSION 16
+
+#define EXC3000_MT1_EVENT 0x06
+#define EXC3000_MT2_EVENT 0x18
+
#define EXC3000_TIMEOUT_MS 100
+#define EXC3000_RESET_MS 10
+#define EXC3000_READY_MS 100
+
+static const struct i2c_device_id exc3000_id[];
+
+struct eeti_dev_info {
+ const char *name;
+ int max_xy;
+};
+
+enum eeti_dev_id {
+ EETI_EXC3000,
+ EETI_EXC80H60,
+ EETI_EXC80H84,
+};
+
+static struct eeti_dev_info exc3000_info[] = {
+ [EETI_EXC3000] = {
+ .name = "EETI EXC3000 Touch Screen",
+ .max_xy = SZ_4K - 1,
+ },
+ [EETI_EXC80H60] = {
+ .name = "EETI EXC80H60 Touch Screen",
+ .max_xy = SZ_16K - 1,
+ },
+ [EETI_EXC80H84] = {
+ .name = "EETI EXC80H84 Touch Screen",
+ .max_xy = SZ_16K - 1,
+ },
+};
+
struct exc3000_data {
struct i2c_client *client;
+ const struct eeti_dev_info *info;
struct input_dev *input;
struct touchscreen_properties prop;
+ struct gpio_desc *reset;
struct timer_list timer;
u8 buf[2 * EXC3000_LEN_FRAME];
+ struct completion wait_event;
+ struct mutex query_lock;
+ int query_result;
+ char model[EXC3000_LEN_MODEL_NAME];
+ char fw_version[EXC3000_LEN_FW_VERSION];
};
static void exc3000_report_slots(struct input_dev *input,
@@ -58,10 +105,15 @@
input_sync(data->input);
}
-static int exc3000_read_frame(struct i2c_client *client, u8 *buf)
+static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
{
+ struct i2c_client *client = data->client;
+ u8 expected_event = EXC3000_MT1_EVENT;
int ret;
+ if (data->info->max_xy == SZ_16K - 1)
+ expected_event = EXC3000_MT2_EVENT;
+
ret = i2c_master_send(client, "'", 2);
if (ret < 0)
return ret;
@@ -76,19 +128,21 @@
if (ret != EXC3000_LEN_FRAME)
return -EIO;
- if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME ||
- buf[2] != EXC3000_MT_EVENT)
+ if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME)
+ return -EINVAL;
+
+ if (buf[2] != expected_event)
return -EINVAL;
return 0;
}
-static int exc3000_read_data(struct i2c_client *client,
+static int exc3000_read_data(struct exc3000_data *data,
u8 *buf, int *n_slots)
{
int error;
- error = exc3000_read_frame(client, buf);
+ error = exc3000_read_frame(data, buf);
if (error)
return error;
@@ -98,7 +152,7 @@
if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
/* Read 2nd frame to get the rest of the contacts. */
- error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME);
+ error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
if (error)
return error;
@@ -110,6 +164,28 @@
return 0;
}
+static int exc3000_query_interrupt(struct exc3000_data *data)
+{
+ u8 *buf = data->buf;
+ int error;
+
+ error = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME);
+ if (error < 0)
+ return error;
+
+ if (buf[0] != 'B')
+ return -EPROTO;
+
+ if (buf[4] == 'E')
+ strlcpy(data->model, buf + 5, sizeof(data->model));
+ else if (buf[4] == 'D')
+ strlcpy(data->fw_version, buf + 5, sizeof(data->fw_version));
+ else
+ return -EPROTO;
+
+ return 0;
+}
+
static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
{
struct exc3000_data *data = dev_id;
@@ -118,7 +194,13 @@
int slots, total_slots;
int error;
- error = exc3000_read_data(data->client, buf, &total_slots);
+ if (mutex_is_locked(&data->query_lock)) {
+ data->query_result = exc3000_query_interrupt(data);
+ complete(&data->wait_event);
+ goto out;
+ }
+
+ error = exc3000_read_data(data, buf, &total_slots);
if (error) {
/* Schedule a timer to release "stuck" contacts */
mod_timer(&data->timer,
@@ -145,31 +227,132 @@
return IRQ_HANDLED;
}
-static int exc3000_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static ssize_t fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct exc3000_data *data = i2c_get_clientdata(client);
+ static const u8 request[68] = {
+ 0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00
+ };
+ int error;
+
+ mutex_lock(&data->query_lock);
+
+ data->query_result = -ETIMEDOUT;
+ reinit_completion(&data->wait_event);
+
+ error = i2c_master_send(client, request, sizeof(request));
+ if (error < 0) {
+ mutex_unlock(&data->query_lock);
+ return error;
+ }
+
+ wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
+ mutex_unlock(&data->query_lock);
+
+ if (data->query_result < 0)
+ return data->query_result;
+
+ return sprintf(buf, "%s\n", data->fw_version);
+}
+static DEVICE_ATTR_RO(fw_version);
+
+static ssize_t exc3000_get_model(struct exc3000_data *data)
+{
+ static const u8 request[68] = {
+ 0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00
+ };
+ struct i2c_client *client = data->client;
+ int error;
+
+ mutex_lock(&data->query_lock);
+ data->query_result = -ETIMEDOUT;
+ reinit_completion(&data->wait_event);
+
+ error = i2c_master_send(client, request, sizeof(request));
+ if (error < 0) {
+ mutex_unlock(&data->query_lock);
+ return error;
+ }
+
+ wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
+ mutex_unlock(&data->query_lock);
+
+ return data->query_result;
+}
+
+static ssize_t model_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct exc3000_data *data = i2c_get_clientdata(client);
+ int error;
+
+ error = exc3000_get_model(data);
+ if (error < 0)
+ return error;
+
+ return sprintf(buf, "%s\n", data->model);
+}
+static DEVICE_ATTR_RO(model);
+
+static struct attribute *sysfs_attrs[] = {
+ &dev_attr_fw_version.attr,
+ &dev_attr_model.attr,
+ NULL
+};
+
+static struct attribute_group exc3000_attribute_group = {
+ .attrs = sysfs_attrs
+};
+
+static int exc3000_probe(struct i2c_client *client)
{
struct exc3000_data *data;
struct input_dev *input;
- int error;
+ int error, max_xy, retry;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
+ data->info = device_get_match_data(&client->dev);
+ if (!data->info) {
+ enum eeti_dev_id eeti_dev_id =
+ i2c_match_id(exc3000_id, client)->driver_data;
+ data->info = &exc3000_info[eeti_dev_id];
+ }
timer_setup(&data->timer, exc3000_timer, 0);
+ init_completion(&data->wait_event);
+ mutex_init(&data->query_lock);
+
+ data->reset = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(data->reset))
+ return PTR_ERR(data->reset);
+
+ if (data->reset) {
+ msleep(EXC3000_RESET_MS);
+ gpiod_set_value_cansleep(data->reset, 0);
+ msleep(EXC3000_READY_MS);
+ }
input = devm_input_allocate_device(&client->dev);
if (!input)
return -ENOMEM;
data->input = input;
+ input_set_drvdata(input, data);
- input->name = "EETI EXC3000 Touch Screen";
+ input->name = data->info->name;
input->id.bustype = BUS_I2C;
- input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0);
+ max_xy = data->info->max_xy;
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0);
+
touchscreen_parse_properties(input, true, &data->prop);
error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
@@ -187,18 +370,49 @@
if (error)
return error;
+ /*
+ * I²C does not have built-in recovery, so retry on failure. This
+ * ensures, that the device probe will not fail for temporary issues
+ * on the bus. This is not needed for the sysfs calls (userspace
+ * will receive the error code and can start another query) and
+ * cannot be done for touch events (but that only means loosing one
+ * or two touch events anyways).
+ */
+ for (retry = 0; retry < 3; retry++) {
+ error = exc3000_get_model(data);
+ if (!error)
+ break;
+ dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n",
+ retry + 1, error);
+ }
+
+ if (error)
+ return error;
+
+ dev_dbg(&client->dev, "TS Model: %s", data->model);
+
+ i2c_set_clientdata(client, data);
+
+ error = devm_device_add_group(&client->dev, &exc3000_attribute_group);
+ if (error)
+ return error;
+
return 0;
}
static const struct i2c_device_id exc3000_id[] = {
- { "exc3000", 0 },
+ { "exc3000", EETI_EXC3000 },
+ { "exc80h60", EETI_EXC80H60 },
+ { "exc80h84", EETI_EXC80H84 },
{ }
};
MODULE_DEVICE_TABLE(i2c, exc3000_id);
#ifdef CONFIG_OF
static const struct of_device_id exc3000_of_match[] = {
- { .compatible = "eeti,exc3000" },
+ { .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] },
+ { .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] },
+ { .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] },
{ }
};
MODULE_DEVICE_TABLE(of, exc3000_of_match);
@@ -210,7 +424,7 @@
.of_match_table = of_match_ptr(exc3000_of_match),
},
.id_table = exc3000_id,
- .probe = exc3000_probe,
+ .probe_new = exc3000_probe,
};
module_i2c_driver(exc3000_driver);
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index bfb945f..5fc789f 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -29,14 +29,68 @@
#include <linux/of.h>
#include <asm/unaligned.h>
+#define GOODIX_GPIO_INT_NAME "irq"
+#define GOODIX_GPIO_RST_NAME "reset"
+
+#define GOODIX_MAX_HEIGHT 4096
+#define GOODIX_MAX_WIDTH 4096
+#define GOODIX_INT_TRIGGER 1
+#define GOODIX_CONTACT_SIZE 8
+#define GOODIX_MAX_CONTACT_SIZE 9
+#define GOODIX_MAX_CONTACTS 10
+#define GOODIX_MAX_KEYS 7
+
+#define GOODIX_CONFIG_MIN_LENGTH 186
+#define GOODIX_CONFIG_911_LENGTH 186
+#define GOODIX_CONFIG_967_LENGTH 228
+#define GOODIX_CONFIG_GT9X_LENGTH 240
+#define GOODIX_CONFIG_MAX_LENGTH 240
+
+/* Register defines */
+#define GOODIX_REG_COMMAND 0x8040
+#define GOODIX_CMD_SCREEN_OFF 0x05
+
+#define GOODIX_READ_COOR_ADDR 0x814E
+#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050
+#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047
+#define GOODIX_REG_ID 0x8140
+
+#define GOODIX_BUFFER_STATUS_READY BIT(7)
+#define GOODIX_HAVE_KEY BIT(4)
+#define GOODIX_BUFFER_STATUS_TIMEOUT 20
+
+#define RESOLUTION_LOC 1
+#define MAX_CONTACTS_LOC 5
+#define TRIGGER_LOC 6
+
+/* Our special handling for GPIO accesses through ACPI is x86 specific */
+#if defined CONFIG_X86 && defined CONFIG_ACPI
+#define ACPI_GPIO_SUPPORT
+#endif
+
struct goodix_ts_data;
+enum goodix_irq_pin_access_method {
+ IRQ_PIN_ACCESS_NONE,
+ IRQ_PIN_ACCESS_GPIO,
+ IRQ_PIN_ACCESS_ACPI_GPIO,
+ IRQ_PIN_ACCESS_ACPI_METHOD,
+};
+
struct goodix_chip_data {
u16 config_addr;
int config_len;
- int (*check_config)(struct goodix_ts_data *, const struct firmware *);
+ int (*check_config)(struct goodix_ts_data *ts, const u8 *cfg, int len);
+ void (*calc_config_checksum)(struct goodix_ts_data *ts);
};
+struct goodix_chip_id {
+ const char *id;
+ const struct goodix_chip_data *data;
+};
+
+#define GOODIX_ID_MAX_LEN 4
+
struct goodix_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
@@ -48,71 +102,74 @@
struct regulator *vddio;
struct gpio_desc *gpiod_int;
struct gpio_desc *gpiod_rst;
- u16 id;
+ int gpio_count;
+ int gpio_int_idx;
+ char id[GOODIX_ID_MAX_LEN + 1];
u16 version;
const char *cfg_name;
+ bool reset_controller_at_probe;
+ bool load_cfg_from_disk;
struct completion firmware_loading_complete;
unsigned long irq_flags;
+ enum goodix_irq_pin_access_method irq_pin_access_method;
unsigned int contact_size;
+ u8 config[GOODIX_CONFIG_MAX_LENGTH];
+ unsigned short keymap[GOODIX_MAX_KEYS];
};
-#define GOODIX_GPIO_INT_NAME "irq"
-#define GOODIX_GPIO_RST_NAME "reset"
-
-#define GOODIX_MAX_HEIGHT 4096
-#define GOODIX_MAX_WIDTH 4096
-#define GOODIX_INT_TRIGGER 1
-#define GOODIX_CONTACT_SIZE 8
-#define GOODIX_MAX_CONTACT_SIZE 9
-#define GOODIX_MAX_CONTACTS 10
-
-#define GOODIX_CONFIG_MAX_LENGTH 240
-#define GOODIX_CONFIG_911_LENGTH 186
-#define GOODIX_CONFIG_967_LENGTH 228
-
-/* Register defines */
-#define GOODIX_REG_COMMAND 0x8040
-#define GOODIX_CMD_SCREEN_OFF 0x05
-
-#define GOODIX_READ_COOR_ADDR 0x814E
-#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050
-#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047
-#define GOODIX_REG_ID 0x8140
-
-#define GOODIX_BUFFER_STATUS_READY BIT(7)
-#define GOODIX_BUFFER_STATUS_TIMEOUT 20
-
-#define RESOLUTION_LOC 1
-#define MAX_CONTACTS_LOC 5
-#define TRIGGER_LOC 6
-
static int goodix_check_cfg_8(struct goodix_ts_data *ts,
- const struct firmware *cfg);
+ const u8 *cfg, int len);
static int goodix_check_cfg_16(struct goodix_ts_data *ts,
- const struct firmware *cfg);
+ const u8 *cfg, int len);
+static void goodix_calc_cfg_checksum_8(struct goodix_ts_data *ts);
+static void goodix_calc_cfg_checksum_16(struct goodix_ts_data *ts);
static const struct goodix_chip_data gt1x_chip_data = {
.config_addr = GOODIX_GT1X_REG_CONFIG_DATA,
- .config_len = GOODIX_CONFIG_MAX_LENGTH,
+ .config_len = GOODIX_CONFIG_GT9X_LENGTH,
.check_config = goodix_check_cfg_16,
+ .calc_config_checksum = goodix_calc_cfg_checksum_16,
};
static const struct goodix_chip_data gt911_chip_data = {
.config_addr = GOODIX_GT9X_REG_CONFIG_DATA,
.config_len = GOODIX_CONFIG_911_LENGTH,
.check_config = goodix_check_cfg_8,
+ .calc_config_checksum = goodix_calc_cfg_checksum_8,
};
static const struct goodix_chip_data gt967_chip_data = {
.config_addr = GOODIX_GT9X_REG_CONFIG_DATA,
.config_len = GOODIX_CONFIG_967_LENGTH,
.check_config = goodix_check_cfg_8,
+ .calc_config_checksum = goodix_calc_cfg_checksum_8,
};
static const struct goodix_chip_data gt9x_chip_data = {
.config_addr = GOODIX_GT9X_REG_CONFIG_DATA,
- .config_len = GOODIX_CONFIG_MAX_LENGTH,
+ .config_len = GOODIX_CONFIG_GT9X_LENGTH,
.check_config = goodix_check_cfg_8,
+ .calc_config_checksum = goodix_calc_cfg_checksum_8,
+};
+
+static const struct goodix_chip_id goodix_chip_ids[] = {
+ { .id = "1151", .data = >1x_chip_data },
+ { .id = "5663", .data = >1x_chip_data },
+ { .id = "5688", .data = >1x_chip_data },
+ { .id = "917S", .data = >1x_chip_data },
+ { .id = "9286", .data = >1x_chip_data },
+
+ { .id = "911", .data = >911_chip_data },
+ { .id = "9271", .data = >911_chip_data },
+ { .id = "9110", .data = >911_chip_data },
+ { .id = "9111", .data = >911_chip_data },
+ { .id = "927", .data = >911_chip_data },
+ { .id = "928", .data = >911_chip_data },
+
+ { .id = "912", .data = >967_chip_data },
+ { .id = "9147", .data = >967_chip_data },
+ { .id = "967", .data = >967_chip_data },
+ { }
};
static const unsigned long goodix_irq_flags[] = {
@@ -122,51 +179,6 @@
IRQ_TYPE_LEVEL_HIGH,
};
-/*
- * Those tablets have their coordinates origin at the bottom right
- * of the tablet, as if rotated 180 degrees
- */
-static const struct dmi_system_id rotated_screen[] = {
-#if defined(CONFIG_DMI) && defined(CONFIG_X86)
- {
- .ident = "Teclast X89",
- .matches = {
- /* tPAD is too generic, also match on bios date */
- DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
- DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
- DMI_MATCH(DMI_BIOS_DATE, "12/19/2014"),
- },
- },
- {
- .ident = "Teclast X98 Pro",
- .matches = {
- /*
- * Only match BIOS date, because the manufacturers
- * BIOS does not report the board name at all
- * (sometimes)...
- */
- DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
- DMI_MATCH(DMI_BIOS_DATE, "10/28/2015"),
- },
- },
- {
- .ident = "WinBook TW100",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
- DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
- }
- },
- {
- .ident = "WinBook TW700",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
- DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
- },
- },
-#endif
- {}
-};
-
static const struct dmi_system_id nine_bytes_report[] = {
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
@@ -263,28 +275,16 @@
return goodix_i2c_write(client, reg, &value, sizeof(value));
}
-static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
+static const struct goodix_chip_data *goodix_get_chip_data(const char *id)
{
- switch (id) {
- case 1151:
- case 5663:
- case 5688:
- return >1x_chip_data;
+ unsigned int i;
- case 911:
- case 9271:
- case 9110:
- case 927:
- case 928:
- return >911_chip_data;
-
- case 912:
- case 967:
- return >967_chip_data;
-
- default:
- return >9x_chip_data;
+ for (i = 0; goodix_chip_ids[i].id; i++) {
+ if (!strcmp(goodix_chip_ids[i].id, id))
+ return goodix_chip_ids[i].data;
}
+
+ return >9x_chip_data;
}
static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
@@ -292,6 +292,13 @@
unsigned long max_timeout;
int touch_num;
int error;
+ u16 addr = GOODIX_READ_COOR_ADDR;
+ /*
+ * We are going to read 1-byte header,
+ * ts->contact_size * max(1, touch_num) bytes of coordinates
+ * and 1-byte footer which contains the touch-key code.
+ */
+ const int header_contact_keycode_size = 1 + ts->contact_size + 1;
/*
* The 'buffer status' bit, which indicates that the data is valid, is
@@ -300,8 +307,8 @@
*/
max_timeout = jiffies + msecs_to_jiffies(GOODIX_BUFFER_STATUS_TIMEOUT);
do {
- error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR,
- data, ts->contact_size + 1);
+ error = goodix_i2c_read(ts->client, addr, data,
+ header_contact_keycode_size);
if (error) {
dev_err(&ts->client->dev, "I2C transfer error: %d\n",
error);
@@ -314,11 +321,10 @@
return -EPROTO;
if (touch_num > 1) {
- data += 1 + ts->contact_size;
+ addr += header_contact_keycode_size;
+ data += header_contact_keycode_size;
error = goodix_i2c_read(ts->client,
- GOODIX_READ_COOR_ADDR +
- 1 + ts->contact_size,
- data,
+ addr, data,
ts->contact_size *
(touch_num - 1));
if (error)
@@ -335,7 +341,7 @@
* The Goodix panel will send spurious interrupts after a
* 'finger up' event, which will always cause a timeout.
*/
- return 0;
+ return -ENOMSG;
}
static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data)
@@ -368,6 +374,25 @@
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
}
+static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data)
+{
+ int touch_num;
+ u8 key_value;
+ int i;
+
+ if (data[0] & GOODIX_HAVE_KEY) {
+ touch_num = data[0] & 0x0f;
+ key_value = data[1 + ts->contact_size * touch_num];
+ for (i = 0; i < GOODIX_MAX_KEYS; i++)
+ if (key_value & BIT(i))
+ input_report_key(ts->input_dev,
+ ts->keymap[i], 1);
+ } else {
+ for (i = 0; i < GOODIX_MAX_KEYS; i++)
+ input_report_key(ts->input_dev, ts->keymap[i], 0);
+ }
+}
+
/**
* goodix_process_events - Process incoming events
*
@@ -378,7 +403,7 @@
*/
static void goodix_process_events(struct goodix_ts_data *ts)
{
- u8 point_data[1 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
+ u8 point_data[2 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
int touch_num;
int i;
@@ -386,11 +411,7 @@
if (touch_num < 0)
return;
- /*
- * Bit 4 of the first byte reports the status of the capacitive
- * Windows/Home button.
- */
- input_report_key(ts->input_dev, KEY_LEFTMETA, point_data[0] & BIT(4));
+ goodix_ts_report_key(ts, point_data);
for (i = 0; i < touch_num; i++)
if (ts->contact_size == 9)
@@ -434,22 +455,21 @@
ts->irq_flags, ts->client->name, ts);
}
-static int goodix_check_cfg_8(struct goodix_ts_data *ts,
- const struct firmware *cfg)
+static int goodix_check_cfg_8(struct goodix_ts_data *ts, const u8 *cfg, int len)
{
- int i, raw_cfg_len = cfg->size - 2;
+ int i, raw_cfg_len = len - 2;
u8 check_sum = 0;
for (i = 0; i < raw_cfg_len; i++)
- check_sum += cfg->data[i];
+ check_sum += cfg[i];
check_sum = (~check_sum) + 1;
- if (check_sum != cfg->data[raw_cfg_len]) {
+ if (check_sum != cfg[raw_cfg_len]) {
dev_err(&ts->client->dev,
"The checksum of the config fw is not correct");
return -EINVAL;
}
- if (cfg->data[raw_cfg_len + 1] != 1) {
+ if (cfg[raw_cfg_len + 1] != 1) {
dev_err(&ts->client->dev,
"Config fw must have Config_Fresh register set");
return -EINVAL;
@@ -458,22 +478,35 @@
return 0;
}
-static int goodix_check_cfg_16(struct goodix_ts_data *ts,
- const struct firmware *cfg)
+static void goodix_calc_cfg_checksum_8(struct goodix_ts_data *ts)
{
- int i, raw_cfg_len = cfg->size - 3;
+ int i, raw_cfg_len = ts->chip->config_len - 2;
+ u8 check_sum = 0;
+
+ for (i = 0; i < raw_cfg_len; i++)
+ check_sum += ts->config[i];
+ check_sum = (~check_sum) + 1;
+
+ ts->config[raw_cfg_len] = check_sum;
+ ts->config[raw_cfg_len + 1] = 1; /* Set "config_fresh" bit */
+}
+
+static int goodix_check_cfg_16(struct goodix_ts_data *ts, const u8 *cfg,
+ int len)
+{
+ int i, raw_cfg_len = len - 3;
u16 check_sum = 0;
for (i = 0; i < raw_cfg_len; i += 2)
- check_sum += get_unaligned_be16(&cfg->data[i]);
+ check_sum += get_unaligned_be16(&cfg[i]);
check_sum = (~check_sum) + 1;
- if (check_sum != get_unaligned_be16(&cfg->data[raw_cfg_len])) {
+ if (check_sum != get_unaligned_be16(&cfg[raw_cfg_len])) {
dev_err(&ts->client->dev,
"The checksum of the config fw is not correct");
return -EINVAL;
}
- if (cfg->data[raw_cfg_len + 2] != 1) {
+ if (cfg[raw_cfg_len + 2] != 1) {
dev_err(&ts->client->dev,
"Config fw must have Config_Fresh register set");
return -EINVAL;
@@ -482,22 +515,35 @@
return 0;
}
+static void goodix_calc_cfg_checksum_16(struct goodix_ts_data *ts)
+{
+ int i, raw_cfg_len = ts->chip->config_len - 3;
+ u16 check_sum = 0;
+
+ for (i = 0; i < raw_cfg_len; i += 2)
+ check_sum += get_unaligned_be16(&ts->config[i]);
+ check_sum = (~check_sum) + 1;
+
+ put_unaligned_be16(check_sum, &ts->config[raw_cfg_len]);
+ ts->config[raw_cfg_len + 2] = 1; /* Set "config_fresh" bit */
+}
+
/**
* goodix_check_cfg - Checks if config fw is valid
*
* @ts: goodix_ts_data pointer
* @cfg: firmware config data
*/
-static int goodix_check_cfg(struct goodix_ts_data *ts,
- const struct firmware *cfg)
+static int goodix_check_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
{
- if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {
+ if (len < GOODIX_CONFIG_MIN_LENGTH ||
+ len > GOODIX_CONFIG_MAX_LENGTH) {
dev_err(&ts->client->dev,
"The length of the config fw is not correct");
return -EINVAL;
}
- return ts->chip->check_config(ts, cfg);
+ return ts->chip->check_config(ts, cfg, len);
}
/**
@@ -506,17 +552,15 @@
* @ts: goodix_ts_data pointer
* @cfg: config firmware to write to device
*/
-static int goodix_send_cfg(struct goodix_ts_data *ts,
- const struct firmware *cfg)
+static int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
{
int error;
- error = goodix_check_cfg(ts, cfg);
+ error = goodix_check_cfg(ts, cfg, len);
if (error)
return error;
- error = goodix_i2c_write(ts->client, ts->chip->config_addr, cfg->data,
- cfg->size);
+ error = goodix_i2c_write(ts->client, ts->chip->config_addr, cfg, len);
if (error) {
dev_err(&ts->client->dev, "Failed to write config data: %d",
error);
@@ -530,17 +574,93 @@
return 0;
}
+#ifdef ACPI_GPIO_SUPPORT
+static int goodix_pin_acpi_direction_input(struct goodix_ts_data *ts)
+{
+ acpi_handle handle = ACPI_HANDLE(&ts->client->dev);
+ acpi_status status;
+
+ status = acpi_evaluate_object(handle, "INTI", NULL, NULL);
+ return ACPI_SUCCESS(status) ? 0 : -EIO;
+}
+
+static int goodix_pin_acpi_output_method(struct goodix_ts_data *ts, int value)
+{
+ acpi_handle handle = ACPI_HANDLE(&ts->client->dev);
+ acpi_status status;
+
+ status = acpi_execute_simple_method(handle, "INTO", value);
+ return ACPI_SUCCESS(status) ? 0 : -EIO;
+}
+#else
+static int goodix_pin_acpi_direction_input(struct goodix_ts_data *ts)
+{
+ dev_err(&ts->client->dev,
+ "%s called on device without ACPI support\n", __func__);
+ return -EINVAL;
+}
+
+static int goodix_pin_acpi_output_method(struct goodix_ts_data *ts, int value)
+{
+ dev_err(&ts->client->dev,
+ "%s called on device without ACPI support\n", __func__);
+ return -EINVAL;
+}
+#endif
+
+static int goodix_irq_direction_output(struct goodix_ts_data *ts, int value)
+{
+ switch (ts->irq_pin_access_method) {
+ case IRQ_PIN_ACCESS_NONE:
+ dev_err(&ts->client->dev,
+ "%s called without an irq_pin_access_method set\n",
+ __func__);
+ return -EINVAL;
+ case IRQ_PIN_ACCESS_GPIO:
+ return gpiod_direction_output(ts->gpiod_int, value);
+ case IRQ_PIN_ACCESS_ACPI_GPIO:
+ /*
+ * The IRQ pin triggers on a falling edge, so its gets marked
+ * as active-low, use output_raw to avoid the value inversion.
+ */
+ return gpiod_direction_output_raw(ts->gpiod_int, value);
+ case IRQ_PIN_ACCESS_ACPI_METHOD:
+ return goodix_pin_acpi_output_method(ts, value);
+ }
+
+ return -EINVAL; /* Never reached */
+}
+
+static int goodix_irq_direction_input(struct goodix_ts_data *ts)
+{
+ switch (ts->irq_pin_access_method) {
+ case IRQ_PIN_ACCESS_NONE:
+ dev_err(&ts->client->dev,
+ "%s called without an irq_pin_access_method set\n",
+ __func__);
+ return -EINVAL;
+ case IRQ_PIN_ACCESS_GPIO:
+ return gpiod_direction_input(ts->gpiod_int);
+ case IRQ_PIN_ACCESS_ACPI_GPIO:
+ return gpiod_direction_input(ts->gpiod_int);
+ case IRQ_PIN_ACCESS_ACPI_METHOD:
+ return goodix_pin_acpi_direction_input(ts);
+ }
+
+ return -EINVAL; /* Never reached */
+}
+
static int goodix_int_sync(struct goodix_ts_data *ts)
{
int error;
- error = gpiod_direction_output(ts->gpiod_int, 0);
+ error = goodix_irq_direction_output(ts, 0);
if (error)
return error;
msleep(50); /* T5: 50ms */
- error = gpiod_direction_input(ts->gpiod_int);
+ error = goodix_irq_direction_input(ts);
if (error)
return error;
@@ -564,7 +684,7 @@
msleep(20); /* T2: > 10ms */
/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
- error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
+ error = goodix_irq_direction_output(ts, ts->client->addr == 0x14);
if (error)
return error;
@@ -588,6 +708,124 @@
return 0;
}
+#ifdef ACPI_GPIO_SUPPORT
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+static const struct x86_cpu_id baytrail_cpu_ids[] = {
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT, X86_FEATURE_ANY, },
+ {}
+};
+
+static inline bool is_byt(void)
+{
+ const struct x86_cpu_id *id = x86_match_cpu(baytrail_cpu_ids);
+
+ return !!id;
+}
+
+static const struct acpi_gpio_params first_gpio = { 0, 0, false };
+static const struct acpi_gpio_params second_gpio = { 1, 0, false };
+
+static const struct acpi_gpio_mapping acpi_goodix_int_first_gpios[] = {
+ { GOODIX_GPIO_INT_NAME "-gpios", &first_gpio, 1 },
+ { GOODIX_GPIO_RST_NAME "-gpios", &second_gpio, 1 },
+ { },
+};
+
+static const struct acpi_gpio_mapping acpi_goodix_int_last_gpios[] = {
+ { GOODIX_GPIO_RST_NAME "-gpios", &first_gpio, 1 },
+ { GOODIX_GPIO_INT_NAME "-gpios", &second_gpio, 1 },
+ { },
+};
+
+static const struct acpi_gpio_mapping acpi_goodix_reset_only_gpios[] = {
+ { GOODIX_GPIO_RST_NAME "-gpios", &first_gpio, 1 },
+ { },
+};
+
+static int goodix_resource(struct acpi_resource *ares, void *data)
+{
+ struct goodix_ts_data *ts = data;
+ struct device *dev = &ts->client->dev;
+ struct acpi_resource_gpio *gpio;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_GPIO:
+ gpio = &ares->data.gpio;
+ if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) {
+ if (ts->gpio_int_idx == -1) {
+ ts->gpio_int_idx = ts->gpio_count;
+ } else {
+ dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n");
+ ts->gpio_int_idx = -2;
+ }
+ }
+ ts->gpio_count++;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * This function gets called in case we fail to get the irq GPIO directly
+ * because the ACPI tables lack GPIO-name to APCI _CRS index mappings
+ * (no _DSD UUID daffd814-6eba-4d8c-8a91-bc9bbf4aa301 data).
+ * In that case we add our own mapping and then goodix_get_gpio_config()
+ * retries to get the GPIOs based on the added mapping.
+ */
+static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)
+{
+ const struct acpi_gpio_mapping *gpio_mapping = NULL;
+ struct device *dev = &ts->client->dev;
+ LIST_HEAD(resources);
+ int ret;
+
+ ts->gpio_count = 0;
+ ts->gpio_int_idx = -1;
+ ret = acpi_dev_get_resources(ACPI_COMPANION(dev), &resources,
+ goodix_resource, ts);
+ if (ret < 0) {
+ dev_err(dev, "Error getting ACPI resources: %d\n", ret);
+ return ret;
+ }
+
+ acpi_dev_free_resource_list(&resources);
+
+ if (ts->gpio_count == 2 && ts->gpio_int_idx == 0) {
+ ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
+ gpio_mapping = acpi_goodix_int_first_gpios;
+ } else if (ts->gpio_count == 2 && ts->gpio_int_idx == 1) {
+ ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
+ gpio_mapping = acpi_goodix_int_last_gpios;
+ } else if (ts->gpio_count == 1 && ts->gpio_int_idx == -1 &&
+ acpi_has_method(ACPI_HANDLE(dev), "INTI") &&
+ acpi_has_method(ACPI_HANDLE(dev), "INTO")) {
+ dev_info(dev, "Using ACPI INTI and INTO methods for IRQ pin access\n");
+ ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_METHOD;
+ gpio_mapping = acpi_goodix_reset_only_gpios;
+ } else if (is_byt() && ts->gpio_count == 2 && ts->gpio_int_idx == -1) {
+ dev_info(dev, "No ACPI GpioInt resource, assuming that the GPIO order is reset, int\n");
+ ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
+ gpio_mapping = acpi_goodix_int_last_gpios;
+ } else {
+ dev_warn(dev, "Unexpected ACPI resources: gpio_count %d, gpio_int_idx %d\n",
+ ts->gpio_count, ts->gpio_int_idx);
+ return -EINVAL;
+ }
+
+ return devm_acpi_dev_add_driver_gpios(dev, gpio_mapping);
+}
+#else
+static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_X86 && CONFIG_ACPI */
+
/**
* goodix_get_gpio_config - Get GPIO config from ACPI/DT
*
@@ -598,6 +836,7 @@
int error;
struct device *dev;
struct gpio_desc *gpiod;
+ bool added_acpi_mappings = false;
if (!ts->client)
return -EINVAL;
@@ -621,6 +860,7 @@
return error;
}
+retry_get_irq_gpio:
/* Get the interrupt GPIO pin number */
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
if (IS_ERR(gpiod)) {
@@ -630,6 +870,11 @@
GOODIX_GPIO_INT_NAME, error);
return error;
}
+ if (!gpiod && has_acpi_companion(dev) && !added_acpi_mappings) {
+ added_acpi_mappings = true;
+ if (goodix_add_acpi_gpio_mappings(ts) == 0)
+ goto retry_get_irq_gpio;
+ }
ts->gpiod_int = gpiod;
@@ -645,6 +890,31 @@
ts->gpiod_rst = gpiod;
+ switch (ts->irq_pin_access_method) {
+ case IRQ_PIN_ACCESS_ACPI_GPIO:
+ /*
+ * We end up here if goodix_add_acpi_gpio_mappings() has
+ * called devm_acpi_dev_add_driver_gpios() because the ACPI
+ * tables did not contain name to index mappings.
+ * Check that we successfully got both GPIOs after we've
+ * added our own acpi_gpio_mapping and if we did not get both
+ * GPIOs reset irq_pin_access_method to IRQ_PIN_ACCESS_NONE.
+ */
+ if (!ts->gpiod_int || !ts->gpiod_rst)
+ ts->irq_pin_access_method = IRQ_PIN_ACCESS_NONE;
+ break;
+ case IRQ_PIN_ACCESS_ACPI_METHOD:
+ if (!ts->gpiod_rst)
+ ts->irq_pin_access_method = IRQ_PIN_ACCESS_NONE;
+ break;
+ default:
+ if (ts->gpiod_int && ts->gpiod_rst) {
+ ts->reset_controller_at_probe = true;
+ ts->load_cfg_from_disk = true;
+ ts->irq_pin_access_method = IRQ_PIN_ACCESS_GPIO;
+ }
+ }
+
return 0;
}
@@ -657,12 +927,11 @@
*/
static void goodix_read_config(struct goodix_ts_data *ts)
{
- u8 config[GOODIX_CONFIG_MAX_LENGTH];
int x_max, y_max;
int error;
error = goodix_i2c_read(ts->client, ts->chip->config_addr,
- config, ts->chip->config_len);
+ ts->config, ts->chip->config_len);
if (error) {
dev_warn(&ts->client->dev, "Error reading config: %d\n",
error);
@@ -671,15 +940,17 @@
return;
}
- ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
- ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
+ ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03;
+ ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f;
- x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
- y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
+ x_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC]);
+ y_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC + 2]);
if (x_max && y_max) {
input_abs_set_max(ts->input_dev, ABS_MT_POSITION_X, x_max - 1);
input_abs_set_max(ts->input_dev, ABS_MT_POSITION_Y, y_max - 1);
}
+
+ ts->chip->calc_config_checksum(ts);
}
/**
@@ -691,7 +962,7 @@
{
int error;
u8 buf[6];
- char id_str[5];
+ char id_str[GOODIX_ID_MAX_LEN + 1];
error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
if (error) {
@@ -699,14 +970,13 @@
return error;
}
- memcpy(id_str, buf, 4);
- id_str[4] = 0;
- if (kstrtou16(id_str, 10, &ts->id))
- ts->id = 0x1001;
+ memcpy(id_str, buf, GOODIX_ID_MAX_LEN);
+ id_str[GOODIX_ID_MAX_LEN] = 0;
+ strscpy(ts->id, id_str, GOODIX_ID_MAX_LEN + 1);
ts->version = get_unaligned_le16(&buf[4]);
- dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id,
+ dev_info(&ts->client->dev, "ID %s, version: %04x\n", ts->id,
ts->version);
return 0;
@@ -750,6 +1020,7 @@
static int goodix_configure_dev(struct goodix_ts_data *ts)
{
int error;
+ int i;
ts->int_trigger_type = GOODIX_INT_TRIGGER;
ts->max_touch_num = GOODIX_MAX_CONTACTS;
@@ -764,11 +1035,23 @@
ts->input_dev->phys = "input/ts";
ts->input_dev->id.bustype = BUS_I2C;
ts->input_dev->id.vendor = 0x0416;
- ts->input_dev->id.product = ts->id;
+ if (kstrtou16(ts->id, 10, &ts->input_dev->id.product))
+ ts->input_dev->id.product = 0x1001;
ts->input_dev->id.version = ts->version;
+ ts->input_dev->keycode = ts->keymap;
+ ts->input_dev->keycodesize = sizeof(ts->keymap[0]);
+ ts->input_dev->keycodemax = GOODIX_MAX_KEYS;
+
/* Capacitive Windows/Home button on some devices */
- input_set_capability(ts->input_dev, EV_KEY, KEY_LEFTMETA);
+ for (i = 0; i < GOODIX_MAX_KEYS; ++i) {
+ if (i == 0)
+ ts->keymap[i] = KEY_LEFTMETA;
+ else
+ ts->keymap[i] = KEY_F1 + (i - 1);
+
+ input_set_capability(ts->input_dev, EV_KEY, ts->keymap[i]);
+ }
input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y);
@@ -794,13 +1077,6 @@
ABS_MT_POSITION_Y, ts->prop.max_y);
}
- if (dmi_check_system(rotated_screen)) {
- ts->prop.invert_x = true;
- ts->prop.invert_y = true;
- dev_dbg(&ts->client->dev,
- "Applying '180 degrees rotated screen' quirk\n");
- }
-
if (dmi_check_system(nine_bytes_report)) {
ts->contact_size = 9;
@@ -854,7 +1130,7 @@
if (cfg) {
/* send device configuration to the firmware */
- error = goodix_send_cfg(ts, cfg);
+ error = goodix_send_cfg(ts, cfg->data, cfg->size);
if (error)
goto err_release_cfg;
}
@@ -923,7 +1199,8 @@
if (error)
return error;
- if (ts->gpiod_int && ts->gpiod_rst) {
+reset:
+ if (ts->reset_controller_at_probe) {
/* reset the controller */
error = goodix_reset(ts);
if (error) {
@@ -934,6 +1211,12 @@
error = goodix_i2c_test(client);
if (error) {
+ if (!ts->reset_controller_at_probe &&
+ ts->irq_pin_access_method != IRQ_PIN_ACCESS_NONE) {
+ /* Retry after a controller reset */
+ ts->reset_controller_at_probe = true;
+ goto reset;
+ }
dev_err(&client->dev, "I2C communication failure: %d\n", error);
return error;
}
@@ -946,10 +1229,10 @@
ts->chip = goodix_get_chip_data(ts->id);
- if (ts->gpiod_int && ts->gpiod_rst) {
+ if (ts->load_cfg_from_disk) {
/* update device config */
ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,
- "goodix_%d_cfg.bin", ts->id);
+ "goodix_%s_cfg.bin", ts->id);
if (!ts->cfg_name)
return -ENOMEM;
@@ -977,7 +1260,7 @@
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
- if (ts->gpiod_int && ts->gpiod_rst)
+ if (ts->load_cfg_from_disk)
wait_for_completion(&ts->firmware_loading_complete);
return 0;
@@ -989,19 +1272,20 @@
struct goodix_ts_data *ts = i2c_get_clientdata(client);
int error;
+ if (ts->load_cfg_from_disk)
+ wait_for_completion(&ts->firmware_loading_complete);
+
/* We need gpio pins to suspend/resume */
- if (!ts->gpiod_int || !ts->gpiod_rst) {
+ if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) {
disable_irq(client->irq);
return 0;
}
- wait_for_completion(&ts->firmware_loading_complete);
-
/* Free IRQ as IRQ pin is used as output in the suspend sequence */
goodix_free_irq(ts);
/* Output LOW on the INT pin for 5 ms */
- error = gpiod_direction_output(ts->gpiod_int, 0);
+ error = goodix_irq_direction_output(ts, 0);
if (error) {
goodix_request_irq(ts);
return error;
@@ -1013,7 +1297,7 @@
GOODIX_CMD_SCREEN_OFF);
if (error) {
dev_err(&ts->client->dev, "Screen off command failed\n");
- gpiod_direction_input(ts->gpiod_int);
+ goodix_irq_direction_input(ts);
goodix_request_irq(ts);
return -EAGAIN;
}
@@ -1031,9 +1315,10 @@
{
struct i2c_client *client = to_i2c_client(dev);
struct goodix_ts_data *ts = i2c_get_clientdata(client);
+ u8 config_ver;
int error;
- if (!ts->gpiod_int || !ts->gpiod_rst) {
+ if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) {
enable_irq(client->irq);
return 0;
}
@@ -1042,7 +1327,7 @@
* Exit sleep mode by outputting HIGH level to INT pin
* for 2ms~5ms.
*/
- error = gpiod_direction_output(ts->gpiod_int, 1);
+ error = goodix_irq_direction_output(ts, 1);
if (error)
return error;
@@ -1052,6 +1337,27 @@
if (error)
return error;
+ error = goodix_i2c_read(ts->client, ts->chip->config_addr,
+ &config_ver, 1);
+ if (error)
+ dev_warn(dev, "Error reading config version: %d, resetting controller\n",
+ error);
+ else if (config_ver != ts->config[0])
+ dev_info(dev, "Config version mismatch %d != %d, resetting controller\n",
+ config_ver, ts->config[0]);
+
+ if (error != 0 || config_ver != ts->config[0]) {
+ error = goodix_reset(ts);
+ if (error) {
+ dev_err(dev, "Controller reset failed.\n");
+ return error;
+ }
+
+ error = goodix_send_cfg(ts, ts->config, ts->chip->config_len);
+ if (error)
+ return error;
+ }
+
error = goodix_request_irq(ts);
if (error)
return error;
@@ -1084,9 +1390,12 @@
{ .compatible = "goodix,gt911" },
{ .compatible = "goodix,gt9110" },
{ .compatible = "goodix,gt912" },
+ { .compatible = "goodix,gt9147" },
+ { .compatible = "goodix,gt917s" },
{ .compatible = "goodix,gt927" },
{ .compatible = "goodix,gt9271" },
{ .compatible = "goodix,gt928" },
+ { .compatible = "goodix,gt9286" },
{ .compatible = "goodix,gt967" },
{ }
};
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
index 22839dd..30576a5 100644
--- a/drivers/input/touchscreen/ili210x.c
+++ b/drivers/input/touchscreen/ili210x.c
@@ -1,54 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
-#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
-#include <linux/delay.h>
-#include <linux/workqueue.h>
-#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
#include <asm/unaligned.h>
-#define ILI210X_TOUCHES 2
-#define ILI251X_TOUCHES 10
-#define DEFAULT_POLL_PERIOD 20
+#define ILI2XXX_POLL_PERIOD 20
+
+#define ILI210X_DATA_SIZE 64
+#define ILI211X_DATA_SIZE 43
+#define ILI251X_DATA_SIZE1 31
+#define ILI251X_DATA_SIZE2 20
/* Touchscreen commands */
#define REG_TOUCHDATA 0x10
#define REG_PANEL_INFO 0x20
-#define REG_FIRMWARE_VERSION 0x40
#define REG_CALIBRATE 0xcc
-struct firmware_version {
- u8 id;
- u8 major;
- u8 minor;
-} __packed;
-
-enum ili2xxx_model {
- MODEL_ILI210X,
- MODEL_ILI251X,
+struct ili2xxx_chip {
+ int (*read_reg)(struct i2c_client *client, u8 reg,
+ void *buf, size_t len);
+ int (*get_touch_data)(struct i2c_client *client, u8 *data);
+ bool (*parse_touch_data)(const u8 *data, unsigned int finger,
+ unsigned int *x, unsigned int *y,
+ unsigned int *z);
+ bool (*continue_polling)(const u8 *data, bool touch);
+ unsigned int max_touches;
+ unsigned int resolution;
+ bool has_calibrate_reg;
+ bool has_pressure_reg;
};
struct ili210x {
struct i2c_client *client;
struct input_dev *input;
- unsigned int poll_period;
- struct delayed_work dwork;
struct gpio_desc *reset_gpio;
struct touchscreen_properties prop;
- enum ili2xxx_model model;
- unsigned int max_touches;
+ const struct ili2xxx_chip *chip;
+ bool stop;
};
-static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
- size_t len)
+static int ili210x_read_reg(struct i2c_client *client,
+ u8 reg, void *buf, size_t len)
{
- struct ili210x *priv = i2c_get_clientdata(client);
- struct i2c_msg msg[2] = {
+ struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
@@ -62,53 +64,29 @@
.buf = buf,
}
};
+ int error, ret;
- if (priv->model == MODEL_ILI251X) {
- if (i2c_transfer(client->adapter, msg, 1) != 1) {
- dev_err(&client->dev, "i2c transfer failed\n");
- return -EIO;
- }
-
- usleep_range(5000, 5500);
-
- if (i2c_transfer(client->adapter, msg + 1, 1) != 1) {
- dev_err(&client->dev, "i2c transfer failed\n");
- return -EIO;
- }
- } else {
- if (i2c_transfer(client->adapter, msg, 2) != 2) {
- dev_err(&client->dev, "i2c transfer failed\n");
- return -EIO;
- }
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev, "%s failed: %d\n", __func__, error);
+ return error;
}
return 0;
}
-static int ili210x_read(struct i2c_client *client, void *buf, size_t len)
+static int ili210x_read_touch_data(struct i2c_client *client, u8 *data)
{
- struct i2c_msg msg = {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = buf,
- };
-
- if (i2c_transfer(client->adapter, &msg, 1) != 1) {
- dev_err(&client->dev, "i2c transfer failed\n");
- return -EIO;
- }
-
- return 0;
+ return ili210x_read_reg(client, REG_TOUCHDATA,
+ data, ILI210X_DATA_SIZE);
}
-static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
+static bool ili210x_touchdata_to_coords(const u8 *touchdata,
unsigned int finger,
- unsigned int *x, unsigned int *y)
+ unsigned int *x, unsigned int *y,
+ unsigned int *z)
{
- if (finger >= ILI210X_TOUCHES)
- return false;
-
if (!(touchdata[0] & BIT(finger)))
return false;
@@ -118,95 +96,229 @@
return true;
}
-static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
- unsigned int finger,
- unsigned int *x, unsigned int *y)
+static bool ili210x_check_continue_polling(const u8 *data, bool touch)
{
- if (finger >= ILI251X_TOUCHES)
+ return data[0] & 0xf3;
+}
+
+static const struct ili2xxx_chip ili210x_chip = {
+ .read_reg = ili210x_read_reg,
+ .get_touch_data = ili210x_read_touch_data,
+ .parse_touch_data = ili210x_touchdata_to_coords,
+ .continue_polling = ili210x_check_continue_polling,
+ .max_touches = 2,
+ .has_calibrate_reg = true,
+};
+
+static int ili211x_read_touch_data(struct i2c_client *client, u8 *data)
+{
+ s16 sum = 0;
+ int error;
+ int ret;
+ int i;
+
+ ret = i2c_master_recv(client, data, ILI211X_DATA_SIZE);
+ if (ret != ILI211X_DATA_SIZE) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev, "%s failed: %d\n", __func__, error);
+ return error;
+ }
+
+ /* This chip uses custom checksum at the end of data */
+ for (i = 0; i < ILI211X_DATA_SIZE - 1; i++)
+ sum = (sum + data[i]) & 0xff;
+
+ if ((-sum & 0xff) != data[ILI211X_DATA_SIZE - 1]) {
+ dev_err(&client->dev,
+ "CRC error (crc=0x%02x expected=0x%02x)\n",
+ sum, data[ILI211X_DATA_SIZE - 1]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static bool ili211x_touchdata_to_coords(const u8 *touchdata,
+ unsigned int finger,
+ unsigned int *x, unsigned int *y,
+ unsigned int *z)
+{
+ u32 data;
+
+ data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0);
+ if (data == 0xffffffff) /* Finger up */
return false;
- *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
- if (!(*x & BIT(15))) /* Touch indication */
- return false;
-
- *x &= 0x3fff;
- *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
+ *x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) |
+ touchdata[1 + (finger * 4) + 1];
+ *y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) |
+ touchdata[1 + (finger * 4) + 2];
return true;
}
+static bool ili211x_decline_polling(const u8 *data, bool touch)
+{
+ return false;
+}
+
+static const struct ili2xxx_chip ili211x_chip = {
+ .read_reg = ili210x_read_reg,
+ .get_touch_data = ili211x_read_touch_data,
+ .parse_touch_data = ili211x_touchdata_to_coords,
+ .continue_polling = ili211x_decline_polling,
+ .max_touches = 10,
+ .resolution = 2048,
+};
+
+static bool ili212x_touchdata_to_coords(const u8 *touchdata,
+ unsigned int finger,
+ unsigned int *x, unsigned int *y,
+ unsigned int *z)
+{
+ u16 val;
+
+ val = get_unaligned_be16(touchdata + 3 + (finger * 5) + 0);
+ if (!(val & BIT(15))) /* Touch indication */
+ return false;
+
+ *x = val & 0x3fff;
+ *y = get_unaligned_be16(touchdata + 3 + (finger * 5) + 2);
+
+ return true;
+}
+
+static bool ili212x_check_continue_polling(const u8 *data, bool touch)
+{
+ return touch;
+}
+
+static const struct ili2xxx_chip ili212x_chip = {
+ .read_reg = ili210x_read_reg,
+ .get_touch_data = ili210x_read_touch_data,
+ .parse_touch_data = ili212x_touchdata_to_coords,
+ .continue_polling = ili212x_check_continue_polling,
+ .max_touches = 10,
+ .has_calibrate_reg = true,
+};
+
+static int ili251x_read_reg(struct i2c_client *client,
+ u8 reg, void *buf, size_t len)
+{
+ int error;
+ int ret;
+
+ ret = i2c_master_send(client, ®, 1);
+ if (ret == 1) {
+ usleep_range(5000, 5500);
+
+ ret = i2c_master_recv(client, buf, len);
+ if (ret == len)
+ return 0;
+ }
+
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev, "%s failed: %d\n", __func__, error);
+ return ret;
+}
+
+static int ili251x_read_touch_data(struct i2c_client *client, u8 *data)
+{
+ int error;
+
+ error = ili251x_read_reg(client, REG_TOUCHDATA,
+ data, ILI251X_DATA_SIZE1);
+ if (!error && data[0] == 2) {
+ error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1,
+ ILI251X_DATA_SIZE2);
+ if (error >= 0 && error != ILI251X_DATA_SIZE2)
+ error = -EIO;
+ }
+
+ return error;
+}
+
+static bool ili251x_touchdata_to_coords(const u8 *touchdata,
+ unsigned int finger,
+ unsigned int *x, unsigned int *y,
+ unsigned int *z)
+{
+ u16 val;
+
+ val = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
+ if (!(val & BIT(15))) /* Touch indication */
+ return false;
+
+ *x = val & 0x3fff;
+ *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
+ *z = touchdata[1 + (finger * 5) + 4];
+
+ return true;
+}
+
+static bool ili251x_check_continue_polling(const u8 *data, bool touch)
+{
+ return touch;
+}
+
+static const struct ili2xxx_chip ili251x_chip = {
+ .read_reg = ili251x_read_reg,
+ .get_touch_data = ili251x_read_touch_data,
+ .parse_touch_data = ili251x_touchdata_to_coords,
+ .continue_polling = ili251x_check_continue_polling,
+ .max_touches = 10,
+ .has_calibrate_reg = true,
+ .has_pressure_reg = true,
+};
+
static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
{
struct input_dev *input = priv->input;
int i;
- bool contact = false, touch = false;
- unsigned int x = 0, y = 0;
+ bool contact = false, touch;
+ unsigned int x = 0, y = 0, z = 0;
- for (i = 0; i < priv->max_touches; i++) {
- if (priv->model == MODEL_ILI210X) {
- touch = ili210x_touchdata_to_coords(priv, touchdata,
- i, &x, &y);
- } else if (priv->model == MODEL_ILI251X) {
- touch = ili251x_touchdata_to_coords(priv, touchdata,
- i, &x, &y);
- if (touch)
- contact = true;
- }
+ for (i = 0; i < priv->chip->max_touches; i++) {
+ touch = priv->chip->parse_touch_data(touchdata, i, &x, &y, &z);
input_mt_slot(input, i);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
- if (!touch)
- continue;
- touchscreen_report_pos(input, &priv->prop, x, y,
- true);
+ if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) {
+ touchscreen_report_pos(input, &priv->prop, x, y, true);
+ if (priv->chip->has_pressure_reg)
+ input_report_abs(input, ABS_MT_PRESSURE, z);
+ contact = true;
+ }
}
input_mt_report_pointer_emulation(input, false);
input_sync(input);
- if (priv->model == MODEL_ILI210X)
- contact = touchdata[0] & 0xf3;
-
return contact;
}
-static void ili210x_work(struct work_struct *work)
-{
- struct ili210x *priv = container_of(work, struct ili210x,
- dwork.work);
- struct i2c_client *client = priv->client;
- u8 touchdata[64] = { 0 };
- bool touch;
- int error = -EINVAL;
-
- if (priv->model == MODEL_ILI210X) {
- error = ili210x_read_reg(client, REG_TOUCHDATA,
- touchdata, sizeof(touchdata));
- } else if (priv->model == MODEL_ILI251X) {
- error = ili210x_read_reg(client, REG_TOUCHDATA,
- touchdata, 31);
- if (!error && touchdata[0] == 2)
- error = ili210x_read(client, &touchdata[31], 20);
- }
-
- if (error) {
- dev_err(&client->dev,
- "Unable to get touchdata, err = %d\n", error);
- return;
- }
-
- touch = ili210x_report_events(priv, touchdata);
-
- if (touch)
- schedule_delayed_work(&priv->dwork,
- msecs_to_jiffies(priv->poll_period));
-}
-
static irqreturn_t ili210x_irq(int irq, void *irq_data)
{
struct ili210x *priv = irq_data;
+ struct i2c_client *client = priv->client;
+ const struct ili2xxx_chip *chip = priv->chip;
+ u8 touchdata[ILI210X_DATA_SIZE] = { 0 };
+ bool keep_polling;
+ bool touch;
+ int error;
- schedule_delayed_work(&priv->dwork, 0);
+ do {
+ error = chip->get_touch_data(client, touchdata);
+ if (error) {
+ dev_err(&client->dev,
+ "Unable to get touch data: %d\n", error);
+ break;
+ }
+
+ touch = ili210x_report_events(priv, touchdata);
+ keep_polling = chip->continue_polling(touchdata, touch);
+ if (keep_polling)
+ msleep(ILI2XXX_POLL_PERIOD);
+ } while (!priv->stop && keep_polling);
return IRQ_HANDLED;
}
@@ -242,8 +354,19 @@
NULL,
};
+static umode_t ili210x_calibrate_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ili210x *priv = i2c_get_clientdata(client);
+
+ return priv->chip->has_calibrate_reg ? attr->mode : 0;
+}
+
static const struct attribute_group ili210x_attr_group = {
.attrs = ili210x_attributes,
+ .is_visible = ili210x_calibrate_visible,
};
static void ili210x_power_down(void *data)
@@ -253,28 +376,35 @@
gpiod_set_value_cansleep(reset_gpio, 1);
}
-static void ili210x_cancel_work(void *data)
+static void ili210x_stop(void *data)
{
struct ili210x *priv = data;
- cancel_delayed_work_sync(&priv->dwork);
+ /* Tell ISR to quit even if there is a contact. */
+ priv->stop = true;
}
static int ili210x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
+ const struct ili2xxx_chip *chip;
struct ili210x *priv;
struct gpio_desc *reset_gpio;
struct input_dev *input;
- struct firmware_version firmware;
- enum ili2xxx_model model;
int error;
-
- model = (enum ili2xxx_model)id->driver_data;
+ unsigned int max_xy;
dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+ chip = device_get_match_data(dev);
+ if (!chip && id)
+ chip = (const struct ili2xxx_chip *)id->driver_data;
+ if (!chip) {
+ dev_err(&client->dev, "unknown device model\n");
+ return -ENODEV;
+ }
+
if (client->irq <= 0) {
dev_err(dev, "No IRQ!\n");
return -EINVAL;
@@ -305,54 +435,41 @@
priv->client = client;
priv->input = input;
- priv->poll_period = DEFAULT_POLL_PERIOD;
- INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
priv->reset_gpio = reset_gpio;
- priv->model = model;
- if (model == MODEL_ILI210X)
- priv->max_touches = ILI210X_TOUCHES;
- if (model == MODEL_ILI251X)
- priv->max_touches = ILI251X_TOUCHES;
-
+ priv->chip = chip;
i2c_set_clientdata(client, priv);
- /* Get firmware version */
- error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
- &firmware, sizeof(firmware));
- if (error) {
- dev_err(dev, "Failed to get firmware version, err: %d\n",
- error);
- return error;
- }
-
/* Setup input device */
input->name = "ILI210x Touchscreen";
input->id.bustype = BUS_I2C;
- input->dev.parent = dev;
/* Multi touch */
- input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);
+ max_xy = (chip->resolution ?: SZ_64K) - 1;
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0);
+ if (priv->chip->has_pressure_reg)
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xa, 0, 0);
touchscreen_parse_properties(input, true, &priv->prop);
- error = input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT);
+ error = input_mt_init_slots(input, priv->chip->max_touches,
+ INPUT_MT_DIRECT);
if (error) {
dev_err(dev, "Unable to set up slots, err: %d\n", error);
return error;
}
- error = devm_add_action(dev, ili210x_cancel_work, priv);
- if (error)
- return error;
-
- error = devm_request_irq(dev, client->irq, ili210x_irq, 0,
- client->name, priv);
+ error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq,
+ IRQF_ONESHOT, client->name, priv);
if (error) {
dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
error);
return error;
}
+ error = devm_add_action_or_reset(dev, ili210x_stop, priv);
+ if (error)
+ return error;
+
error = devm_device_add_group(dev, &ili210x_attr_group);
if (error) {
dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
@@ -366,56 +483,30 @@
return error;
}
- device_init_wakeup(dev, 1);
-
- dev_dbg(dev,
- "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
- client->irq, firmware.id, firmware.major, firmware.minor);
-
return 0;
}
-static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- if (device_may_wakeup(&client->dev))
- enable_irq_wake(client->irq);
-
- return 0;
-}
-
-static int __maybe_unused ili210x_i2c_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- if (device_may_wakeup(&client->dev))
- disable_irq_wake(client->irq);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
- ili210x_i2c_suspend, ili210x_i2c_resume);
-
static const struct i2c_device_id ili210x_i2c_id[] = {
- { "ili210x", MODEL_ILI210X },
- { "ili251x", MODEL_ILI251X },
+ { "ili210x", (long)&ili210x_chip },
+ { "ili2117", (long)&ili211x_chip },
+ { "ili2120", (long)&ili212x_chip },
+ { "ili251x", (long)&ili251x_chip },
{ }
};
MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
static const struct of_device_id ili210x_dt_ids[] = {
- { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X },
- { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X },
- { },
+ { .compatible = "ilitek,ili210x", .data = &ili210x_chip },
+ { .compatible = "ilitek,ili2117", .data = &ili211x_chip },
+ { .compatible = "ilitek,ili2120", .data = &ili212x_chip },
+ { .compatible = "ilitek,ili251x", .data = &ili251x_chip },
+ { }
};
MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
static struct i2c_driver ili210x_ts_driver = {
.driver = {
.name = "ili210x_i2c",
- .pm = &ili210x_i2c_pm,
.of_match_table = ili210x_dt_ids,
},
.id_table = ili210x_i2c_id,
diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c
index 5e6ba5c..cd369f9 100644
--- a/drivers/input/touchscreen/imx6ul_tsc.c
+++ b/drivers/input/touchscreen/imx6ul_tsc.c
@@ -315,9 +315,8 @@
return IRQ_HANDLED;
}
-static int imx6ul_tsc_open(struct input_dev *input_dev)
+static int imx6ul_tsc_start(struct imx6ul_tsc *tsc)
{
- struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
int err;
err = clk_prepare_enable(tsc->adc_clk);
@@ -349,16 +348,29 @@
return err;
}
-static void imx6ul_tsc_close(struct input_dev *input_dev)
+static void imx6ul_tsc_stop(struct imx6ul_tsc *tsc)
{
- struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
-
imx6ul_tsc_disable(tsc);
clk_disable_unprepare(tsc->tsc_clk);
clk_disable_unprepare(tsc->adc_clk);
}
+
+static int imx6ul_tsc_open(struct input_dev *input_dev)
+{
+ struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
+
+ return imx6ul_tsc_start(tsc);
+}
+
+static void imx6ul_tsc_close(struct input_dev *input_dev)
+{
+ struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
+
+ imx6ul_tsc_stop(tsc);
+}
+
static int imx6ul_tsc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -509,12 +521,8 @@
mutex_lock(&input_dev->mutex);
- if (input_dev->users) {
- imx6ul_tsc_disable(tsc);
-
- clk_disable_unprepare(tsc->tsc_clk);
- clk_disable_unprepare(tsc->adc_clk);
- }
+ if (input_dev->users)
+ imx6ul_tsc_stop(tsc);
mutex_unlock(&input_dev->mutex);
@@ -530,27 +538,11 @@
mutex_lock(&input_dev->mutex);
- if (!input_dev->users)
- goto out;
+ if (input_dev->users)
+ retval = imx6ul_tsc_start(tsc);
- retval = clk_prepare_enable(tsc->adc_clk);
- if (retval)
- goto out;
-
- retval = clk_prepare_enable(tsc->tsc_clk);
- if (retval) {
- clk_disable_unprepare(tsc->adc_clk);
- goto out;
- }
-
- retval = imx6ul_tsc_init(tsc);
- if (retval) {
- clk_disable_unprepare(tsc->tsc_clk);
- clk_disable_unprepare(tsc->adc_clk);
- goto out;
- }
-out:
mutex_unlock(&input_dev->mutex);
+
return retval;
}
diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 5875bb1..3162b68 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -289,7 +289,7 @@
break;
case IQS5XX_BL_CMD_EXEC:
usleep_range(10000, 10100);
- /* fall through */
+ fallthrough;
default:
return 0;
}
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
index 1af08d3..f15713a 100644
--- a/drivers/input/touchscreen/max11801_ts.c
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -130,7 +130,6 @@
switch (buf[1] & EVENT_TAG_MASK) {
case EVENT_INIT:
- /* fall through */
case EVENT_MIDDLE:
input_report_abs(data->input_dev, ABS_X, x);
input_report_abs(data->input_dev, ABS_Y, y);
diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c
index 247c3aa..f67efdd 100644
--- a/drivers/input/touchscreen/melfas_mip4.c
+++ b/drivers/input/touchscreen/melfas_mip4.c
@@ -391,7 +391,7 @@
/* Screen */
for (i = 0; i < MIP4_MAX_FINGERS; i++) {
input_mt_slot(ts->input, i);
- input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
+ input_mt_report_slot_inactive(ts->input);
}
/* Keys */
@@ -534,7 +534,7 @@
} else {
/* Release event */
input_mt_slot(ts->input, id);
- input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
+ input_mt_report_slot_inactive(ts->input);
}
input_mt_sync_frame(ts->input);
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
index fb28fd2..1f96657 100644
--- a/drivers/input/touchscreen/mms114.c
+++ b/drivers/input/touchscreen/mms114.c
@@ -454,8 +454,7 @@
data->client = client;
data->input_dev = input_dev;
- /* FIXME: switch to device_get_match_data() when available */
- match_data = of_device_get_match_data(&client->dev);
+ match_data = device_get_match_data(&client->dev);
if (!match_data)
return -EINVAL;
@@ -558,7 +557,7 @@
/* Release all touch */
for (id = 0; id < MMS114_MAX_TOUCH; id++) {
input_mt_slot(input_dev, id);
- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+ input_mt_report_slot_inactive(input_dev);
}
input_mt_report_pointer_emulation(input_dev, true);
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c
index e16ec4c..8719a8b 100644
--- a/drivers/input/touchscreen/of_touchscreen.c
+++ b/drivers/input/touchscreen/of_touchscreen.c
@@ -66,7 +66,7 @@
{
struct device *dev = input->dev.parent;
struct input_absinfo *absinfo;
- unsigned int axis;
+ unsigned int axis, axis_x, axis_y;
unsigned int minimum, maximum, fuzz;
bool data_present;
@@ -74,58 +74,57 @@
if (!input->absinfo)
return;
- axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
- data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-x",
- input_abs_get_min(input, axis),
- &minimum) |
- touchscreen_get_prop_u32(dev, "touchscreen-size-x",
- input_abs_get_max(input,
- axis) + 1,
- &maximum) |
- touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x",
- input_abs_get_fuzz(input, axis),
- &fuzz);
- if (data_present)
- touchscreen_set_params(input, axis, minimum, maximum - 1, fuzz);
+ axis_x = multitouch ? ABS_MT_POSITION_X : ABS_X;
+ axis_y = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
- axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
- data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-y",
- input_abs_get_min(input, axis),
- &minimum) |
- touchscreen_get_prop_u32(dev, "touchscreen-size-y",
- input_abs_get_max(input,
- axis) + 1,
- &maximum) |
- touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y",
- input_abs_get_fuzz(input, axis),
- &fuzz);
+ data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-x",
+ input_abs_get_min(input, axis_x),
+ &minimum);
+ data_present |= touchscreen_get_prop_u32(dev, "touchscreen-size-x",
+ input_abs_get_max(input,
+ axis_x) + 1,
+ &maximum);
+ data_present |= touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x",
+ input_abs_get_fuzz(input, axis_x),
+ &fuzz);
if (data_present)
- touchscreen_set_params(input, axis, minimum, maximum - 1, fuzz);
+ touchscreen_set_params(input, axis_x, minimum, maximum - 1, fuzz);
+
+ data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-y",
+ input_abs_get_min(input, axis_y),
+ &minimum);
+ data_present |= touchscreen_get_prop_u32(dev, "touchscreen-size-y",
+ input_abs_get_max(input,
+ axis_y) + 1,
+ &maximum);
+ data_present |= touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y",
+ input_abs_get_fuzz(input, axis_y),
+ &fuzz);
+ if (data_present)
+ touchscreen_set_params(input, axis_y, minimum, maximum - 1, fuzz);
axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
data_present = touchscreen_get_prop_u32(dev,
"touchscreen-max-pressure",
input_abs_get_max(input, axis),
- &maximum) |
- touchscreen_get_prop_u32(dev,
- "touchscreen-fuzz-pressure",
- input_abs_get_fuzz(input, axis),
- &fuzz);
+ &maximum);
+ data_present |= touchscreen_get_prop_u32(dev,
+ "touchscreen-fuzz-pressure",
+ input_abs_get_fuzz(input, axis),
+ &fuzz);
if (data_present)
touchscreen_set_params(input, axis, 0, maximum, fuzz);
if (!prop)
return;
- axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
-
- prop->max_x = input_abs_get_max(input, axis);
- prop->max_y = input_abs_get_max(input, axis + 1);
+ prop->max_x = input_abs_get_max(input, axis_x);
+ prop->max_y = input_abs_get_max(input, axis_y);
prop->invert_x =
device_property_read_bool(dev, "touchscreen-inverted-x");
if (prop->invert_x) {
- absinfo = &input->absinfo[axis];
+ absinfo = &input->absinfo[axis_x];
absinfo->maximum -= absinfo->minimum;
absinfo->minimum = 0;
}
@@ -133,7 +132,7 @@
prop->invert_y =
device_property_read_bool(dev, "touchscreen-inverted-y");
if (prop->invert_y) {
- absinfo = &input->absinfo[axis + 1];
+ absinfo = &input->absinfo[axis_y];
absinfo->maximum -= absinfo->minimum;
absinfo->minimum = 0;
}
@@ -141,7 +140,7 @@
prop->swap_x_y =
device_property_read_bool(dev, "touchscreen-swapped-x-y");
if (prop->swap_x_y)
- swap(input->absinfo[axis], input->absinfo[axis + 1]);
+ swap(input->absinfo[axis_x], input->absinfo[axis_y]);
}
EXPORT_SYMBOL(touchscreen_parse_properties);
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index e146dfa..9aa0985 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -5,22 +5,73 @@
* Copyright (C) 2010-2011 Pixcir, Inc.
*/
+#include <asm/unaligned.h>
#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
#include <linux/of_device.h>
-#include <linux/platform_data/pixcir_i2c_ts.h>
-#include <asm/unaligned.h>
+#include <linux/module.h>
+#include <linux/slab.h>
#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */
+/*
+ * Register map
+ */
+#define PIXCIR_REG_POWER_MODE 51
+#define PIXCIR_REG_INT_MODE 52
+
+/*
+ * Power modes:
+ * active: max scan speed
+ * idle: lower scan speed with automatic transition to active on touch
+ * halt: datasheet says sleep but this is more like halt as the chip
+ * clocks are cut and it can only be brought out of this mode
+ * using the RESET pin.
+ */
+enum pixcir_power_mode {
+ PIXCIR_POWER_ACTIVE,
+ PIXCIR_POWER_IDLE,
+ PIXCIR_POWER_HALT,
+};
+
+#define PIXCIR_POWER_MODE_MASK 0x03
+#define PIXCIR_POWER_ALLOW_IDLE (1UL << 2)
+
+/*
+ * Interrupt modes:
+ * periodical: interrupt is asserted periodicaly
+ * diff coordinates: interrupt is asserted when coordinates change
+ * level on touch: interrupt level asserted during touch
+ * pulse on touch: interrupt pulse asserted during touch
+ *
+ */
+enum pixcir_int_mode {
+ PIXCIR_INT_PERIODICAL,
+ PIXCIR_INT_DIFF_COORD,
+ PIXCIR_INT_LEVEL_TOUCH,
+ PIXCIR_INT_PULSE_TOUCH,
+};
+
+#define PIXCIR_INT_MODE_MASK 0x03
+#define PIXCIR_INT_ENABLE (1UL << 3)
+#define PIXCIR_INT_POL_HIGH (1UL << 2)
+
+/**
+ * struct pixcir_i2c_chip_data - chip related data
+ * @max_fingers: Max number of fingers reported simultaneously by h/w
+ * @has_hw_ids: Hardware supports finger tracking IDs
+ *
+ */
+struct pixcir_i2c_chip_data {
+ u8 max_fingers;
+ bool has_hw_ids;
+};
+
struct pixcir_i2c_ts_data {
struct i2c_client *client;
struct input_dev *input;
@@ -30,7 +81,6 @@
struct gpio_desc *gpio_wake;
const struct pixcir_i2c_chip_data *chip;
struct touchscreen_properties prop;
- int max_fingers; /* Max fingers supported in this instance */
bool running;
};
@@ -54,7 +104,7 @@
memset(report, 0, sizeof(struct pixcir_report_data));
i = chip->has_hw_ids ? 1 : 0;
- readsize = 2 + tsdata->max_fingers * (4 + i);
+ readsize = 2 + tsdata->chip->max_fingers * (4 + i);
if (readsize > sizeof(rdbuf))
readsize = sizeof(rdbuf);
@@ -75,8 +125,8 @@
}
touch = rdbuf[0] & 0x7;
- if (touch > tsdata->max_fingers)
- touch = tsdata->max_fingers;
+ if (touch > tsdata->chip->max_fingers)
+ touch = tsdata->chip->max_fingers;
report->num_touches = touch;
bufptr = &rdbuf[2];
@@ -192,7 +242,7 @@
ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
if (ret < 0) {
- dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ dev_err(dev, "%s: can't read reg %d : %d\n",
__func__, PIXCIR_REG_POWER_MODE, ret);
return ret;
}
@@ -205,7 +255,7 @@
ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret);
if (ret < 0) {
- dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ dev_err(dev, "%s: can't write reg %d : %d\n",
__func__, PIXCIR_REG_POWER_MODE, ret);
return ret;
}
@@ -231,7 +281,7 @@
ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
if (ret < 0) {
- dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ dev_err(dev, "%s: can't read reg %d : %d\n",
__func__, PIXCIR_REG_INT_MODE, ret);
return ret;
}
@@ -246,7 +296,7 @@
ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
if (ret < 0) {
- dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ dev_err(dev, "%s: can't write reg %d : %d\n",
__func__, PIXCIR_REG_INT_MODE, ret);
return ret;
}
@@ -264,7 +314,7 @@
ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
if (ret < 0) {
- dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ dev_err(dev, "%s: can't read reg %d : %d\n",
__func__, PIXCIR_REG_INT_MODE, ret);
return ret;
}
@@ -276,7 +326,7 @@
ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
if (ret < 0) {
- dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ dev_err(dev, "%s: can't write reg %d : %d\n",
__func__, PIXCIR_REG_INT_MODE, ret);
return ret;
}
@@ -412,31 +462,9 @@
static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
-#ifdef CONFIG_OF
-static const struct of_device_id pixcir_of_match[];
-
-static int pixcir_parse_dt(struct device *dev,
- struct pixcir_i2c_ts_data *tsdata)
-{
- tsdata->chip = of_device_get_match_data(dev);
- if (!tsdata->chip)
- return -EINVAL;
-
- return 0;
-}
-#else
-static int pixcir_parse_dt(struct device *dev,
- struct pixcir_i2c_ts_data *tsdata)
-{
- return -EINVAL;
-}
-#endif
-
static int pixcir_i2c_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct pixcir_ts_platform_data *pdata =
- dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
struct pixcir_i2c_ts_data *tsdata;
struct input_dev *input;
@@ -446,19 +474,11 @@
if (!tsdata)
return -ENOMEM;
- if (pdata) {
- tsdata->chip = &pdata->chip;
- } else if (dev->of_node) {
- error = pixcir_parse_dt(dev, tsdata);
- if (error)
- return error;
- } else {
- dev_err(dev, "platform data not defined\n");
- return -EINVAL;
- }
-
- if (!tsdata->chip->max_fingers) {
- dev_err(dev, "Invalid max_fingers in chip data\n");
+ tsdata->chip = device_get_match_data(dev);
+ if (!tsdata->chip && id)
+ tsdata->chip = (const void *)id->driver_data;
+ if (!tsdata->chip) {
+ dev_err(dev, "can't locate chip data\n");
return -EINVAL;
}
@@ -475,30 +495,17 @@
input->id.bustype = BUS_I2C;
input->open = pixcir_input_open;
input->close = pixcir_input_close;
- input->dev.parent = dev;
- if (pdata) {
- input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
- } else {
- input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
- input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
- touchscreen_parse_properties(input, true, &tsdata->prop);
- if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
- !input_abs_get_max(input, ABS_MT_POSITION_Y)) {
- dev_err(dev, "Touchscreen size is not specified\n");
- return -EINVAL;
- }
+ input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
+ input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
+ touchscreen_parse_properties(input, true, &tsdata->prop);
+ if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
+ !input_abs_get_max(input, ABS_MT_POSITION_Y)) {
+ dev_err(dev, "Touchscreen size is not specified\n");
+ return -EINVAL;
}
- tsdata->max_fingers = tsdata->chip->max_fingers;
- if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) {
- tsdata->max_fingers = PIXCIR_MAX_SLOTS;
- dev_info(dev, "Limiting maximum fingers to %d\n",
- tsdata->max_fingers);
- }
-
- error = input_mt_init_slots(input, tsdata->max_fingers,
+ error = input_mt_init_slots(input, tsdata->chip->max_fingers,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(dev, "Error initializing Multi-Touch slots\n");
@@ -510,7 +517,9 @@
tsdata->gpio_attb = devm_gpiod_get(dev, "attb", GPIOD_IN);
if (IS_ERR(tsdata->gpio_attb)) {
error = PTR_ERR(tsdata->gpio_attb);
- dev_err(dev, "Failed to request ATTB gpio: %d\n", error);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request ATTB gpio: %d\n",
+ error);
return error;
}
@@ -518,7 +527,9 @@
GPIOD_OUT_LOW);
if (IS_ERR(tsdata->gpio_reset)) {
error = PTR_ERR(tsdata->gpio_reset);
- dev_err(dev, "Failed to request RESET gpio: %d\n", error);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request RESET gpio: %d\n",
+ error);
return error;
}
@@ -574,14 +585,6 @@
return 0;
}
-static const struct i2c_device_id pixcir_i2c_ts_id[] = {
- { "pixcir_ts", 0 },
- { "pixcir_tangoc", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
-
-#ifdef CONFIG_OF
static const struct pixcir_i2c_chip_data pixcir_ts_data = {
.max_fingers = 2,
/* no hw id support */
@@ -592,6 +595,14 @@
.has_hw_ids = true,
};
+static const struct i2c_device_id pixcir_i2c_ts_id[] = {
+ { "pixcir_ts", (unsigned long) &pixcir_ts_data },
+ { "pixcir_tangoc", (unsigned long) &pixcir_tangoc_data },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
+
+#ifdef CONFIG_OF
static const struct of_device_id pixcir_of_match[] = {
{ .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data },
{ .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data },
diff --git a/drivers/input/touchscreen/raspberrypi-ts.c b/drivers/input/touchscreen/raspberrypi-ts.c
index 6988126..ef6aaed 100644
--- a/drivers/input/touchscreen/raspberrypi-ts.c
+++ b/drivers/input/touchscreen/raspberrypi-ts.c
@@ -16,7 +16,6 @@
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/input/mt.h>
-#include <linux/input-polldev.h>
#include <linux/input/touchscreen.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
@@ -34,7 +33,7 @@
struct rpi_ts {
struct platform_device *pdev;
- struct input_polled_dev *poll_dev;
+ struct input_dev *input;
struct touchscreen_properties prop;
void __iomem *fw_regs_va;
@@ -57,10 +56,9 @@
} point[RPI_TS_MAX_SUPPORTED_POINTS];
};
-static void rpi_ts_poll(struct input_polled_dev *dev)
+static void rpi_ts_poll(struct input_dev *input)
{
- struct input_dev *input = dev->input;
- struct rpi_ts *ts = dev->private;
+ struct rpi_ts *ts = input_get_drvdata(input);
struct rpi_ts_regs regs;
int modified_ids = 0;
long released_ids;
@@ -102,7 +100,7 @@
released_ids = ts->known_ids & ~modified_ids;
for_each_set_bit(i, &released_ids, RPI_TS_MAX_SUPPORTED_POINTS) {
input_mt_slot(input, i);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, 0);
+ input_mt_report_slot_inactive(input);
modified_ids &= ~(BIT(i));
}
ts->known_ids = modified_ids;
@@ -123,10 +121,9 @@
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
- struct input_polled_dev *poll_dev;
+ struct input_dev *input;
struct device_node *fw_node;
struct rpi_firmware *fw;
- struct input_dev *input;
struct rpi_ts *ts;
u32 touchbuf;
int error;
@@ -160,7 +157,6 @@
return error;
}
-
touchbuf = (u32)ts->fw_regs_phys;
error = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF,
&touchbuf, sizeof(touchbuf));
@@ -170,19 +166,17 @@
return error;
}
- poll_dev = devm_input_allocate_polled_device(dev);
- if (!poll_dev) {
+ input = devm_input_allocate_device(dev);
+ if (!input) {
dev_err(dev, "Failed to allocate input device\n");
return -ENOMEM;
}
- ts->poll_dev = poll_dev;
- input = poll_dev->input;
+
+ ts->input = input;
+ input_set_drvdata(input, ts);
input->name = "raspberrypi-ts";
input->id.bustype = BUS_HOST;
- poll_dev->poll_interval = RPI_TS_POLL_INTERVAL;
- poll_dev->poll = rpi_ts_poll;
- poll_dev->private = ts;
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
RPI_TS_DEFAULT_WIDTH, 0, 0);
@@ -197,7 +191,15 @@
return error;
}
- error = input_register_polled_device(poll_dev);
+ error = input_setup_polling(input, rpi_ts_poll);
+ if (error) {
+ dev_err(dev, "could not set up polling mode, %d\n", error);
+ return error;
+ }
+
+ input_set_poll_interval(input, RPI_TS_POLL_INTERVAL);
+
+ error = input_register_device(input);
if (error) {
dev_err(dev, "could not register input device, %d\n", error);
return error;
@@ -214,10 +216,10 @@
static struct platform_driver rpi_ts_driver = {
.driver = {
- .name = "raspberrypi-ts",
+ .name = "raspberrypi-ts",
.of_match_table = rpi_ts_match,
},
- .probe = rpi_ts_probe,
+ .probe = rpi_ts_probe,
};
module_platform_driver(rpi_ts_driver);
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
index 2c67f8e..4d2d22a 100644
--- a/drivers/input/touchscreen/raydium_i2c_ts.c
+++ b/drivers/input/touchscreen/raydium_i2c_ts.c
@@ -51,6 +51,7 @@
/* Touch relative info */
#define RM_MAX_RETRIES 3
+#define RM_RETRY_DELAY_MS 20
#define RM_MAX_TOUCH_NUM 10
#define RM_BOOT_DELAY_MS 100
@@ -136,83 +137,132 @@
bool wake_irq_enabled;
};
-static int raydium_i2c_send(struct i2c_client *client,
- u8 addr, const void *data, size_t len)
-{
- u8 *buf;
- int tries = 0;
- int ret;
+/*
+ * Header to be sent for RM_CMD_BANK_SWITCH command. This is used by
+ * raydium_i2c_{read|send} below.
+ */
+struct __packed raydium_bank_switch_header {
+ u8 cmd;
+ __be32 be_addr;
+};
- buf = kmalloc(len + 1, GFP_KERNEL);
- if (!buf)
+static int raydium_i2c_xfer(struct i2c_client *client, u32 addr,
+ struct i2c_msg *xfer, size_t xfer_count)
+{
+ int ret;
+ /*
+ * If address is greater than 255, then RM_CMD_BANK_SWITCH needs to be
+ * sent first. Else, skip the header i.e. xfer[0].
+ */
+ int xfer_start_idx = (addr > 0xff) ? 0 : 1;
+ xfer_count -= xfer_start_idx;
+
+ ret = i2c_transfer(client->adapter, &xfer[xfer_start_idx], xfer_count);
+ if (likely(ret == xfer_count))
+ return 0;
+
+ return ret < 0 ? ret : -EIO;
+}
+
+static int raydium_i2c_send(struct i2c_client *client,
+ u32 addr, const void *data, size_t len)
+{
+ int tries = 0;
+ int error;
+ u8 *tx_buf;
+ u8 reg_addr = addr & 0xff;
+
+ tx_buf = kmalloc(len + 1, GFP_KERNEL);
+ if (!tx_buf)
return -ENOMEM;
- buf[0] = addr;
- memcpy(buf + 1, data, len);
+ tx_buf[0] = reg_addr;
+ memcpy(tx_buf + 1, data, len);
do {
- ret = i2c_master_send(client, buf, len + 1);
- if (likely(ret == len + 1))
- break;
+ struct raydium_bank_switch_header header = {
+ .cmd = RM_CMD_BANK_SWITCH,
+ .be_addr = cpu_to_be32(addr),
+ };
- msleep(20);
+ /*
+ * Perform as a single i2c_transfer transaction to ensure that
+ * no other I2C transactions are initiated on the bus to any
+ * other device in between. Initiating transacations to other
+ * devices after RM_CMD_BANK_SWITCH is sent is known to cause
+ * issues. This is also why regmap infrastructure cannot be used
+ * for this driver. Regmap handles page(bank) switch and reads
+ * as separate i2c_transfer() operations. This can result in
+ * problems if the Raydium device is on a shared I2C bus.
+ */
+ struct i2c_msg xfer[] = {
+ {
+ .addr = client->addr,
+ .len = sizeof(header),
+ .buf = (u8 *)&header,
+ },
+ {
+ .addr = client->addr,
+ .len = len + 1,
+ .buf = tx_buf,
+ },
+ };
+
+ error = raydium_i2c_xfer(client, addr, xfer, ARRAY_SIZE(xfer));
+ if (likely(!error))
+ return 0;
+
+ msleep(RM_RETRY_DELAY_MS);
} while (++tries < RM_MAX_RETRIES);
- kfree(buf);
-
- if (unlikely(ret != len + 1)) {
- if (ret >= 0)
- ret = -EIO;
- dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
- return ret;
- }
-
- return 0;
+ dev_err(&client->dev, "%s failed: %d\n", __func__, error);
+ return error;
}
static int raydium_i2c_read(struct i2c_client *client,
- u8 addr, void *data, size_t len)
+ u32 addr, void *data, size_t len)
{
- struct i2c_msg xfer[] = {
- {
- .addr = client->addr,
- .len = 1,
- .buf = &addr,
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = data,
- }
- };
- int ret;
-
- ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
- if (unlikely(ret != ARRAY_SIZE(xfer)))
- return ret < 0 ? ret : -EIO;
-
- return 0;
-}
-
-static int raydium_i2c_read_message(struct i2c_client *client,
- u32 addr, void *data, size_t len)
-{
- __be32 be_addr;
- size_t xfer_len;
int error;
while (len) {
- xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
+ u8 reg_addr = addr & 0xff;
+ struct raydium_bank_switch_header header = {
+ .cmd = RM_CMD_BANK_SWITCH,
+ .be_addr = cpu_to_be32(addr),
+ };
+ size_t xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
- be_addr = cpu_to_be32(addr);
+ /*
+ * Perform as a single i2c_transfer transaction to ensure that
+ * no other I2C transactions are initiated on the bus to any
+ * other device in between. Initiating transacations to other
+ * devices after RM_CMD_BANK_SWITCH is sent is known to cause
+ * issues. This is also why regmap infrastructure cannot be used
+ * for this driver. Regmap handles page(bank) switch and writes
+ * as separate i2c_transfer() operations. This can result in
+ * problems if the Raydium device is on a shared I2C bus.
+ */
+ struct i2c_msg xfer[] = {
+ {
+ .addr = client->addr,
+ .len = sizeof(header),
+ .buf = (u8 *)&header,
+ },
+ {
+ .addr = client->addr,
+ .len = 1,
+ .buf = ®_addr,
+ },
+ {
+ .addr = client->addr,
+ .len = xfer_len,
+ .buf = data,
+ .flags = I2C_M_RD,
+ }
+ };
- error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
- &be_addr, sizeof(be_addr));
- if (!error)
- error = raydium_i2c_read(client, addr & 0xff,
- data, xfer_len);
- if (error)
+ error = raydium_i2c_xfer(client, addr, xfer, ARRAY_SIZE(xfer));
+ if (unlikely(error))
return error;
len -= xfer_len;
@@ -223,27 +273,13 @@
return 0;
}
-static int raydium_i2c_send_message(struct i2c_client *client,
- u32 addr, const void *data, size_t len)
-{
- __be32 be_addr = cpu_to_be32(addr);
- int error;
-
- error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
- &be_addr, sizeof(be_addr));
- if (!error)
- error = raydium_i2c_send(client, addr & 0xff, data, len);
-
- return error;
-}
-
static int raydium_i2c_sw_reset(struct i2c_client *client)
{
const u8 soft_rst_cmd = 0x01;
int error;
- error = raydium_i2c_send_message(client, RM_RESET_MSG_ADDR,
- &soft_rst_cmd, sizeof(soft_rst_cmd));
+ error = raydium_i2c_send(client, RM_RESET_MSG_ADDR, &soft_rst_cmd,
+ sizeof(soft_rst_cmd));
if (error) {
dev_err(&client->dev, "software reset failed: %d\n", error);
return error;
@@ -295,9 +331,8 @@
if (error)
continue;
- error = raydium_i2c_read_message(client,
- le32_to_cpu(query_bank_addr),
- &ts->info, sizeof(ts->info));
+ error = raydium_i2c_read(client, le32_to_cpu(query_bank_addr),
+ &ts->info, sizeof(ts->info));
if (error)
continue;
@@ -835,8 +870,8 @@
if (ts->boot_mode != RAYDIUM_TS_MAIN)
goto out;
- error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
- ts->report_data, ts->pkg_size);
+ error = raydium_i2c_read(ts->client, ts->data_bank_addr,
+ ts->report_data, ts->pkg_size);
if (error)
goto out;
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index b346e7c..2e70c0b 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -13,7 +13,6 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -21,10 +20,43 @@
#include <linux/clk.h>
#include <linux/io.h>
-#include <plat/adc.h>
-#include <plat/regs-adc.h>
+#include <linux/soc/samsung/s3c-adc.h>
#include <linux/platform_data/touchscreen-s3c2410.h>
+#define S3C2410_ADCCON (0x00)
+#define S3C2410_ADCTSC (0x04)
+#define S3C2410_ADCDLY (0x08)
+#define S3C2410_ADCDAT0 (0x0C)
+#define S3C2410_ADCDAT1 (0x10)
+#define S3C64XX_ADCUPDN (0x14)
+#define S3C2443_ADCMUX (0x18)
+#define S3C64XX_ADCCLRINT (0x18)
+#define S5P_ADCMUX (0x1C)
+#define S3C64XX_ADCCLRINTPNDNUP (0x20)
+
+/* ADCTSC Register Bits */
+#define S3C2443_ADCTSC_UD_SEN (1 << 8)
+#define S3C2410_ADCTSC_YM_SEN (1<<7)
+#define S3C2410_ADCTSC_YP_SEN (1<<6)
+#define S3C2410_ADCTSC_XM_SEN (1<<5)
+#define S3C2410_ADCTSC_XP_SEN (1<<4)
+#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3)
+#define S3C2410_ADCTSC_AUTO_PST (1<<2)
+#define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0)
+
+/* ADCDAT0 Bits */
+#define S3C2410_ADCDAT0_UPDOWN (1<<15)
+#define S3C2410_ADCDAT0_AUTO_PST (1<<14)
+#define S3C2410_ADCDAT0_XY_PST (0x3<<12)
+#define S3C2410_ADCDAT0_XPDATA_MASK (0x03FF)
+
+/* ADCDAT1 Bits */
+#define S3C2410_ADCDAT1_UPDOWN (1<<15)
+#define S3C2410_ADCDAT1_AUTO_PST (1<<14)
+#define S3C2410_ADCDAT1_XY_PST (0x3<<12)
+#define S3C2410_ADCDAT1_YPDATA_MASK (0x03FF)
+
+
#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
#define INT_DOWN (0)
diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
index c877614..e8b6c31 100644
--- a/drivers/input/touchscreen/silead.c
+++ b/drivers/input/touchscreen/silead.c
@@ -289,7 +289,7 @@
dev_dbg(dev, "Firmware file name: %s", data->fw_name);
- error = request_firmware(&fw, data->fw_name, dev);
+ error = firmware_request_platform(&fw, data->fw_name, dev);
if (error) {
dev_err(dev, "Firmware request error %d\n", error);
return error;
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index 1c5f887..63b29c7 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -14,23 +14,19 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/types.h>
-#include <linux/input/touchscreen.h>
#define ST1232_TS_NAME "st1232-ts"
#define ST1633_TS_NAME "st1633-ts"
-struct st1232_ts_finger {
- u16 x;
- u16 y;
- u8 t;
- bool is_valid;
-};
+#define ST_TS_MAX_FINGERS 10
struct st_chip_info {
bool have_z;
@@ -50,81 +46,89 @@
const struct st_chip_info *chip_info;
int read_buf_len;
u8 *read_buf;
- struct st1232_ts_finger *finger;
};
static int st1232_ts_read_data(struct st1232_ts_data *ts)
{
- struct st1232_ts_finger *finger = ts->finger;
struct i2c_client *client = ts->client;
- struct i2c_msg msg[2];
- int error;
- int i, y;
u8 start_reg = ts->chip_info->start_reg;
- u8 *buf = ts->read_buf;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .len = sizeof(start_reg),
+ .buf = &start_reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD | I2C_M_DMA_SAFE,
+ .len = ts->read_buf_len,
+ .buf = ts->read_buf,
+ }
+ };
+ int ret;
- /* read touchscreen data */
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 1;
- msg[0].buf = &start_reg;
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg))
+ return ret < 0 ? ret : -EIO;
- msg[1].addr = ts->client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = ts->read_buf_len;
- msg[1].buf = buf;
+ return 0;
+}
- error = i2c_transfer(client->adapter, msg, 2);
- if (error < 0)
- return error;
+static int st1232_ts_parse_and_report(struct st1232_ts_data *ts)
+{
+ struct input_dev *input = ts->input_dev;
+ struct input_mt_pos pos[ST_TS_MAX_FINGERS];
+ u8 z[ST_TS_MAX_FINGERS];
+ int slots[ST_TS_MAX_FINGERS];
+ int n_contacts = 0;
+ int i;
- for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) {
- finger[i].is_valid = buf[i + y] >> 7;
- if (finger[i].is_valid) {
- finger[i].x = ((buf[i + y] & 0x0070) << 4) |
- buf[i + y + 1];
- finger[i].y = ((buf[i + y] & 0x0007) << 8) |
- buf[i + y + 2];
+ for (i = 0; i < ts->chip_info->max_fingers; i++) {
+ u8 *buf = &ts->read_buf[i * 4];
+
+ if (buf[0] & BIT(7)) {
+ unsigned int x = ((buf[0] & 0x70) << 4) | buf[1];
+ unsigned int y = ((buf[0] & 0x07) << 8) | buf[2];
+
+ touchscreen_set_mt_pos(&pos[n_contacts],
+ &ts->prop, x, y);
/* st1232 includes a z-axis / touch strength */
if (ts->chip_info->have_z)
- finger[i].t = buf[i + 6];
+ z[n_contacts] = ts->read_buf[i + 6];
+
+ n_contacts++;
}
}
- return 0;
+ input_mt_assign_slots(input, slots, pos, n_contacts, 0);
+ for (i = 0; i < n_contacts; 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, pos[i].x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos[i].y);
+ if (ts->chip_info->have_z)
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, z[i]);
+ }
+
+ input_mt_sync_frame(input);
+ input_sync(input);
+
+ return n_contacts;
}
static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
{
struct st1232_ts_data *ts = dev_id;
- struct st1232_ts_finger *finger = ts->finger;
- struct input_dev *input_dev = ts->input_dev;
- int count = 0;
- int i, ret;
+ int count;
+ int error;
- ret = st1232_ts_read_data(ts);
- if (ret < 0)
- goto end;
+ error = st1232_ts_read_data(ts);
+ if (error)
+ goto out;
- /* multi touch protocol */
- for (i = 0; i < ts->chip_info->max_fingers; i++) {
- if (!finger[i].is_valid)
- continue;
-
- if (ts->chip_info->have_z)
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
- finger[i].t);
-
- touchscreen_report_pos(input_dev, &ts->prop,
- finger[i].x, finger[i].y, true);
- input_mt_sync(input_dev);
- count++;
- }
-
- /* SYN_MT_REPORT only if no contact */
+ count = st1232_ts_parse_and_report(ts);
if (!count) {
- input_mt_sync(input_dev);
if (ts->low_latency_req.dev) {
dev_pm_qos_remove_request(&ts->low_latency_req);
ts->low_latency_req.dev = NULL;
@@ -136,10 +140,7 @@
DEV_PM_QOS_RESUME_LATENCY, 100);
}
- /* SYN_REPORT */
- input_sync(input_dev);
-
-end:
+out:
return IRQ_HANDLED;
}
@@ -177,7 +178,6 @@
{
const struct st_chip_info *match;
struct st1232_ts_data *ts;
- struct st1232_ts_finger *finger;
struct input_dev *input_dev;
int error;
@@ -204,11 +204,6 @@
return -ENOMEM;
ts->chip_info = match;
- ts->finger = devm_kcalloc(&client->dev,
- ts->chip_info->max_fingers, sizeof(*finger),
- GFP_KERNEL);
- if (!ts->finger)
- return -ENOMEM;
/* allocate a buffer according to the number of registers to read */
ts->read_buf_len = ts->chip_info->max_fingers * 4;
@@ -243,12 +238,6 @@
input_dev->name = "st1232-touchscreen";
input_dev->id.bustype = BUS_I2C;
- input_dev->dev.parent = &client->dev;
-
- __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
- __set_bit(EV_SYN, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(EV_ABS, input_dev->evbit);
if (ts->chip_info->have_z)
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
@@ -261,6 +250,14 @@
touchscreen_parse_properties(input_dev, true, &ts->prop);
+ error = input_mt_init_slots(input_dev, ts->chip_info->max_fingers,
+ INPUT_MT_DIRECT | INPUT_MT_TRACK |
+ INPUT_MT_DROP_UNUSED);
+ if (error) {
+ dev_err(&client->dev, "failed to initialize MT slots\n");
+ return error;
+ }
+
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, st1232_ts_irq_handler,
IRQF_ONESHOT,
@@ -278,7 +275,6 @@
}
i2c_set_clientdata(client, ts);
- device_init_wakeup(&client->dev, 1);
return 0;
}
@@ -288,12 +284,10 @@
struct i2c_client *client = to_i2c_client(dev);
struct st1232_ts_data *ts = i2c_get_clientdata(client);
- if (device_may_wakeup(&client->dev)) {
- enable_irq_wake(client->irq);
- } else {
- disable_irq(client->irq);
+ disable_irq(client->irq);
+
+ if (!device_may_wakeup(&client->dev))
st1232_ts_power(ts, false);
- }
return 0;
}
@@ -303,12 +297,10 @@
struct i2c_client *client = to_i2c_client(dev);
struct st1232_ts_data *ts = i2c_get_clientdata(client);
- if (device_may_wakeup(&client->dev)) {
- disable_irq_wake(client->irq);
- } else {
+ if (!device_may_wakeup(&client->dev))
st1232_ts_power(ts, true);
- enable_irq(client->irq);
- }
+
+ enable_irq(client->irq);
return 0;
}
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index cd8805d..9a64e1d 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -198,7 +198,7 @@
u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4;
input_mt_slot(sdata->input, slot_id);
- input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, false);
+ input_mt_report_slot_inactive(sdata->input);
input_sync(sdata->input);
}
@@ -255,7 +255,7 @@
case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
case STMFTS_EV_STATUS:
complete(&sdata->cmd_done);
- /* fall through */
+ fallthrough;
case STMFTS_EV_NO_EVENT:
case STMFTS_EV_DEBUG:
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index 902522d..12f2562 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -27,7 +27,7 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/printk.h>
-#include <linux/input-polldev.h>
+#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/usb/input.h>
#include <linux/videodev2.h>
@@ -206,7 +206,7 @@
struct usb_device *usbdev;
struct device *dev;
- struct input_polled_dev *input;
+ struct input_dev *input;
struct v4l2_device v4l2;
struct video_device vdev;
@@ -370,6 +370,10 @@
goto error;
result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12);
+ if (result < 0)
+ goto error;
+
+ result = 0;
/*
* Discard the result buffer - no known data inside except
@@ -381,22 +385,22 @@
}
/*
- * Callback routines from input_polled_dev
+ * Callback routines from input_dev
*/
/* Enable the device, polling will now start. */
-static void sur40_open(struct input_polled_dev *polldev)
+static int sur40_open(struct input_dev *input)
{
- struct sur40_state *sur40 = polldev->private;
+ struct sur40_state *sur40 = input_get_drvdata(input);
dev_dbg(sur40->dev, "open\n");
- sur40_init(sur40);
+ return sur40_init(sur40);
}
/* Disable device, polling has stopped. */
-static void sur40_close(struct input_polled_dev *polldev)
+static void sur40_close(struct input_dev *input)
{
- struct sur40_state *sur40 = polldev->private;
+ struct sur40_state *sur40 = input_get_drvdata(input);
dev_dbg(sur40->dev, "close\n");
/*
@@ -448,13 +452,10 @@
}
/* core function: poll for new input data */
-static void sur40_poll(struct input_polled_dev *polldev)
+static void sur40_poll(struct input_dev *input)
{
- struct sur40_state *sur40 = polldev->private;
- struct input_dev *input = polldev->input;
+ struct sur40_state *sur40 = input_get_drvdata(input);
int result, bulk_read, need_blobs, packet_blobs, i;
- u32 uninitialized_var(packet_id);
-
struct sur40_header *header = &sur40->bulk_in_buffer->header;
struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0];
@@ -488,7 +489,7 @@
if (need_blobs == -1) {
need_blobs = le16_to_cpu(header->count);
dev_dbg(sur40->dev, "need %d blobs\n", need_blobs);
- packet_id = le32_to_cpu(header->packet_id);
+ /* packet_id = le32_to_cpu(header->packet_id); */
}
/*
@@ -613,10 +614,9 @@
}
/* Initialize input device parameters. */
-static void sur40_input_setup(struct input_dev *input_dev)
+static int sur40_input_setup_events(struct input_dev *input_dev)
{
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(EV_ABS, input_dev->evbit);
+ int error;
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, SENSOR_RES_X, 0, 0);
@@ -637,8 +637,14 @@
input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
- input_mt_init_slots(input_dev, MAX_CONTACTS,
- INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ error = input_mt_init_slots(input_dev, MAX_CONTACTS,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error) {
+ dev_err(input_dev->dev.parent, "failed to set up slots\n");
+ return error;
+ }
+
+ return 0;
}
/* Check candidate USB interface. */
@@ -649,7 +655,7 @@
struct sur40_state *sur40;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
- struct input_polled_dev *poll_dev;
+ struct input_dev *input;
int error;
/* Check if we really have the right interface. */
@@ -670,8 +676,8 @@
if (!sur40)
return -ENOMEM;
- poll_dev = input_allocate_polled_device();
- if (!poll_dev) {
+ input = input_allocate_device();
+ if (!input) {
error = -ENOMEM;
goto err_free_dev;
}
@@ -681,26 +687,33 @@
spin_lock_init(&sur40->qlock);
mutex_init(&sur40->lock);
- /* Set up polled input device control structure */
- poll_dev->private = sur40;
- poll_dev->poll_interval = POLL_INTERVAL;
- poll_dev->open = sur40_open;
- poll_dev->poll = sur40_poll;
- poll_dev->close = sur40_close;
-
/* Set up regular input device structure */
- sur40_input_setup(poll_dev->input);
-
- poll_dev->input->name = DRIVER_LONG;
- usb_to_input_id(usbdev, &poll_dev->input->id);
+ input->name = DRIVER_LONG;
+ usb_to_input_id(usbdev, &input->id);
usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
- poll_dev->input->phys = sur40->phys;
- poll_dev->input->dev.parent = &interface->dev;
+ input->phys = sur40->phys;
+ input->dev.parent = &interface->dev;
+
+ input->open = sur40_open;
+ input->close = sur40_close;
+
+ error = sur40_input_setup_events(input);
+ if (error)
+ goto err_free_input;
+
+ input_set_drvdata(input, sur40);
+ error = input_setup_polling(input, sur40_poll);
+ if (error) {
+ dev_err(&interface->dev, "failed to set up polling");
+ goto err_free_input;
+ }
+
+ input_set_poll_interval(input, POLL_INTERVAL);
sur40->usbdev = usbdev;
sur40->dev = &interface->dev;
- sur40->input = poll_dev;
+ sur40->input = input;
/* use the bulk-in endpoint tested above */
sur40->bulk_in_size = usb_endpoint_maxp(endpoint);
@@ -709,11 +722,11 @@
if (!sur40->bulk_in_buffer) {
dev_err(&interface->dev, "Unable to allocate input buffer.");
error = -ENOMEM;
- goto err_free_polldev;
+ goto err_free_input;
}
/* register the polled input device */
- error = input_register_polled_device(poll_dev);
+ error = input_register_device(input);
if (error) {
dev_err(&interface->dev,
"Unable to register polled input device.");
@@ -797,8 +810,8 @@
v4l2_device_unregister(&sur40->v4l2);
err_free_buffer:
kfree(sur40->bulk_in_buffer);
-err_free_polldev:
- input_free_polled_device(sur40->input);
+err_free_input:
+ input_free_device(input);
err_free_dev:
kfree(sur40);
@@ -814,8 +827,7 @@
video_unregister_device(&sur40->vdev);
v4l2_device_unregister(&sur40->v4l2);
- input_unregister_polled_device(sur40->input);
- input_free_polled_device(sur40->input);
+ input_unregister_device(sur40->input);
kfree(sur40->bulk_in_buffer);
kfree(sur40);
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
index 75170a7..357a310 100644
--- a/drivers/input/touchscreen/tps6507x-ts.c
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -17,7 +17,6 @@
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/input.h>
-#include <linux/input-polldev.h>
#include <linux/platform_device.h>
#include <linux/mfd/tps6507x.h>
#include <linux/input/tps6507x-ts.h>
@@ -40,7 +39,7 @@
struct tps6507x_ts {
struct device *dev;
- struct input_polled_dev *poll_dev;
+ struct input_dev *input;
struct tps6507x_dev *mfd;
char phys[32];
struct ts_event tc;
@@ -148,10 +147,9 @@
return ret;
}
-static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
+static void tps6507x_ts_poll(struct input_dev *input_dev)
{
- struct tps6507x_ts *tsc = poll_dev->private;
- struct input_dev *input_dev = poll_dev->input;
+ struct tps6507x_ts *tsc = input_get_drvdata(input_dev);
bool pendown;
s32 ret;
@@ -205,7 +203,6 @@
const struct tps6507x_board *tps_board;
const struct touchscreen_init_data *init_data;
struct tps6507x_ts *tsc;
- struct input_polled_dev *poll_dev;
struct input_dev *input_dev;
int error;
@@ -240,23 +237,16 @@
snprintf(tsc->phys, sizeof(tsc->phys),
"%s/input0", dev_name(tsc->dev));
- poll_dev = devm_input_allocate_polled_device(&pdev->dev);
- if (!poll_dev) {
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev) {
dev_err(tsc->dev, "Failed to allocate polled input device.\n");
return -ENOMEM;
}
- tsc->poll_dev = poll_dev;
+ tsc->input = input_dev;
+ input_set_drvdata(input_dev, tsc);
- poll_dev->private = tsc;
- poll_dev->poll = tps6507x_ts_poll;
- poll_dev->poll_interval = init_data ?
- init_data->poll_period : TSC_DEFAULT_POLL_PERIOD;
-
- input_dev = poll_dev->input;
- 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_10BIT, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
@@ -275,7 +265,15 @@
if (error)
return error;
- error = input_register_polled_device(poll_dev);
+ error = input_setup_polling(input_dev, tps6507x_ts_poll);
+ if (error)
+ return error;
+
+ input_set_poll_interval(input_dev,
+ init_data ? init_data->poll_period :
+ TSC_DEFAULT_POLL_PERIOD);
+
+ error = input_register_device(input_dev);
if (error)
return error;
diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c
index 5b4f536..6cf66aa 100644
--- a/drivers/input/touchscreen/ts4800-ts.c
+++ b/drivers/input/touchscreen/ts4800-ts.c
@@ -10,7 +10,6 @@
#include <linux/bitops.h>
#include <linux/input.h>
-#include <linux/input-polldev.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
@@ -33,7 +32,7 @@
#define Y_OFFSET 0x2
struct ts4800_ts {
- struct input_polled_dev *poll_dev;
+ struct input_dev *input;
struct device *dev;
char phys[32];
@@ -46,22 +45,26 @@
int debounce;
};
-static void ts4800_ts_open(struct input_polled_dev *dev)
+static int ts4800_ts_open(struct input_dev *input_dev)
{
- struct ts4800_ts *ts = dev->private;
- int ret;
+ struct ts4800_ts *ts = input_get_drvdata(input_dev);
+ int error;
ts->pendown = false;
ts->debounce = DEBOUNCE_COUNT;
- ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
- if (ret)
- dev_warn(ts->dev, "Failed to enable touchscreen\n");
+ error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
+ if (error) {
+ dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error);
+ return error;
+ }
+
+ return 0;
}
-static void ts4800_ts_close(struct input_polled_dev *dev)
+static void ts4800_ts_close(struct input_dev *input_dev)
{
- struct ts4800_ts *ts = dev->private;
+ struct ts4800_ts *ts = input_get_drvdata(input_dev);
int ret;
ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
@@ -70,10 +73,9 @@
}
-static void ts4800_ts_poll(struct input_polled_dev *dev)
+static void ts4800_ts_poll(struct input_dev *input_dev)
{
- struct input_dev *input_dev = dev->input;
- struct ts4800_ts *ts = dev->private;
+ struct ts4800_ts *ts = input_get_drvdata(input_dev);
u16 last_x = readw(ts->base + X_OFFSET);
u16 last_y = readw(ts->base + Y_OFFSET);
bool pendown = last_x & PENDOWN_MASK;
@@ -146,7 +148,7 @@
static int ts4800_ts_probe(struct platform_device *pdev)
{
- struct input_polled_dev *poll_dev;
+ struct input_dev *input_dev;
struct ts4800_ts *ts;
int error;
@@ -162,32 +164,38 @@
if (IS_ERR(ts->base))
return PTR_ERR(ts->base);
- poll_dev = devm_input_allocate_polled_device(&pdev->dev);
- if (!poll_dev)
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev)
return -ENOMEM;
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
- ts->poll_dev = poll_dev;
+ ts->input = input_dev;
ts->dev = &pdev->dev;
- poll_dev->private = ts;
- poll_dev->poll_interval = POLL_INTERVAL;
- poll_dev->open = ts4800_ts_open;
- poll_dev->close = ts4800_ts_close;
- poll_dev->poll = ts4800_ts_poll;
+ input_set_drvdata(input_dev, ts);
- poll_dev->input->name = "TS-4800 Touchscreen";
- poll_dev->input->phys = ts->phys;
+ input_dev->name = "TS-4800 Touchscreen";
+ input_dev->phys = ts->phys;
- input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH);
- input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0);
- input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0);
+ input_dev->open = ts4800_ts_open;
+ input_dev->close = ts4800_ts_close;
- error = input_register_polled_device(poll_dev);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+ 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);
+
+ error = input_setup_polling(input_dev, ts4800_ts_poll);
+ if (error) {
+ dev_err(&pdev->dev, "Unable to set up polling: %d\n", error);
+ return error;
+ }
+
+ input_set_poll_interval(input_dev, POLL_INTERVAL);
+
+ error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev,
- "Unabled to register polled input device (%d)\n",
- error);
+ "Unable to register input device: %d\n", error);
return error;
}
diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c
index 3b0e3fa..752eb7f 100644
--- a/drivers/input/touchscreen/tsc2007_iio.c
+++ b/drivers/input/touchscreen/tsc2007_iio.c
@@ -119,7 +119,6 @@
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;
diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c
index f017af8..1afc6bd 100644
--- a/drivers/input/touchscreen/wacom_i2c.c
+++ b/drivers/input/touchscreen/wacom_i2c.c
@@ -12,7 +12,6 @@
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
-#include <linux/gpio.h>
#include <asm/unaligned.h>
#define WACOM_CMD_QUERY0 0x04
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
index 607d1ae..bb1699e 100644
--- a/drivers/input/touchscreen/wm831x-ts.c
+++ b/drivers/input/touchscreen/wm831x-ts.c
@@ -290,7 +290,7 @@
default:
dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
pdata->isel);
- /* Fall through */
+ fallthrough;
case 200:
case 0:
wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
new file mode 100644
index 0000000..6df6f07
--- /dev/null
+++ b/drivers/input/touchscreen/zinitix.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/* Register Map */
+
+#define BT541_SWRESET_CMD 0x0000
+#define BT541_WAKEUP_CMD 0x0001
+
+#define BT541_IDLE_CMD 0x0004
+#define BT541_SLEEP_CMD 0x0005
+
+#define BT541_CLEAR_INT_STATUS_CMD 0x0003
+#define BT541_CALIBRATE_CMD 0x0006
+#define BT541_SAVE_STATUS_CMD 0x0007
+#define BT541_SAVE_CALIBRATION_CMD 0x0008
+#define BT541_RECALL_FACTORY_CMD 0x000f
+
+#define BT541_THRESHOLD 0x0020
+
+#define BT541_LARGE_PALM_REJECT_AREA_TH 0x003F
+
+#define BT541_DEBUG_REG 0x0115 /* 0~7 */
+
+#define BT541_TOUCH_MODE 0x0010
+#define BT541_CHIP_REVISION 0x0011
+#define BT541_FIRMWARE_VERSION 0x0012
+
+#define ZINITIX_USB_DETECT 0x116
+
+#define BT541_MINOR_FW_VERSION 0x0121
+
+#define BT541_VENDOR_ID 0x001C
+#define BT541_HW_ID 0x0014
+
+#define BT541_DATA_VERSION_REG 0x0013
+#define BT541_SUPPORTED_FINGER_NUM 0x0015
+#define BT541_EEPROM_INFO 0x0018
+#define BT541_INITIAL_TOUCH_MODE 0x0019
+
+#define BT541_TOTAL_NUMBER_OF_X 0x0060
+#define BT541_TOTAL_NUMBER_OF_Y 0x0061
+
+#define BT541_DELAY_RAW_FOR_HOST 0x007f
+
+#define BT541_BUTTON_SUPPORTED_NUM 0x00B0
+#define BT541_BUTTON_SENSITIVITY 0x00B2
+#define BT541_DUMMY_BUTTON_SENSITIVITY 0X00C8
+
+#define BT541_X_RESOLUTION 0x00C0
+#define BT541_Y_RESOLUTION 0x00C1
+
+#define BT541_POINT_STATUS_REG 0x0080
+#define BT541_ICON_STATUS_REG 0x00AA
+
+#define BT541_POINT_COORD_REG (BT541_POINT_STATUS_REG + 2)
+
+#define BT541_AFE_FREQUENCY 0x0100
+#define BT541_DND_N_COUNT 0x0122
+#define BT541_DND_U_COUNT 0x0135
+
+#define BT541_RAWDATA_REG 0x0200
+
+#define BT541_EEPROM_INFO_REG 0x0018
+
+#define BT541_INT_ENABLE_FLAG 0x00f0
+#define BT541_PERIODICAL_INTERRUPT_INTERVAL 0x00f1
+
+#define BT541_BTN_WIDTH 0x016d
+
+#define BT541_CHECKSUM_RESULT 0x012c
+
+#define BT541_INIT_FLASH 0x01d0
+#define BT541_WRITE_FLASH 0x01d1
+#define BT541_READ_FLASH 0x01d2
+
+#define ZINITIX_INTERNAL_FLAG_02 0x011e
+#define ZINITIX_INTERNAL_FLAG_03 0x011f
+
+#define ZINITIX_I2C_CHECKSUM_WCNT 0x016a
+#define ZINITIX_I2C_CHECKSUM_RESULT 0x016c
+
+/* Interrupt & status register flags */
+
+#define BIT_PT_CNT_CHANGE BIT(0)
+#define BIT_DOWN BIT(1)
+#define BIT_MOVE BIT(2)
+#define BIT_UP BIT(3)
+#define BIT_PALM BIT(4)
+#define BIT_PALM_REJECT BIT(5)
+#define BIT_RESERVED_0 BIT(6)
+#define BIT_RESERVED_1 BIT(7)
+#define BIT_WEIGHT_CHANGE BIT(8)
+#define BIT_PT_NO_CHANGE BIT(9)
+#define BIT_REJECT BIT(10)
+#define BIT_PT_EXIST BIT(11)
+#define BIT_RESERVED_2 BIT(12)
+#define BIT_ERROR BIT(13)
+#define BIT_DEBUG BIT(14)
+#define BIT_ICON_EVENT BIT(15)
+
+#define SUB_BIT_EXIST BIT(0)
+#define SUB_BIT_DOWN BIT(1)
+#define SUB_BIT_MOVE BIT(2)
+#define SUB_BIT_UP BIT(3)
+#define SUB_BIT_UPDATE BIT(4)
+#define SUB_BIT_WAIT BIT(5)
+
+#define DEFAULT_TOUCH_POINT_MODE 2
+#define MAX_SUPPORTED_FINGER_NUM 5
+
+#define CHIP_ON_DELAY 15 // ms
+#define FIRMWARE_ON_DELAY 40 // ms
+
+struct point_coord {
+ __le16 x;
+ __le16 y;
+ u8 width;
+ u8 sub_status;
+ // currently unused, but needed as padding:
+ u8 minor_width;
+ u8 angle;
+};
+
+struct touch_event {
+ __le16 status;
+ u8 finger_cnt;
+ u8 time_stamp;
+ struct point_coord point_coord[MAX_SUPPORTED_FINGER_NUM];
+};
+
+struct bt541_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct touchscreen_properties prop;
+ struct regulator_bulk_data supplies[2];
+ u32 zinitix_mode;
+};
+
+static int zinitix_read_data(struct i2c_client *client,
+ u16 reg, void *values, size_t length)
+{
+ __le16 reg_le = cpu_to_le16(reg);
+ int ret;
+
+ /* A single i2c_transfer() transaction does not work here. */
+ ret = i2c_master_send(client, (u8 *)®_le, sizeof(reg_le));
+ if (ret != sizeof(reg_le))
+ return ret < 0 ? ret : -EIO;
+
+ ret = i2c_master_recv(client, (u8 *)values, length);
+ if (ret != length)
+ return ret < 0 ? ret : -EIO; ;
+
+ return 0;
+}
+
+static int zinitix_write_u16(struct i2c_client *client, u16 reg, u16 value)
+{
+ __le16 packet[2] = {cpu_to_le16(reg), cpu_to_le16(value)};
+ int ret;
+
+ ret = i2c_master_send(client, (u8 *)packet, sizeof(packet));
+ if (ret != sizeof(packet))
+ return ret < 0 ? ret : -EIO;
+
+ return 0;
+}
+
+static int zinitix_write_cmd(struct i2c_client *client, u16 reg)
+{
+ __le16 reg_le = cpu_to_le16(reg);
+ int ret;
+
+ ret = i2c_master_send(client, (u8 *)®_le, sizeof(reg_le));
+ if (ret != sizeof(reg_le))
+ return ret < 0 ? ret : -EIO;
+
+ return 0;
+}
+
+static int zinitix_init_touch(struct bt541_ts_data *bt541)
+{
+ struct i2c_client *client = bt541->client;
+ int i;
+ int error;
+
+ error = zinitix_write_cmd(client, BT541_SWRESET_CMD);
+ if (error) {
+ dev_err(&client->dev, "Failed to write reset command\n");
+ return error;
+ }
+
+ error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, 0x0);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to reset interrupt enable flag\n");
+ return error;
+ }
+
+ /* initialize */
+ error = zinitix_write_u16(client, BT541_X_RESOLUTION,
+ bt541->prop.max_x);
+ if (error)
+ return error;
+
+ error = zinitix_write_u16(client, BT541_Y_RESOLUTION,
+ bt541->prop.max_y);
+ if (error)
+ return error;
+
+ error = zinitix_write_u16(client, BT541_SUPPORTED_FINGER_NUM,
+ MAX_SUPPORTED_FINGER_NUM);
+ if (error)
+ return error;
+
+ error = zinitix_write_u16(client, BT541_INITIAL_TOUCH_MODE,
+ bt541->zinitix_mode);
+ if (error)
+ return error;
+
+ error = zinitix_write_u16(client, BT541_TOUCH_MODE,
+ bt541->zinitix_mode);
+ if (error)
+ return error;
+
+ error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG,
+ BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE |
+ BIT_UP);
+ if (error)
+ return error;
+
+ /* clear queue */
+ for (i = 0; i < 10; i++) {
+ zinitix_write_cmd(client, BT541_CLEAR_INT_STATUS_CMD);
+ udelay(10);
+ }
+
+ return 0;
+}
+
+static int zinitix_init_regulators(struct bt541_ts_data *bt541)
+{
+ struct i2c_client *client = bt541->client;
+ int error;
+
+ bt541->supplies[0].supply = "vdd";
+ bt541->supplies[1].supply = "vddo";
+ error = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(bt541->supplies),
+ bt541->supplies);
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to get regulators: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int zinitix_send_power_on_sequence(struct bt541_ts_data *bt541)
+{
+ int error;
+ struct i2c_client *client = bt541->client;
+
+ error = zinitix_write_u16(client, 0xc000, 0x0001);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to send power sequence(vendor cmd enable)\n");
+ return error;
+ }
+ udelay(10);
+
+ error = zinitix_write_cmd(client, 0xc004);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to send power sequence (intn clear)\n");
+ return error;
+ }
+ udelay(10);
+
+ error = zinitix_write_u16(client, 0xc002, 0x0001);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to send power sequence (nvm init)\n");
+ return error;
+ }
+ mdelay(2);
+
+ error = zinitix_write_u16(client, 0xc001, 0x0001);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to send power sequence (program start)\n");
+ return error;
+ }
+ msleep(FIRMWARE_ON_DELAY);
+
+ return 0;
+}
+
+static void zinitix_report_finger(struct bt541_ts_data *bt541, int slot,
+ const struct point_coord *p)
+{
+ input_mt_slot(bt541->input_dev, slot);
+ input_mt_report_slot_state(bt541->input_dev, MT_TOOL_FINGER, true);
+ touchscreen_report_pos(bt541->input_dev, &bt541->prop,
+ le16_to_cpu(p->x), le16_to_cpu(p->y), true);
+ input_report_abs(bt541->input_dev, ABS_MT_TOUCH_MAJOR, p->width);
+}
+
+static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
+{
+ struct bt541_ts_data *bt541 = bt541_handler;
+ struct i2c_client *client = bt541->client;
+ struct touch_event touch_event;
+ int error;
+ int i;
+
+ memset(&touch_event, 0, sizeof(struct touch_event));
+
+ error = zinitix_read_data(bt541->client, BT541_POINT_STATUS_REG,
+ &touch_event, sizeof(struct touch_event));
+ if (error) {
+ dev_err(&client->dev, "Failed to read in touchpoint struct\n");
+ goto out;
+ }
+
+ for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++)
+ if (touch_event.point_coord[i].sub_status & SUB_BIT_EXIST)
+ zinitix_report_finger(bt541, i,
+ &touch_event.point_coord[i]);
+
+ input_mt_sync_frame(bt541->input_dev);
+ input_sync(bt541->input_dev);
+
+out:
+ zinitix_write_cmd(bt541->client, BT541_CLEAR_INT_STATUS_CMD);
+ return IRQ_HANDLED;
+}
+
+static int zinitix_start(struct bt541_ts_data *bt541)
+{
+ int error;
+
+ error = regulator_bulk_enable(ARRAY_SIZE(bt541->supplies),
+ bt541->supplies);
+ if (error) {
+ dev_err(&bt541->client->dev,
+ "Failed to enable regulators: %d\n", error);
+ return error;
+ }
+
+ msleep(CHIP_ON_DELAY);
+
+ error = zinitix_send_power_on_sequence(bt541);
+ if (error) {
+ dev_err(&bt541->client->dev,
+ "Error while sending power-on sequence: %d\n", error);
+ return error;
+ }
+
+ error = zinitix_init_touch(bt541);
+ if (error) {
+ dev_err(&bt541->client->dev,
+ "Error while configuring touch IC\n");
+ return error;
+ }
+
+ enable_irq(bt541->client->irq);
+
+ return 0;
+}
+
+static int zinitix_stop(struct bt541_ts_data *bt541)
+{
+ int error;
+
+ disable_irq(bt541->client->irq);
+
+ error = regulator_bulk_disable(ARRAY_SIZE(bt541->supplies),
+ bt541->supplies);
+ if (error) {
+ dev_err(&bt541->client->dev,
+ "Failed to disable regulators: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int zinitix_input_open(struct input_dev *dev)
+{
+ struct bt541_ts_data *bt541 = input_get_drvdata(dev);
+
+ return zinitix_start(bt541);
+}
+
+static void zinitix_input_close(struct input_dev *dev)
+{
+ struct bt541_ts_data *bt541 = input_get_drvdata(dev);
+
+ zinitix_stop(bt541);
+}
+
+static int zinitix_init_input_dev(struct bt541_ts_data *bt541)
+{
+ struct input_dev *input_dev;
+ int error;
+
+ input_dev = devm_input_allocate_device(&bt541->client->dev);
+ if (!input_dev) {
+ dev_err(&bt541->client->dev,
+ "Failed to allocate input device.");
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(input_dev, bt541);
+ bt541->input_dev = input_dev;
+
+ input_dev->name = "Zinitix Capacitive TouchScreen";
+ input_dev->phys = "input/ts";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->open = zinitix_input_open;
+ input_dev->close = zinitix_input_close;
+
+ input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
+ input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &bt541->prop);
+ if (!bt541->prop.max_x || !bt541->prop.max_y) {
+ dev_err(&bt541->client->dev,
+ "Touchscreen-size-x and/or touchscreen-size-y not set in dts\n");
+ return -EINVAL;
+ }
+
+ error = input_mt_init_slots(input_dev, MAX_SUPPORTED_FINGER_NUM,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error) {
+ dev_err(&bt541->client->dev,
+ "Failed to initialize MT slots: %d", error);
+ return error;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&bt541->client->dev,
+ "Failed to register input device: %d", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int zinitix_ts_probe(struct i2c_client *client)
+{
+ struct bt541_ts_data *bt541;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev,
+ "Failed to assert adapter's support for plain I2C.\n");
+ return -ENXIO;
+ }
+
+ bt541 = devm_kzalloc(&client->dev, sizeof(*bt541), GFP_KERNEL);
+ if (!bt541)
+ return -ENOMEM;
+
+ bt541->client = client;
+ i2c_set_clientdata(client, bt541);
+
+ error = zinitix_init_regulators(bt541);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to initialize regulators: %d\n", error);
+ return error;
+ }
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, zinitix_ts_irq_handler,
+ IRQF_ONESHOT,
+ client->name, bt541);
+ if (error) {
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+ return error;
+ }
+
+ error = zinitix_init_input_dev(bt541);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to initialize input device: %d\n", error);
+ return error;
+ }
+
+ error = device_property_read_u32(&client->dev, "zinitix,mode",
+ &bt541->zinitix_mode);
+ if (error < 0) {
+ /* fall back to mode 2 */
+ bt541->zinitix_mode = DEFAULT_TOUCH_POINT_MODE;
+ }
+
+ if (bt541->zinitix_mode != 2) {
+ /*
+ * If there are devices that don't support mode 2, support
+ * for other modes (0, 1) will be needed.
+ */
+ dev_err(&client->dev,
+ "Malformed zinitix,mode property, must be 2 (supplied: %d)\n",
+ bt541->zinitix_mode);
+ return -EINVAL;
+ }
+
+ irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
+
+ return 0;
+}
+
+static int __maybe_unused zinitix_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bt541_ts_data *bt541 = i2c_get_clientdata(client);
+
+ mutex_lock(&bt541->input_dev->mutex);
+
+ if (bt541->input_dev->users)
+ zinitix_stop(bt541);
+
+ mutex_unlock(&bt541->input_dev->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused zinitix_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bt541_ts_data *bt541 = i2c_get_clientdata(client);
+ int ret = 0;
+
+ mutex_lock(&bt541->input_dev->mutex);
+
+ if (bt541->input_dev->users)
+ ret = zinitix_start(bt541);
+
+ mutex_unlock(&bt541->input_dev->mutex);
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(zinitix_pm_ops, zinitix_suspend, zinitix_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id zinitix_of_match[] = {
+ { .compatible = "zinitix,bt541" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, zinitix_of_match);
+#endif
+
+static struct i2c_driver zinitix_ts_driver = {
+ .probe_new = zinitix_ts_probe,
+ .driver = {
+ .name = "Zinitix-TS",
+ .pm = &zinitix_pm_ops,
+ .of_match_table = of_match_ptr(zinitix_of_match),
+ },
+};
+module_i2c_driver(zinitix_ts_driver);
+
+MODULE_AUTHOR("Michael Srba <Michael.Srba@seznam.cz>");
+MODULE_DESCRIPTION("Zinitix touchscreen driver");
+MODULE_LICENSE("GPL v2");