touchscreen: cy8c_tma_ts driver
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index e77f3dc..c42d497 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1014,6 +1014,11 @@
help
This enables support for Atmel over I2C based touchscreens.
+config TOUCHSCREEN_CYPRESS_TMA
+ tristate "Cypress TMA300 based touchscreens"
+ depends on I2C
+ default n
+
config TOUCHSCREEN_CYPRESS_CS
tristate "Cypress CY8C20x34 based touchkey"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ba0e55a..a899a22 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -89,3 +89,4 @@
obj-$(CONFIG_TOUCHSCREEN_ATMEL_224E) += atmel_224e.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CS) += cy8c_cs.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_3K) += synaptics_3200.o rmi_dev.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_TMA) += cy8c_tma_ts.o
diff --git a/drivers/input/touchscreen/cy8c_tma_ts.c b/drivers/input/touchscreen/cy8c_tma_ts.c
new file mode 100644
index 0000000..8772ca6
--- /dev/null
+++ b/drivers/input/touchscreen/cy8c_tma_ts.c
@@ -0,0 +1,1260 @@
+/* drivers/input/touchscreen/cy8c_tma_ts.c
+ *
+ * Copyright (C) 2010 HTC Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cy8c_tma_ts.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#define CY8C_I2C_RETRY_TIMES 10
+
+struct cy8c_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ uint16_t version;
+ uint8_t id;
+ uint16_t intr;
+ int (*power) (int on);
+ int (*wake)(void);
+ uint8_t unlock_attr;
+ uint8_t unlock_page;
+ struct early_suspend early_suspend;
+ uint8_t debug_log_level;
+ uint8_t orient;
+ uint8_t timeout;
+ uint8_t interval;
+ uint8_t diag_command;
+ uint8_t first_pressed;
+ int pre_finger_data[2];
+ uint8_t x_channel;
+ uint8_t y_channel;
+ uint8_t finger_count;
+ uint16_t finger_id;
+ uint8_t p_finger_count;
+ uint16_t p_finger_id;
+ uint16_t *filter_level;
+ uint8_t grip_suppression;
+ uint8_t ambiguous_state;
+ uint8_t auto_reset;
+ uint32_t sameFilter[3];
+ int (*reset)(void);
+ uint8_t flag_htc_event;
+ uint8_t suspend;
+};
+
+static struct cy8c_ts_data *private_ts;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_ts_early_suspend(struct early_suspend *h);
+static void cy8c_ts_late_resume(struct early_suspend *h);
+#endif
+
+static int cy8c_init_panel(struct cy8c_ts_data *ts);
+static int cy8c_reset_baseline(void);
+
+static DEFINE_MUTEX(cy8c_mutex);
+
+int i2c_cy8c_read(struct i2c_client *client, uint8_t addr, uint8_t *data, uint8_t length)
+{
+ int retry;
+ struct cy8c_ts_data *ts = private_ts;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = data,
+ }
+ };
+
+ for (retry = 0; retry < CY8C_I2C_RETRY_TIMES; retry++) {
+ if (i2c_transfer(client->adapter, msg, 2) == 2)
+ break;
+ if (ts->wake)
+ ts->wake();
+ else
+ msleep(10);
+ }
+ if (retry == CY8C_I2C_RETRY_TIMES) {
+ printk(KERN_INFO "i2c_read_block retry over %d\n",
+ CY8C_I2C_RETRY_TIMES);
+
+ return -EIO;
+ }
+ return 0;
+
+}
+
+int i2c_cy8c_write(struct i2c_client *client, uint8_t addr, uint8_t *data, uint8_t length)
+{
+ int retry, loop_i;
+ struct cy8c_ts_data *ts = private_ts;
+ uint8_t buf[length + 1];
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = length + 1,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = addr;
+ for (loop_i = 0; loop_i < length; loop_i++)
+ buf[loop_i + 1] = data[loop_i];
+
+ for (retry = 0; retry < CY8C_I2C_RETRY_TIMES; retry++) {
+ if (i2c_transfer(client->adapter, msg, 1) == 1)
+ break;
+ if (ts->wake)
+ ts->wake();
+ else
+ msleep(10);
+ }
+
+ if (retry == CY8C_I2C_RETRY_TIMES) {
+ printk(KERN_ERR "i2c_write_block retry over %d\n",
+ CY8C_I2C_RETRY_TIMES);
+
+ return -EIO;
+ }
+ return 0;
+
+}
+
+int i2c_cy8c_write_byte_data(struct i2c_client *client, uint8_t addr, uint8_t value)
+{
+ return i2c_cy8c_write(client, addr, &value, 1);;
+}
+
+static int cy8c_data_toggle(struct cy8c_ts_data *ts)
+{
+ uint8_t buf = 0;
+ int ret = 0;
+ ret = i2c_cy8c_read(ts->client, 0x00, &buf, 1);
+ if (!ret)
+ return i2c_cy8c_write_byte_data(ts->client, 0x00, buf ^= BIT(7));
+
+ return -1;
+}
+
+static ssize_t cy8c_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret = 0;
+ struct cy8c_ts_data *ts_data;
+ ts_data = private_ts;
+ sprintf(buf, "%s_x%4.4X_%2.2X\n", CYPRESS_TMA_NAME,
+ ts_data->version, ts_data->id);
+ ret = strlen(buf);
+ return ret;
+}
+
+static DEVICE_ATTR(vendor, S_IRUGO, cy8c_vendor_show, NULL);
+
+static ssize_t cy8c_attn_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret = 0, value = 0;
+ struct cy8c_ts_data *ts_data;
+ ts_data = private_ts;
+
+ value = gpio_get_value(ts_data->intr);
+ sprintf(buf, "attn = %X\n", value);
+
+ ret = strlen(buf);
+ return ret;
+}
+
+static DEVICE_ATTR(attn, S_IRUGO, cy8c_attn_show, NULL);
+
+static long cy8c_reg_addr;
+
+static ssize_t cy8c_register_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret = 0;
+ uint8_t ptr[1] = { 0 };
+ struct cy8c_ts_data *ts_data;
+ ts_data = private_ts;
+ if (ts_data->wake)
+ ts_data->wake();
+ if (i2c_cy8c_read(ts_data->client, cy8c_reg_addr, ptr, 1) < 0) {
+ printk(KERN_ERR "%s: read fail\n", __func__);
+ return ret;
+ }
+ ret += sprintf(buf, "addr: %ld, data: %d\n", cy8c_reg_addr, ptr[0]);
+ return ret;
+}
+
+static ssize_t cy8c_register_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct cy8c_ts_data *ts_data;
+ char buf_tmp[4];
+ long write_da;
+
+ ts_data = private_ts;
+ memset(buf_tmp, 0x0, sizeof(buf_tmp));
+ if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':' &&
+ (buf[5] == ':' || buf[5] == '\n')) {
+ memcpy(buf_tmp, buf + 2, 3);
+ ret = strict_strtol(buf_tmp, 16, &cy8c_reg_addr);
+ printk(KERN_DEBUG "%s: set cy8c_reg_addr is: %ld\n",
+ __func__, cy8c_reg_addr);
+ if (buf[0] == 'w' && buf[5] == ':' && buf[9] == '\n') {
+ memcpy(buf_tmp, buf + 6, 3);
+ ret = strict_strtol(buf_tmp, 10, &write_da);
+ printk(KERN_DEBUG "write addr: 0x%lX, data: 0x%lX\n",
+ cy8c_reg_addr, write_da);
+ if (ts_data->wake)
+ ts_data->wake();
+ ret = i2c_cy8c_write_byte_data(ts_data->client,
+ cy8c_reg_addr, write_da);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: write fail(%d)\n",
+ __func__, ret);
+ }
+ }
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(register, (S_IWUSR|S_IRUGO),
+ cy8c_register_show, cy8c_register_store);
+
+
+static ssize_t cy8c_debug_level_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cy8c_ts_data *ts_data;
+ size_t count = 0;
+ ts_data = private_ts;
+
+ count += sprintf(buf, "%d\n", ts_data->debug_log_level);
+
+ return count;
+}
+
+static ssize_t cy8c_debug_level_dump(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cy8c_ts_data *ts_data;
+ ts_data = private_ts;
+ if (buf[0] >= '0' && buf[0] <= '9' && buf[1] == '\n')
+ ts_data->debug_log_level = buf[0] - '0';
+
+ return count;
+}
+
+static DEVICE_ATTR(debug_level, (S_IWUSR|S_IRUGO),
+ cy8c_debug_level_show, cy8c_debug_level_dump);
+
+static ssize_t cy8c_htc_event_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cy8c_ts_data *ts_data;
+ size_t count = 0;
+ ts_data = private_ts;
+
+ count += sprintf(buf, "%d\n", ts_data->flag_htc_event);
+
+ return count;
+}
+
+static ssize_t cy8c_htc_event_write(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cy8c_ts_data *ts_data;
+ int ret = 0;
+ long value;
+ ts_data = private_ts;
+
+ ret = strict_strtol(buf, 10, &value);
+ ts_data->flag_htc_event = value;
+
+ return count;
+}
+
+static DEVICE_ATTR(htc_event, (S_IWUSR|S_IRUGO), cy8c_htc_event_read, cy8c_htc_event_write);
+
+static ssize_t cy8c_diag_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cy8c_ts_data *ts_data;
+ size_t count = 0;
+ uint8_t data[250];
+ uint8_t loop_i;
+ uint16_t num_nodes;
+ ts_data = private_ts;
+ memset(data, 0x0, sizeof(data));
+ num_nodes = ts_data->x_channel * ts_data->y_channel;
+
+ if (ts_data->diag_command < 4 || ts_data->diag_command > 7)
+ return count;
+
+ disable_irq(ts_data->client->irq);
+ if (ts_data->wake)
+ ts_data->wake();
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 1);
+ i2c_cy8c_write_byte_data(ts_data->client, 0x00,
+ (data[0] & 0x8F) | (ts_data->diag_command << 4));
+ msleep(80);
+ for (loop_i = 0; loop_i < 20; loop_i++) {
+ if (!gpio_get_value(ts_data->intr))
+ break;
+ msleep(20);
+ }
+ printk(KERN_DEBUG "[%d] change mode to %d\n", loop_i, ts_data->diag_command);
+ count += sprintf(buf, "Channel: %d * %d\n", ts_data->x_channel, ts_data->y_channel);
+
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 7 + num_nodes);
+ if ((data[1] & 0x10) == 0x10) {
+ cy8c_init_panel(ts_data);
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 7 + num_nodes);
+ }
+ if (ts_data->diag_command == 6) {
+ if ((data[1] & 0x40) == 0x40)
+ count += sprintf(buf + count, "Global IDAC:\n");
+ else
+ count += sprintf(buf + count, "Local IDAC:\n");
+ } else if (ts_data->diag_command == 7) {
+ if ((data[1] & 0x40) == 0x40)
+ count += sprintf(buf + count, "RAW count:\n");
+ else
+ count += sprintf(buf + count, "Baseline:\n");
+ }
+ for (loop_i = 7; loop_i < 7 + num_nodes; loop_i++) {
+ count += sprintf(buf + count, "%5d", data[loop_i]);
+ if ((loop_i - 6) % ts_data->y_channel == 0)
+ count += sprintf(buf + count, "\n");
+ }
+ count += sprintf(buf + count, "\n");
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 1);
+ i2c_cy8c_write_byte_data(ts_data->client, 0x00, ((data[0] ^= BIT(7)) & 0x8F));
+ msleep(40);
+ enable_irq(ts_data->client->irq);
+
+ return count;
+}
+
+static ssize_t cy8c_diag_dump(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cy8c_ts_data *ts_data;
+ ts_data = private_ts;
+
+ if (buf[0] >= 0x31 && buf[0] <= 0x34)
+ ts_data->diag_command = buf[0] - 0x2D;
+ else
+ ts_data->diag_command = 0;
+ printk(KERN_INFO "ts_data->diag_command = %X\n", ts_data->diag_command);
+ return count;
+}
+
+static DEVICE_ATTR(diag, (S_IWUSR|S_IRUGO),
+ cy8c_diag_show, cy8c_diag_dump);
+
+static ssize_t cy8c_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cy8c_ts_data *ts_data;
+ size_t count = 0;
+ uint8_t diag_cmd = 7;
+ uint8_t data[256];
+ int i, loop_i;
+ uint16_t num_nodes;
+ ts_data = private_ts;
+ memset(data, 0x0, sizeof(data));
+ num_nodes = ts_data->x_channel * ts_data->y_channel;
+
+ disable_irq(ts_data->client->irq);
+ if (ts_data->wake)
+ ts_data->wake();
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 1);
+ i2c_cy8c_write_byte_data(ts_data->client, 0x00,
+ (data[0] & 0x8F) | (diag_cmd << 4));
+ msleep(80);
+ for (loop_i = 0; loop_i < 20; loop_i++) {
+ if (!gpio_get_value(ts_data->intr))
+ break;
+ msleep(20);
+ }
+
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 1);
+ printk(KERN_INFO "[cal_show]change mode to 0x%X\n", data[0]);
+
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 7 + num_nodes);
+ if ((data[1] & 0x40) == 0x40) {
+ for (i = 0; i < 10; i++) {
+ i2c_cy8c_write_byte_data(ts_data->client, 0x00,
+ ((data[0] ^= BIT(7)) & 0x8F) | (diag_cmd << 4));
+ msleep(80);
+ for (loop_i = 0; loop_i < 20; loop_i++) {
+ if (!gpio_get_value(ts_data->intr))
+ break;
+ msleep(20);
+ }
+
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 2);
+ if (!(data[1] & 0x40))
+ break;
+ msleep(10);
+ }
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 7 + num_nodes);
+ }
+
+ for (i = 7; i < 7 + num_nodes; i++) {
+ count += sprintf(buf + count, "%5d", data[i]);
+ if ((i - 6) % ts_data->y_channel == 0)
+ count += sprintf(buf + count, "\n");
+ }
+ count += sprintf(buf + count, "\n");
+
+ /* Enter operation mode */
+ i2c_cy8c_read(ts_data->client, 0x00, data, 1);
+ if ((data[0] & 0x70) == 0x70)
+ i2c_cy8c_write_byte_data(ts_data->client, 0x00, ((data[0] ^= BIT(7)) & 0x8F));
+ mdelay(64);
+
+ i2c_cy8c_read(ts_data->client, 0x00, &data[0], 2);
+ printk(KERN_INFO "[cal_show]change mode to 0x%X 0x%X\n", data[0], data[1]);
+ enable_irq(ts_data->client->irq);
+ return count;
+}
+
+static ssize_t cy8c_cal_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cy8c_ts_data *ts_data;
+ uint8_t cal_command[3] = {0x20, 0x00, 0x00};
+ uint8_t data[3] = {0};
+ uint8_t loop_i;
+ ts_data = private_ts;
+
+ if (buf[0] == 0x31) {
+ if (ts_data->wake)
+ ts_data->wake();
+ i2c_cy8c_read(ts_data->client, 0x00, data, 2);
+ if ((data[1] & 0x10) == 0x10) {
+ printk(KERN_INFO "Bootloader mode to OP mode2\n");
+ cy8c_init_panel(ts_data);
+ }
+
+ disable_irq(ts_data->client->irq);
+
+ if (!i2c_cy8c_read(ts_data->client, 0x00, data, 3)) {
+ if ((data[2] & 0x0F) >= 1) {
+ printk(KERN_INFO "[cal_store]Number of touches %d\n", data[2] & 0x0F);
+ enable_irq(ts_data->client->irq);
+ return count;
+ }
+ }
+
+ /* Enter System mode */
+ if (!i2c_cy8c_read(ts_data->client, 0x00, &data[0], 1)) {
+ i2c_cy8c_write_byte_data(ts_data->client, 0x00,
+ (data[0] & 0x8F) | (1 << 4));
+ mdelay(64);
+
+ i2c_cy8c_write(ts_data->client, 0x02, &cal_command[0], 3);
+ cy8c_data_toggle(ts_data);
+ mdelay(500);
+ for (loop_i = 0; loop_i < 100; loop_i++) {
+ i2c_cy8c_read(ts_data->client, 0x00, data, 3);
+ if (data[1] == 0x86) {
+ printk(KERN_INFO "[cal_store][%d]status return 0x%X\n",
+ loop_i + 1, data[1]);
+ break;
+ } else
+ mdelay(10);
+ }
+ }
+ /* Enter operation mode */
+ if (!i2c_cy8c_read(ts_data->client, 0x00, data, 1)) {
+ if ((data[0] & 0x70) == 0x10) {
+ i2c_cy8c_write_byte_data(ts_data->client, 0x00,
+ ((data[0] ^= BIT(7)) & 0x8F));
+ }
+ mdelay(64);
+ }
+
+ if (!i2c_cy8c_read(ts_data->client, 0x00, &data[0], 2))
+ printk(KERN_INFO "[cal_store]change mode to 0x%X 0x%X\n", data[0], data[1]);
+
+ enable_irq(ts_data->client->irq);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(calibration, (S_IWUSR|S_IRUGO),
+ cy8c_cal_show, cy8c_cal_store);
+
+static ssize_t cy8c_unlock_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cy8c_ts_data *ts_data;
+ int unlock = -1;
+ ts_data = private_ts;
+
+ if (!sscanf(buf, "%d", &unlock)) {
+ printk(KERN_INFO "cy8c_unlock_store sscanf return failed\n");
+ return count;
+ }
+
+ printk(KERN_INFO "Touch: unlock change to (%d %d %d)\n", unlock, ts_data->finger_count, ts_data->unlock_attr);
+ if (unlock == 1 && ts_data->unlock_attr)
+ ts_data->unlock_page = 1;
+
+ mutex_lock(&cy8c_mutex);
+
+ if (unlock == 2 && (!ts_data->finger_count)
+ && ts_data->unlock_attr && ts_data->suspend == 0) {
+ ts_data->unlock_page = 0;
+ cy8c_reset_baseline();
+ } else if (unlock == 2 && ts_data->finger_count
+ && ts_data->unlock_attr && ts_data->suspend == 0) {
+ ts_data->unlock_page = 0;
+ }
+ mutex_unlock(&cy8c_mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(unlock, (S_IWUSR|S_IRUGO),
+ NULL, cy8c_unlock_store);
+
+static ssize_t cy8c_hw_reset(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cy8c_ts_data *ts_data;
+ ts_data = private_ts;
+
+ printk(KERN_INFO "[TP] Reset touch chip!\n");
+ if (ts_data)
+ if (ts_data->reset)
+ ts_data->reset();
+
+ return count;
+}
+
+static DEVICE_ATTR(hw_reset, S_IWUSR,
+ NULL, cy8c_hw_reset);
+
+static struct kobject *android_touch_kobj;
+
+static int cy8c_touch_sysfs_init(void)
+{
+ int ret;
+ android_touch_kobj = kobject_create_and_add("android_touch", NULL);
+ if (android_touch_kobj == NULL) {
+ printk(KERN_ERR "%s: subsystem_register failed\n", __func__);
+ ret = -ENOMEM;
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_vendor.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ cy8c_reg_addr = 0;
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_register.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_debug_level.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_diag.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_calibration.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_htc_event.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_unlock.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_hw_reset.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ ret = sysfs_create_file(android_touch_kobj, &dev_attr_attn.attr);
+ if (ret) {
+ printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__);
+ return ret;
+ }
+ return 0;
+}
+
+static void cy8c_touch_sysfs_deinit(void)
+{
+ sysfs_remove_file(android_touch_kobj, &dev_attr_diag.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_debug_level.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_register.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_vendor.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_htc_event.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_unlock.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_calibration.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_hw_reset.attr);
+ sysfs_remove_file(android_touch_kobj, &dev_attr_attn.attr);
+ kobject_del(android_touch_kobj);
+}
+
+static int cy8c_init_panel(struct cy8c_ts_data *ts)
+{
+ uint8_t buf = 0, loop_i;
+ uint8_t sec_key[] = {0x00, 0xFF, 0xA5, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+ if (i2c_cy8c_write(ts->client, 0x00, sec_key, 11) < 0) {
+ printk(KERN_ERR "TOUCH_ERR: init failed to system info mode\n");
+ return -1;
+ }
+ msleep(80);
+ for (loop_i = 0; loop_i < 10; loop_i++) {
+ if (!gpio_get_value(ts->intr))
+ break;
+ msleep(10);
+ }
+ if (!ts->id)
+ if (i2c_cy8c_read(ts->client, 0x17, &ts->id, 1) < 0) {
+ printk(KERN_ERR "TOUCH_ERR: init failed to check id\n");
+ return -1;
+ }
+ if (ts->timeout)
+ i2c_cy8c_write_byte_data(ts->client, 0x1E, ts->timeout);
+ if (ts->interval)
+ i2c_cy8c_write_byte_data(ts->client, 0x1F, ts->interval);
+
+ if (!i2c_cy8c_read(ts->client, 0x00, &buf, 1)) {
+ if ((buf & 0x70) == 0x10)
+ i2c_cy8c_write_byte_data(ts->client, 0x00, ((buf ^= BIT(7)) & 0x8F));
+ msleep(40);
+ }
+
+ return 0;
+}
+
+static int cy8c_reset_baseline(void)
+{
+ struct cy8c_ts_data *ts_data;
+ uint8_t data[3] = {0};
+ ts_data = private_ts;
+
+ i2c_cy8c_read(ts_data->client, 0x00, data, 2);
+ if ((data[1] & 0x10) == 0x10) {
+ printk(KERN_INFO "Bootloader mode to OP mode3\n");
+ cy8c_init_panel(ts_data);
+ }
+ i2c_cy8c_read(ts_data->client, 0x1B, &data[0], 1);
+ if ((data[0] & 0x01) == 0) {
+ i2c_cy8c_write_byte_data(ts_data->client, 0x1B,
+ (data[0] | 0x01));
+ printk(KERN_INFO "[TOUCH] cy8c reset baseline\n");
+ return 0;
+ } else {
+ printk(KERN_INFO "[TOUCH] cy8c reset baseline bypass\n");
+ return 1;
+ }
+}
+
+static void cy8c_orient(uint16_t *x, uint16_t *y, uint8_t orient)
+{
+ if (orient & 0x01)
+ *x = 1023 - *x;
+ if (orient & 0x02)
+ *y = 1023 - *y;
+ if (orient & 0x04)
+ swap(*x, *y);
+}
+
+static irqreturn_t cy8c_ts_irq_thread(int irq, void *ptr)
+{
+ struct cy8c_ts_data *ts = ptr;
+ uint8_t buf[32] = {0}, loop_i, loop_j;
+
+ i2c_cy8c_read(ts->client, 0x00, buf, 32);
+ if (ts->debug_log_level & 0x1) {
+ for (loop_i = 0; loop_i < 32; loop_i++) {
+ printk(KERN_INFO "0x%2.2X ", buf[loop_i]);
+ if (loop_i % 16 == 15)
+ printk("\n");
+ }
+ }
+
+ if ((buf[1] & 0x10) == 0x10) {
+ printk(KERN_INFO "Bootloader mode to OP mode\n");
+ if (cy8c_init_panel(ts) < 0)
+ printk(KERN_ERR "TOUCH_ERR: %s init failed\n",
+ __func__);
+ }
+
+ if (buf[2] & 0x10)
+ printk(KERN_INFO "[TOUCH] cy8c large object detected\n");
+ if ((buf[2] & 0x0F) >= 1) {
+ int base = 0x03;
+ int report = -1;
+ uint16_t finger_data[4][3];
+ ts->p_finger_count = ts->finger_count;
+ ts->p_finger_id = ts->finger_id;
+ ts->finger_count = ((buf[2] & 0x0F) > 4) ? 4 : buf[2] & 0x0F;
+ ts->finger_id = buf[8] << 8 | buf[21];
+ if (ts->debug_log_level & 0x4)
+ printk(KERN_INFO "Finger ID: %X, count: %d\n",
+ ts->finger_id, ts->finger_count);
+ if (ts->p_finger_count && (ts->finger_count != ts->p_finger_count ||
+ ts->finger_id != ts->p_finger_id)) {
+ report = 0;
+
+ for (loop_i = ts->p_finger_count < ts->finger_count
+ ? ts->p_finger_count : ts->finger_count;
+ loop_i > 0; loop_i--) {
+ for (loop_j = ts->p_finger_count; loop_j > 0; loop_j--) {
+ if (ts->debug_log_level & 0x4)
+ printk(KERN_INFO "i = %d, j = %d, A = %X, B = %X\n",
+ loop_i, loop_j, ((ts->finger_id >> (16 - 4 * loop_i)) & 0x000F),
+ ((ts->p_finger_id >> (16 - 4 * loop_j)) & 0x000F));
+
+ if (((ts->finger_id >> (16 - 4 * loop_i)) & 0x000F)
+ == ((ts->p_finger_id >> (16 - 4 * loop_j)) & 0x000F)) {
+ report = loop_i;
+ break;
+ }
+ }
+ if (report > 0)
+ break;
+ }
+ if (report >= ts->p_finger_count ||
+ report == ts->finger_count || ts->p_finger_count == 0)
+ report = -1;
+ }
+ for (loop_i = 0; loop_i < ts->finger_count; loop_i++) {
+ finger_data[loop_i][0] = buf[base] << 8 | buf[base + 1];
+ finger_data[loop_i][1] = buf[base + 2] << 8 | buf[base + 3];
+ cy8c_orient(&finger_data[loop_i][0], &finger_data[loop_i][1], ts->orient);
+ finger_data[loop_i][2] = buf[base + 4];
+ if (loop_i % 2 == 1)
+ base += 7;
+ else
+ base += 6;
+ }
+ if (ts->filter_level[0] &&
+ ((ts->finger_count > ts->p_finger_count) || report >= 0 || ts->grip_suppression)) {
+ for (loop_i = 0; loop_i < ts->finger_count; loop_i++) {
+ if ((finger_data[loop_i][0] < ts->filter_level[0] ||
+ finger_data[loop_i][0] > ts->filter_level[3]) &&
+ !(ts->grip_suppression & BIT(loop_i)) &&
+ loop_i >= (report < 0 ? ts->p_finger_count : report)) {
+ ts->grip_suppression |= BIT(loop_i);
+ } else if ((finger_data[loop_i][0] < ts->filter_level[1] ||
+ finger_data[loop_i][0] > ts->filter_level[2]) &&
+ (ts->grip_suppression & BIT(loop_i)))
+ ts->grip_suppression |= BIT(loop_i);
+ else if (finger_data[loop_i][0] > ts->filter_level[1] &&
+ finger_data[loop_i][0] < ts->filter_level[2]) {
+ ts->grip_suppression &= ~BIT(loop_i);
+ }
+ }
+ ts->ambiguous_state = 0;
+ for (loop_i = 0; loop_i < ts->finger_count; loop_i++)
+ if (((ts->grip_suppression >> loop_i) & 1) == 1)
+ ts->ambiguous_state++;
+ }
+
+ if (ts->ambiguous_state == ts->finger_count
+ || ts->ambiguous_state == report) {
+ if (ts->flag_htc_event == 0)
+ input_mt_sync(ts->input_dev);
+ else {
+ input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE, 0);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION, 1 << 31);
+ }
+ } else {
+ if (report >= 0) {
+ if (ts->debug_log_level & 0x4)
+ printk(KERN_INFO "Change: %d\n", report);
+ if (report == 0) {
+ if (ts->flag_htc_event == 0) {
+ input_mt_sync(ts->input_dev);
+ input_sync(ts->input_dev);
+ ts->sameFilter[2] = ts->sameFilter[0] = ts->sameFilter[1] = -1;
+ } else {
+ input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE, 0);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION, 1 << 31);
+ }
+ } else {
+ for (loop_i = 0; loop_i < report; loop_i++) {
+ if (!(ts->grip_suppression & BIT(loop_i))) {
+ if (ts->flag_htc_event == 0) {
+ if (!(finger_data[loop_i][2] == ts->sameFilter[2] &&
+ finger_data[loop_i][0] == ts->sameFilter[0] &&
+ finger_data[loop_i][1] == ts->sameFilter[1] &&
+ (buf[2] & 0x0F) == 1)) {
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ finger_data[loop_i][0]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ finger_data[loop_i][1]);
+ input_mt_sync(ts->input_dev);
+ ts->sameFilter[2] = finger_data[loop_i][2];
+ ts->sameFilter[0] = finger_data[loop_i][0];
+ ts->sameFilter[1] = finger_data[loop_i][1];
+ }
+ } else {
+ input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE,
+ finger_data[loop_i][2] << 16 | finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION,
+ (((report - 1) == loop_i) ? BIT(31) : 0)
+ | finger_data[loop_i][0] << 16 | finger_data[loop_i][1]);
+ }
+ if (ts->debug_log_level & 0x2)
+ printk(KERN_INFO "Finger %d=> X:%d, Y:%d w:%d, z:%d\n",
+ loop_i + 1, finger_data[loop_i][0], finger_data[loop_i][1],
+ finger_data[loop_i][2], finger_data[loop_i][2]);
+ }
+ }
+ }
+ if (ts->flag_htc_event == 0)
+ input_sync(ts->input_dev);
+
+ base = 3;
+ }
+
+ for (loop_i = 0; loop_i < ts->finger_count; loop_i++) {
+ if (!(ts->grip_suppression & BIT(loop_i))) {
+ if (ts->flag_htc_event == 0) {
+ if (!(finger_data[loop_i][2] == ts->sameFilter[2] &&
+ finger_data[loop_i][0] == ts->sameFilter[0] &&
+ finger_data[loop_i][1] == ts->sameFilter[1] &&
+ (buf[2] & 0x0F) == 1)) {
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ finger_data[loop_i][0]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ finger_data[loop_i][1]);
+ input_mt_sync(ts->input_dev);
+ ts->sameFilter[2] = finger_data[loop_i][2];
+ ts->sameFilter[0] = finger_data[loop_i][0];
+ ts->sameFilter[1] = finger_data[loop_i][1];
+ }
+ } else {
+ input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE,
+ finger_data[loop_i][2] << 16 | finger_data[loop_i][2]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION,
+ (((ts->finger_count - 1) == loop_i) ? BIT(31) : 0)
+ | finger_data[loop_i][0] << 16 | finger_data[loop_i][1]);
+ }
+ if (ts->debug_log_level & 0x2)
+ printk(KERN_INFO "Finger %d=> X:%d, Y:%d w:%d, z:%d\n",
+ loop_i + 1, finger_data[loop_i][0], finger_data[loop_i][1],
+ finger_data[loop_i][2], finger_data[loop_i][2]);
+ }
+ if (!ts->first_pressed) {
+ ts->first_pressed = 1;
+ printk(KERN_INFO "S1@%d,%d\n",
+ finger_data[0][0], finger_data[0][1]);
+ }
+ if (ts->first_pressed == 1) {
+ ts->pre_finger_data[0] = finger_data[0][0];
+ ts->pre_finger_data[1] = finger_data[0][1];
+ }
+ }
+ }
+ if ((ts->unlock_page) &&
+ ((ts->p_finger_count > ts->finger_count) ||
+ (ts->finger_count == 4))) {
+ cy8c_reset_baseline();
+ }
+ } else {
+ ts->finger_count = 0;
+ ts->p_finger_count = 0;
+ ts->p_finger_id = 0x1FFF;
+ ts->grip_suppression = 0;
+ cy8c_data_toggle(ts);
+ if (ts->flag_htc_event == 0) {
+ input_mt_sync(ts->input_dev);
+ ts->sameFilter[2] = ts->sameFilter[0] = ts->sameFilter[1] = -1;
+ } else {
+ input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE, 0);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION, 1 << 31);
+ }
+ if (ts->first_pressed == 1) {
+ ts->first_pressed = 2;
+ printk(KERN_INFO "E%d@%d, %d\n",
+ (ts->finger_id >> 12) & 0x000F,
+ ts->pre_finger_data[0] , ts->pre_finger_data[1]);
+ }
+
+ if (ts->debug_log_level & 0x2)
+ printk(KERN_INFO "Finger leave\n");
+ }
+ if (ts->flag_htc_event == 0) {
+ input_report_key(ts->input_dev, BTN_TOUCH, (ts->finger_count > 0)?1:0);
+ input_sync(ts->input_dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cy8c_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cy8c_ts_data *ts;
+ struct cy8c_i2c_platform_data *pdata;
+ int ret = 0;
+ uint8_t buf[6] = {0};
+ printk(KERN_DEBUG "%s: enter\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ ts = kzalloc(sizeof(struct cy8c_ts_data), GFP_KERNEL);
+ if (ts == NULL) {
+ dev_err(&client->dev, "allocate cy8c_ts_data failed\n");
+ ret = -ENOMEM;
+ goto err_alloc_data_failed;
+ }
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+ pdata = client->dev.platform_data;
+ private_ts = ts;
+ if (pdata)
+ ts->power = pdata->power;
+ if (ts->power) {
+ ret = ts->power(1);
+ msleep(10);
+ if (ret < 0) {
+ dev_err(&client->dev, "power on failed\n");
+ goto err_power_failed;
+ }
+ }
+ ret = i2c_cy8c_read(ts->client, 0x01, buf, 2);
+ if (ret < 0) {
+ dev_info(&client->dev, "No Cypress chip found\n");
+ goto err_detect_failed;
+ }
+ if ((buf[0] & 0x10) != 0x10) {
+ i2c_cy8c_write_byte_data(ts->client, 0x00, 0x01);
+ msleep(200);
+ i2c_cy8c_read(ts->client, 0x01, buf, 2);
+ }
+
+ ret = i2c_cy8c_read(ts->client, 0x0B, &buf[2], 4);
+ if (ret < 0) {
+ dev_err(&client->dev, "TOUCH_ERR: Cypress chip abnormal\n");
+ goto err_detect_failed;
+ }
+ dev_info(&client->dev, "buf: %x, %x, %x, %x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ ts->version = buf[2] << 8 | buf[3];
+ if ((buf[4] + buf[5]) <= 32) {
+ ts->x_channel = buf[4];
+ ts->y_channel = buf[5];
+ } else {
+ ts->x_channel = 21;
+ ts->y_channel = 11;
+ }
+ dev_info(&client->dev,
+ "application verion = %X, x_channel = %X, y_channel = %X\n",
+ ts->version, ts->x_channel, ts->y_channel);
+ if (cy8c_init_panel(ts) < 0)
+ printk(KERN_ERR "TOUCH_ERR: %s init failed\n",
+ __func__);
+
+ if (pdata) {
+ while (pdata->version > ts->version)
+ pdata++;
+ ts->intr = pdata->gpio_irq;
+ ts->orient = pdata->orient;
+ ts->timeout = pdata->timeout;
+ ts->interval = pdata->interval;
+ ts->unlock_attr = pdata->unlock_attr;
+ if (ts->unlock_attr)
+ ts->unlock_page = 1;
+ dev_info(&client->dev, "orient: %d\n", ts->orient);
+ ts->wake = pdata->wake;
+ ts->filter_level = pdata->filter_level;
+ ts->auto_reset = pdata->auto_reset;
+ ts->reset = pdata->reset;
+ if (ts->wake)
+ gpio_request(ts->intr, "touch-intr");
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "TOUCH_ERR: Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ ts->input_dev->name = "cy8c-touchscreen";
+ ts->input_dev->id.version = ts->version;
+ ts->input_dev->mtsize = 4;/* Initialize buffer with maximum 4 fingers at the same time */
+
+ set_bit(EV_SYN, ts->input_dev->evbit);
+ set_bit(EV_ABS, ts->input_dev->evbit);
+ set_bit(EV_KEY, ts->input_dev->evbit);
+
+ set_bit(KEY_BACK, ts->input_dev->keybit);
+ set_bit(KEY_HOME, ts->input_dev->keybit);
+ set_bit(KEY_MENU, ts->input_dev->keybit);
+ set_bit(KEY_SEARCH, ts->input_dev->keybit);
+ set_bit(BTN_TOUCH, ts->input_dev->keybit);
+ if (pdata) {
+ printk(KERN_INFO "input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n",
+ pdata->abs_x_min, pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max);
+
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
+ pdata->abs_x_min, pdata->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+ pdata->abs_y_min, pdata->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ pdata->abs_pressure_min, pdata->abs_pressure_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ pdata->abs_width_min, pdata->abs_width_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE,
+ pdata->abs_pressure_min, pdata->abs_pressure_max, 0, 0);
+
+ input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE,
+ 0, ((pdata->abs_pressure_max << 16) | pdata->abs_width_max), 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION,
+ 0, (BIT(31) | (pdata->abs_x_max << 16) | pdata->abs_y_max), 0, 0);
+ }
+
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "TOUCH_ERR: Unable to register %s input device\n",
+ ts->input_dev->name);
+ goto err_input_register_device_failed;
+ }
+
+ ret = request_threaded_irq(client->irq, NULL, cy8c_ts_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "cy8c_ts", ts);
+ if (ret != 0) {
+ dev_err(&client->dev, "TOUCH_ERR: request_irq failed\n");
+ dev_err(&client->dev, "TOUCH_ERR: don't support method without irq\n");
+ goto err_input_register_device_failed;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING + 1;
+ ts->early_suspend.suspend = cy8c_ts_early_suspend;
+ ts->early_suspend.resume = cy8c_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ dev_info(&client->dev, "Start touchscreen %s in %s mode\n",
+ ts->input_dev->name, "interrupt");
+
+ ts->flag_htc_event = 0;
+ cy8c_touch_sysfs_init();
+
+ return 0;
+
+err_input_register_device_failed:
+ input_free_device(ts->input_dev);
+
+err_input_dev_alloc_failed:
+ gpio_free(ts->intr);
+err_detect_failed:
+err_power_failed:
+ kfree(ts);
+err_alloc_data_failed:
+err_check_functionality_failed:
+ return ret;
+}
+
+static int cy8c_ts_remove(struct i2c_client *client)
+{
+ struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+
+ cy8c_touch_sysfs_deinit();
+
+ unregister_early_suspend(&ts->early_suspend);
+
+ free_irq(client->irq, ts);
+
+ input_unregister_device(ts->input_dev);
+ gpio_free(ts->intr);
+ kfree(ts);
+
+ return 0;
+}
+
+static int cy8c_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+ uint8_t buf[2] = {0};
+
+ disable_irq_nosync(client->irq);
+
+ if (!ts->p_finger_count) {
+ if (ts->wake)
+ ts->wake();
+ if (!gpio_get_value(ts->intr))
+ cy8c_data_toggle(ts);
+ }
+ if (!i2c_cy8c_read(ts->client, 0x00, buf, 2))
+ printk(KERN_INFO "%s: %x, %x\n", __func__, buf[0], buf[1]);
+
+ ts->first_pressed = 0;
+ ts->grip_suppression = 0;
+ ts->p_finger_count = 0;
+ ts->finger_count = 0;
+ ts->unlock_page = 0;
+
+ if ((buf[1] & 0x10) == 0x10)
+ if (cy8c_init_panel(ts) < 0)
+ printk(KERN_ERR "TOUCH_ERR: %s init failed\n",
+ __func__);
+ if (buf[0] & 0x70)
+ i2c_cy8c_write_byte_data(ts->client, 0x00, buf[0] & 0x8F);
+ mutex_lock(&cy8c_mutex);
+ i2c_cy8c_write_byte_data(ts->client, 0x00, (buf[0] & 0x8F) | 0x02);
+ ts->suspend = 1;
+ mutex_unlock(&cy8c_mutex);
+
+ return 0;
+}
+
+static int cy8c_ts_resume(struct i2c_client *client)
+{
+ struct cy8c_ts_data *ts = i2c_get_clientdata(client);
+ uint8_t buf[2] = {0};
+
+ if (ts->wake)
+ ts->wake();
+ ts->suspend = 0;
+
+ if (!i2c_cy8c_read(ts->client, 0x00, buf, 2))
+ printk(KERN_INFO "%s: %x, %x\n", __func__, buf[0], buf[1]);
+ else if (ts->auto_reset && ts->reset) {
+ printk(KERN_INFO "[TP]For PVT device, auto reset for recovery.\n");
+ ts->reset();
+ if (!i2c_cy8c_read(ts->client, 0x00, buf, 2))
+ printk(KERN_INFO "%s: %x, %x\n", __func__, buf[0], buf[1]);
+ }
+
+ if ((buf[1] & 0x10) == 0x10)
+ if (cy8c_init_panel(ts) < 0)
+ printk(KERN_ERR "TOUCH_ERR: %s init failed\n",
+ __func__);
+
+ i2c_cy8c_write_byte_data(ts->client, 0x00, (buf[0] & 0x8F) | 0x04);
+
+ ts->unlock_page = 1;
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_ts_early_suspend(struct early_suspend *h)
+{
+ struct cy8c_ts_data *ts;
+ ts = container_of(h, struct cy8c_ts_data, early_suspend);
+ cy8c_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void cy8c_ts_late_resume(struct early_suspend *h)
+{
+ struct cy8c_ts_data *ts;
+ ts = container_of(h, struct cy8c_ts_data, early_suspend);
+ cy8c_ts_resume(ts->client);
+}
+#endif
+
+static const struct i2c_device_id cy8c_ts_i2c_id[] = {
+ {CYPRESS_TMA_NAME, 0},
+ {}
+};
+
+static struct i2c_driver cy8c_ts_driver = {
+ .id_table = cy8c_ts_i2c_id,
+ .probe = cy8c_ts_probe,
+ .remove = cy8c_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = cy8c_ts_suspend,
+ .resume = cy8c_ts_resume,
+#endif
+ .driver = {
+ .name = CYPRESS_TMA_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit cy8c_ts_init(void)
+{
+ printk(KERN_INFO "%s: enter\n", __func__);
+
+ return i2c_add_driver(&cy8c_ts_driver);
+}
+
+static void __exit cy8c_ts_exit(void)
+{
+ i2c_del_driver(&cy8c_ts_driver);
+}
+
+module_init(cy8c_ts_init);
+module_exit(cy8c_ts_exit);
+
+MODULE_DESCRIPTION("Cypress TMA Touchscreen Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/cy8c_tma_ts.h b/include/linux/cy8c_tma_ts.h
new file mode 100644
index 0000000..1d5066e
--- /dev/null
+++ b/include/linux/cy8c_tma_ts.h
@@ -0,0 +1,46 @@
+/* include/linux/cy8c_tmg_ts.c
+ *
+ * Copyright (C) 2007-2008 HTC Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef CY8C_I2C_H
+#define CY8C_I2C_H
+
+#include <linux/types.h>
+
+#define CYPRESS_TMA_NAME "CY8CTMA340"
+
+struct cy8c_i2c_platform_data {
+ uint16_t version;
+ uint16_t gpio_irq;
+ uint8_t orient;
+ uint8_t timeout;
+ uint8_t interval;
+ uint8_t unlock_attr;
+ uint8_t auto_reset;
+ int abs_x_min;
+ int abs_x_max;
+ int abs_y_min;
+ int abs_y_max;
+ int abs_pressure_min;
+ int abs_pressure_max;
+ int abs_width_min;
+ int abs_width_max;
+ int (*power)(int on);
+ int (*wake)(void);
+ int (*reset)(void);
+ uint16_t filter_level[4];
+};
+
+#endif
+