summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/elants_i2c.c8
-rw-r--r--drivers/input/touchscreen/imx6ul_tsc.c47
-rw-r--r--drivers/input/touchscreen/raydium_i2c_ts.c131
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c37
-rw-r--r--drivers/input/touchscreen/stmfts.c2
-rw-r--r--drivers/input/touchscreen/zinitix.c581
8 files changed, 717 insertions, 102 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 35c867b2d9a7..f012fe746df0 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1322,4 +1322,16 @@ config TOUCHSCREEN_IQS5XX
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 30d1e1b42492..6233541e9173 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -111,3 +111,4 @@ obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o
obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o
+obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index b0bd5bb079be..50c348297e38 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -90,7 +90,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 0xE0
+#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
@@ -134,6 +134,7 @@ struct elants_data {
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;
@@ -459,6 +460,9 @@ static int elants_i2c_query_ts_info(struct elants_data *ts)
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),
@@ -1325,6 +1329,8 @@ static int elants_i2c_probe(struct i2c_client *client,
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);
diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c
index 9ed258854349..cd369f9ac5e6 100644
--- a/drivers/input/touchscreen/imx6ul_tsc.c
+++ b/drivers/input/touchscreen/imx6ul_tsc.c
@@ -315,9 +315,8 @@ static irqreturn_t adc_irq_fn(int irq, void *dev_id)
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 @@ disable_adc_clk:
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 @@ static int __maybe_unused imx6ul_tsc_suspend(struct device *dev)
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,22 +538,11 @@ static int __maybe_unused imx6ul_tsc_resume(struct device *dev)
mutex_lock(&input_dev->mutex);
- if (input_dev->users) {
- 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 (input_dev->users)
+ retval = imx6ul_tsc_start(tsc);
-out:
mutex_unlock(&input_dev->mutex);
+
return retval;
}
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
index fe245439adee..e694a9b2b1e5 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,82 @@ struct raydium_data {
bool wake_irq_enabled;
};
-static int raydium_i2c_send(struct i2c_client *client,
- u8 addr, const void *data, size_t len)
+static int raydium_i2c_xfer(struct i2c_client *client,
+ u32 addr, void *data, size_t len, bool is_read)
{
- u8 *buf;
- int tries = 0;
- int ret;
-
- buf = kmalloc(len + 1, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- buf[0] = addr;
- memcpy(buf + 1, data, len);
-
- do {
- ret = i2c_master_send(client, buf, len + 1);
- if (likely(ret == len + 1))
- break;
-
- msleep(20);
- } 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;
- }
+ struct raydium_bank_switch_header {
+ u8 cmd;
+ __be32 be_addr;
+ } __packed header = {
+ .cmd = RM_CMD_BANK_SWITCH,
+ .be_addr = cpu_to_be32(addr),
+ };
- return 0;
-}
+ u8 reg_addr = addr & 0xff;
-static int raydium_i2c_read(struct i2c_client *client,
- u8 addr, void *data, size_t len)
-{
struct i2c_msg xfer[] = {
{
.addr = client->addr,
+ .len = sizeof(header),
+ .buf = (u8 *)&header,
+ },
+ {
+ .addr = client->addr,
.len = 1,
- .buf = &addr,
+ .buf = &reg_addr,
},
{
.addr = client->addr,
- .flags = I2C_M_RD,
.len = len,
.buf = data,
+ .flags = is_read ? I2C_M_RD : 0,
}
};
+
+ /*
+ * 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;
+ size_t xfer_count = ARRAY_SIZE(xfer) - xfer_start_idx;
int ret;
- ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
- if (unlikely(ret != ARRAY_SIZE(xfer)))
- return ret < 0 ? ret : -EIO;
+ ret = i2c_transfer(client->adapter, &xfer[xfer_start_idx], xfer_count);
+ if (likely(ret == xfer_count))
+ return 0;
+
+ return ret < 0 ? ret : -EIO;
+}
- return 0;
+static int raydium_i2c_send(struct i2c_client *client,
+ u32 addr, const void *data, size_t len)
+{
+ int tries = 0;
+ int error;
+
+ do {
+ error = raydium_i2c_xfer(client, addr, (void *)data, len,
+ false);
+ if (likely(!error))
+ return 0;
+
+ msleep(RM_RETRY_DELAY_MS);
+ } while (++tries < RM_MAX_RETRIES);
+
+ dev_err(&client->dev, "%s failed: %d\n", __func__, error);
+ return error;
}
-static int raydium_i2c_read_message(struct i2c_client *client,
- u32 addr, void *data, size_t len)
+static int raydium_i2c_read(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);
-
- be_addr = cpu_to_be32(addr);
-
- 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, data, xfer_len, true);
+ if (unlikely(error))
return error;
len -= xfer_len;
@@ -223,27 +223,13 @@ static int raydium_i2c_read_message(struct i2c_client *client,
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 +281,8 @@ static int raydium_i2c_query_ts_info(struct raydium_data *ts)
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;
@@ -834,8 +819,8 @@ static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
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 82920ff46f72..2e70c0b79444 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -20,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/stmfts.c b/drivers/input/touchscreen/stmfts.c
index df946869d4cd..9a64e1dbc04a 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -479,7 +479,7 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev,
mutex_lock(&sdata->mutex);
- if (value & sdata->hover_enabled)
+ if (value && sdata->hover_enabled)
goto out;
if (sdata->running)
diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
new file mode 100644
index 000000000000..1acc2eb2bcb3
--- /dev/null
+++ b/drivers/input/touchscreen/zinitix.c
@@ -0,0 +1,581 @@
+// 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 *)&reg_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 *)&reg_le, sizeof(reg_le));
+ if (ret != sizeof(reg_le))
+ return ret < 0 ? ret : -EIO;
+
+ return 0;
+}
+
+static bool 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 = 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);
+ 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;
+ }
+
+ 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");