|
@@ -0,0 +1,828 @@
|
|
|
+commit ab28538ebe289c686cf357a6274c3f4a71969ca3
|
|
|
+Author: Reinhard Russinger <reinhard@russinger.at>
|
|
|
+Date: Thu May 4 06:59:24 2017 +0200
|
|
|
+
|
|
|
+ changes edt-ft5x06 touchcontroller
|
|
|
+
|
|
|
+diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
|
|
|
+index 703e295..e862958 100644
|
|
|
+--- a/drivers/input/touchscreen/edt-ft5x06.c
|
|
|
++++ b/drivers/input/touchscreen/edt-ft5x06.c
|
|
|
+@@ -27,7 +27,6 @@
|
|
|
+
|
|
|
+ #include <linux/module.h>
|
|
|
+ #include <linux/ratelimit.h>
|
|
|
+-#include <linux/irq.h>
|
|
|
+ #include <linux/interrupt.h>
|
|
|
+ #include <linux/input.h>
|
|
|
+ #include <linux/i2c.h>
|
|
|
+@@ -35,10 +34,12 @@
|
|
|
+ #include <linux/delay.h>
|
|
|
+ #include <linux/debugfs.h>
|
|
|
+ #include <linux/slab.h>
|
|
|
+-#include <linux/gpio/consumer.h>
|
|
|
++#include <linux/gpio.h>
|
|
|
++#include <linux/of_gpio.h>
|
|
|
+ #include <linux/input/mt.h>
|
|
|
+-#include <linux/input/touchscreen.h>
|
|
|
+-#include <linux/of_device.h>
|
|
|
++#include <linux/input/edt-ft5x06.h>
|
|
|
++
|
|
|
++#define MAX_SUPPORT_POINTS 5
|
|
|
+
|
|
|
+ #define WORK_REGISTER_THRESHOLD 0x00
|
|
|
+ #define WORK_REGISTER_REPORT_RATE 0x08
|
|
|
+@@ -53,6 +54,13 @@
|
|
|
+ #define M09_REGISTER_NUM_X 0x94
|
|
|
+ #define M09_REGISTER_NUM_Y 0x95
|
|
|
+
|
|
|
++#define M12_REGISTER_THRESHOLD 0x80
|
|
|
++#define M12_REGISTER_GAIN 0x92
|
|
|
++#define M12_REGISTER_OFFSET 0x93
|
|
|
++#define M12_REGISTER_NUM_X 0x94
|
|
|
++#define M12_REGISTER_NUM_Y 0x95
|
|
|
++#define M12_REGISTER_REPORT_RATE 0x88
|
|
|
++
|
|
|
+ #define NO_REGISTER 0xff
|
|
|
+
|
|
|
+ #define WORK_REGISTER_OPMODE 0x3c
|
|
|
+@@ -72,6 +80,7 @@
|
|
|
+ enum edt_ver {
|
|
|
+ M06,
|
|
|
+ M09,
|
|
|
++ M12,
|
|
|
+ };
|
|
|
+
|
|
|
+ struct edt_reg_addr {
|
|
|
+@@ -86,12 +95,12 @@ struct edt_reg_addr {
|
|
|
+ struct edt_ft5x06_ts_data {
|
|
|
+ struct i2c_client *client;
|
|
|
+ struct input_dev *input;
|
|
|
+- struct touchscreen_properties prop;
|
|
|
+ u16 num_x;
|
|
|
+ u16 num_y;
|
|
|
+
|
|
|
+- struct gpio_desc *reset_gpio;
|
|
|
+- struct gpio_desc *wake_gpio;
|
|
|
++ int reset_pin;
|
|
|
++ int irq_pin;
|
|
|
++ int wake_pin;
|
|
|
+
|
|
|
+ #if defined(CONFIG_DEBUG_FS)
|
|
|
+ struct dentry *debug_dir;
|
|
|
+@@ -105,18 +114,20 @@ struct edt_ft5x06_ts_data {
|
|
|
+ int gain;
|
|
|
+ int offset;
|
|
|
+ int report_rate;
|
|
|
+- int max_support_points;
|
|
|
+
|
|
|
++ u32 invert_x;
|
|
|
++ u32 invert_y;
|
|
|
++ u32 swap_xy;
|
|
|
++
|
|
|
++ u32 max_x;
|
|
|
++ u32 max_y;
|
|
|
++
|
|
|
+ char name[EDT_NAME_LEN];
|
|
|
+
|
|
|
+ struct edt_reg_addr reg_addr;
|
|
|
+ enum edt_ver version;
|
|
|
+ };
|
|
|
+
|
|
|
+-struct edt_i2c_chip_data {
|
|
|
+- int max_support_points;
|
|
|
+-};
|
|
|
+-
|
|
|
+ static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
|
|
|
+ u16 wr_len, u8 *wr_buf,
|
|
|
+ u16 rd_len, u8 *rd_buf)
|
|
|
+@@ -173,9 +184,9 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
|
|
+ struct edt_ft5x06_ts_data *tsdata = dev_id;
|
|
|
+ struct device *dev = &tsdata->client->dev;
|
|
|
+ u8 cmd;
|
|
|
+- u8 rdbuf[63];
|
|
|
++ u8 rdbuf[29];
|
|
|
+ int i, type, x, y, id;
|
|
|
+- int offset, tplen, datalen, crclen;
|
|
|
++ int offset, tplen, datalen;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ switch (tsdata->version) {
|
|
|
+@@ -183,14 +194,15 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
|
|
+ cmd = 0xf9; /* tell the controller to send touch data */
|
|
|
+ offset = 5; /* where the actual touch data starts */
|
|
|
+ tplen = 4; /* data comes in so called frames */
|
|
|
+- crclen = 1; /* length of the crc data */
|
|
|
++ datalen = 26; /* how much bytes to listen for */
|
|
|
+ break;
|
|
|
+-
|
|
|
++
|
|
|
++ case M12:
|
|
|
+ case M09:
|
|
|
+- cmd = 0x0;
|
|
|
+- offset = 3;
|
|
|
++ cmd = 0x02;
|
|
|
++ offset = 1;
|
|
|
+ tplen = 6;
|
|
|
+- crclen = 0;
|
|
|
++ datalen = 29;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+@@ -198,7 +210,6 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(rdbuf, 0, sizeof(rdbuf));
|
|
|
+- datalen = tplen * tsdata->max_support_points + offset + crclen;
|
|
|
+
|
|
|
+ error = edt_ft5x06_ts_readwrite(tsdata->client,
|
|
|
+ sizeof(cmd), &cmd,
|
|
|
+@@ -209,8 +220,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+- /* M09 does not send header or CRC */
|
|
|
+- if (tsdata->version == M06) {
|
|
|
++ /* M09 M12 does not send header or CRC */
|
|
|
++ if (tsdata->version == M06) {
|
|
|
+ if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa ||
|
|
|
+ rdbuf[2] != datalen) {
|
|
|
+ dev_err_ratelimited(dev,
|
|
|
+@@ -223,7 +234,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+- for (i = 0; i < tsdata->max_support_points; i++) {
|
|
|
++ for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
|
|
|
+ u8 *buf = &rdbuf[i * tplen + offset];
|
|
|
+ bool down;
|
|
|
+
|
|
|
+@@ -236,19 +247,32 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
|
|
+ if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN)
|
|
|
+ continue;
|
|
|
+
|
|
|
+- x = ((buf[0] << 8) | buf[1]) & 0x0fff;
|
|
|
+- y = ((buf[2] << 8) | buf[3]) & 0x0fff;
|
|
|
++ if( tsdata->swap_xy) {
|
|
|
++ y = ((buf[0] << 8) | buf[1]) & 0x0fff;
|
|
|
++ x = ((buf[2] << 8) | buf[3]) & 0x0fff;
|
|
|
++ } else {
|
|
|
++ x = ((buf[0] << 8) | buf[1]) & 0x0fff;
|
|
|
++ y = ((buf[2] << 8) | buf[3]) & 0x0fff;
|
|
|
++ }
|
|
|
+ id = (buf[2] >> 4) & 0x0f;
|
|
|
+ down = type != TOUCH_EVENT_UP;
|
|
|
+
|
|
|
++ if(tsdata->invert_x) {
|
|
|
++ x = tsdata->max_x - x;
|
|
|
++ }
|
|
|
++
|
|
|
++ if(tsdata->invert_y) {
|
|
|
++ y = tsdata->max_y - y;
|
|
|
++ }
|
|
|
++
|
|
|
+ input_mt_slot(tsdata->input, id);
|
|
|
+ input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
|
|
|
+
|
|
|
+ if (!down)
|
|
|
+ continue;
|
|
|
+
|
|
|
+- touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y,
|
|
|
+- true);
|
|
|
++ input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
|
|
|
++ input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
|
|
|
+ }
|
|
|
+
|
|
|
+ input_mt_report_pointer_emulation(tsdata->input, true);
|
|
|
+@@ -267,15 +291,17 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
|
|
|
+ case M06:
|
|
|
+ wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
|
|
|
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
|
|
|
++ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
|
|
|
+ wrbuf[2] = value;
|
|
|
+ wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
|
|
|
+- return edt_ft5x06_ts_readwrite(tsdata->client, 4,
|
|
|
+- wrbuf, 0, NULL);
|
|
|
++ return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL);
|
|
|
++
|
|
|
++ case M12:
|
|
|
+ case M09:
|
|
|
+ wrbuf[0] = addr;
|
|
|
+ wrbuf[1] = value;
|
|
|
+
|
|
|
+- return edt_ft5x06_ts_readwrite(tsdata->client, 2,
|
|
|
++ return edt_ft5x06_ts_readwrite(tsdata->client, 3,
|
|
|
+ wrbuf, 0, NULL);
|
|
|
+
|
|
|
+ default:
|
|
|
+@@ -309,6 +335,7 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
++ case M12:
|
|
|
+ case M09:
|
|
|
+ wrbuf[0] = addr;
|
|
|
+ error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
|
|
|
+@@ -331,9 +358,10 @@ struct edt_ft5x06_attribute {
|
|
|
+ u8 limit_high;
|
|
|
+ u8 addr_m06;
|
|
|
+ u8 addr_m09;
|
|
|
++ u8 addr_m12;
|
|
|
+ };
|
|
|
+
|
|
|
+-#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \
|
|
|
++#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_m12, \
|
|
|
+ _limit_low, _limit_high) \
|
|
|
+ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
|
|
|
+ .dattr = __ATTR(_field, _mode, \
|
|
|
+@@ -342,6 +370,7 @@ struct edt_ft5x06_attribute {
|
|
|
+ .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
|
|
|
+ .addr_m06 = _addr_m06, \
|
|
|
+ .addr_m09 = _addr_m09, \
|
|
|
++ .addr_m12 = _addr_m12, \
|
|
|
+ .limit_low = _limit_low, \
|
|
|
+ .limit_high = _limit_high, \
|
|
|
+ }
|
|
|
+@@ -372,6 +401,10 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
|
|
|
+ addr = attr->addr_m06;
|
|
|
+ break;
|
|
|
+
|
|
|
++ case M12:
|
|
|
++ addr = attr->addr_m12;
|
|
|
++ break;
|
|
|
++
|
|
|
+ case M09:
|
|
|
+ addr = attr->addr_m09;
|
|
|
+ break;
|
|
|
+@@ -441,6 +474,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
|
|
|
+ addr = attr->addr_m06;
|
|
|
+ break;
|
|
|
+
|
|
|
++ case M12:
|
|
|
++ addr = attr->addr_m12;
|
|
|
++ break;
|
|
|
++
|
|
|
+ case M09:
|
|
|
+ addr = attr->addr_m09;
|
|
|
+ break;
|
|
|
+@@ -467,13 +504,13 @@ out:
|
|
|
+ }
|
|
|
+
|
|
|
+ static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
|
|
|
+- M09_REGISTER_GAIN, 0, 31);
|
|
|
++ M09_REGISTER_GAIN, M12_REGISTER_GAIN, 0, 31);
|
|
|
+ static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
|
|
|
+- M09_REGISTER_OFFSET, 0, 31);
|
|
|
++ M09_REGISTER_OFFSET, M12_REGISTER_OFFSET, 0, 31);
|
|
|
+ static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
|
|
|
+- M09_REGISTER_THRESHOLD, 20, 80);
|
|
|
++ M09_REGISTER_THRESHOLD, M12_REGISTER_THRESHOLD, 1, 255);
|
|
|
+ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
|
|
|
+- NO_REGISTER, 3, 14);
|
|
|
++ NO_REGISTER, M12_REGISTER_REPORT_RATE, 3, 14);
|
|
|
+
|
|
|
+ static struct attribute *edt_ft5x06_attrs[] = {
|
|
|
+ &edt_ft5x06_attr_gain.dattr.attr,
|
|
|
+@@ -508,7 +545,7 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
|
|
|
+ }
|
|
|
+
|
|
|
+ /* mode register is 0x3c when in the work mode */
|
|
|
+- if (tsdata->version == M09)
|
|
|
++ if ((tsdata->version == M09) || (tsdata->version == M12))
|
|
|
+ goto m09_out;
|
|
|
+
|
|
|
+ error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
|
|
|
+@@ -545,7 +582,7 @@ err_out:
|
|
|
+ return error;
|
|
|
+
|
|
|
+ m09_out:
|
|
|
+- dev_err(&client->dev, "No factory mode support for M09\n");
|
|
|
++ dev_err(&client->dev, "No factory mode support for M09 and M12\n");
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ }
|
|
|
+@@ -721,23 +758,46 @@ static void
|
|
|
+ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
|
|
|
+ const char *debugfs_name)
|
|
|
+ {
|
|
|
++ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
|
|
|
++
|
|
|
+ tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
|
|
|
+ if (!tsdata->debug_dir)
|
|
|
+ return;
|
|
|
+
|
|
|
+ debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
|
|
|
+ debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
|
|
|
++ debugfs_create_u32("threshold", S_IRUSR, tsdata->debug_dir, &tsdata->threshold);
|
|
|
++ debugfs_create_u32("gain", S_IRUSR, tsdata->debug_dir, &tsdata->gain);
|
|
|
++ debugfs_create_u32("offset", S_IRUSR, tsdata->debug_dir, &tsdata->offset);
|
|
|
++
|
|
|
+
|
|
|
+ debugfs_create_file("mode", S_IRUSR | S_IWUSR,
|
|
|
+ tsdata->debug_dir, tsdata, &debugfs_mode_fops);
|
|
|
+ debugfs_create_file("raw_data", S_IRUSR,
|
|
|
+ tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
|
|
|
++
|
|
|
++ if (reg_addr->reg_report_rate != NO_REGISTER)
|
|
|
++ debugfs_create_u32("report_rate", S_IRUSR, tsdata->debug_dir, &tsdata->report_rate);
|
|
|
++
|
|
|
++ debugfs_create_u32("invert_x", S_IRUSR | S_IWUSR,
|
|
|
++ tsdata->debug_dir, &tsdata->invert_x);
|
|
|
++ debugfs_create_u32("invert_y", S_IRUSR | S_IWUSR,
|
|
|
++ tsdata->debug_dir, &tsdata->invert_y);
|
|
|
++ debugfs_create_u32("swap_xy", S_IRUSR,
|
|
|
++ tsdata->debug_dir, &tsdata->swap_xy);
|
|
|
++ debugfs_create_u32("max_x", S_IRUSR,
|
|
|
++ tsdata->debug_dir, &tsdata->max_x);
|
|
|
++ debugfs_create_u32("max_y", S_IRUSR,
|
|
|
++ tsdata->debug_dir, &tsdata->max_y);
|
|
|
++
|
|
|
++
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
|
|
|
+ {
|
|
|
+- debugfs_remove_recursive(tsdata->debug_dir);
|
|
|
++ if (tsdata->debug_dir)
|
|
|
++ debugfs_remove_recursive(tsdata->debug_dir);
|
|
|
+ kfree(tsdata->raw_buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -756,6 +816,45 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
|
|
|
+
|
|
|
+ #endif /* CONFIG_DEBUGFS */
|
|
|
+
|
|
|
++static int edt_ft5x06_ts_reset(struct i2c_client *client,
|
|
|
++ struct edt_ft5x06_ts_data *tsdata)
|
|
|
++{
|
|
|
++ int error;
|
|
|
++
|
|
|
++ if (gpio_is_valid(tsdata->wake_pin)) {
|
|
|
++ error = devm_gpio_request_one(&client->dev,
|
|
|
++ tsdata->wake_pin, GPIOF_OUT_INIT_LOW,
|
|
|
++ "edt-ft5x06 wake");
|
|
|
++ if (error) {
|
|
|
++ dev_err(&client->dev,
|
|
|
++ "Failed to request GPIO %d as wake pin, error %d\n",
|
|
|
++ tsdata->wake_pin, error);
|
|
|
++ return error;
|
|
|
++ }
|
|
|
++
|
|
|
++ msleep(5);
|
|
|
++ gpio_set_value(tsdata->wake_pin, 1);
|
|
|
++ }
|
|
|
++ if (gpio_is_valid(tsdata->reset_pin)) {
|
|
|
++ /* this pulls reset down, enabling the low active reset */
|
|
|
++ error = devm_gpio_request_one(&client->dev,
|
|
|
++ tsdata->reset_pin, GPIOF_OUT_INIT_LOW,
|
|
|
++ "edt-ft5x06 reset");
|
|
|
++ if (error) {
|
|
|
++ dev_err(&client->dev,
|
|
|
++ "Failed to request GPIO %d as reset pin, error %d\n",
|
|
|
++ tsdata->reset_pin, error);
|
|
|
++ return error;
|
|
|
++ }
|
|
|
++
|
|
|
++ msleep(5);
|
|
|
++ gpio_set_value(tsdata->reset_pin, 1);
|
|
|
++ msleep(300);
|
|
|
++ }
|
|
|
++
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
+ static int edt_ft5x06_ts_identify(struct i2c_client *client,
|
|
|
+ struct edt_ft5x06_ts_data *tsdata,
|
|
|
+ char *fw_version)
|
|
|
+@@ -771,14 +870,16 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
|
|
|
+ */
|
|
|
+ memset(rdbuf, 0, sizeof(rdbuf));
|
|
|
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xbb",
|
|
|
+- EDT_NAME_LEN - 1, rdbuf);
|
|
|
++ EDT_NAME_LEN - 1,
|
|
|
++ rdbuf);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+
|
|
|
++ printk(KERN_INFO "EDT-TC:: %s ::-----%s----\n", __func__, rdbuf);
|
|
|
+ /* if we find something consistent, stay with that assumption
|
|
|
+ * at least M09 won't send 3 bytes here
|
|
|
+ */
|
|
|
+- if (!(strncasecmp(rdbuf + 1, "EP0", 3))) {
|
|
|
++ if ((strncasecmp(rdbuf + 1, "EP0", 3) == 0) && (strncasecmp(rdbuf + 6, "M12", 3) != 0)) {
|
|
|
+ tsdata->version = M06;
|
|
|
+
|
|
|
+ /* remove last '$' end marker */
|
|
|
+@@ -792,8 +893,24 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
|
|
|
+ *p++ = '\0';
|
|
|
+ strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
|
|
|
+ strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
|
|
|
++ } else if (!(strncasecmp(rdbuf + 6, "M12", 3))) {
|
|
|
++ tsdata->version = M12;
|
|
|
++
|
|
|
++ error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
|
|
|
++ 2, rdbuf);
|
|
|
++ if (error)
|
|
|
++ return error;
|
|
|
++
|
|
|
++ strlcpy(fw_version, rdbuf, 2);
|
|
|
++
|
|
|
++ error = edt_ft5x06_ts_readwrite(client, 1, "\xA8",
|
|
|
++ 1, rdbuf);
|
|
|
++ if (error)
|
|
|
++ return error;
|
|
|
++
|
|
|
++ snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M12",
|
|
|
++ rdbuf[0] >> 4, rdbuf[0] & 0x0F);
|
|
|
+ } else {
|
|
|
+- /* since there are only two versions around (M06, M09) */
|
|
|
+ tsdata->version = M09;
|
|
|
+
|
|
|
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
|
|
|
+@@ -815,30 +932,58 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+-static void edt_ft5x06_ts_get_defaults(struct device *dev,
|
|
|
+- struct edt_ft5x06_ts_data *tsdata)
|
|
|
++#define EDT_ATTR_CHECKSET(name, reg) \
|
|
|
++ if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \
|
|
|
++ pdata->name <= edt_ft5x06_attr_##name.limit_high) \
|
|
|
++ edt_ft5x06_register_write(tsdata, reg, pdata->name)
|
|
|
++
|
|
|
++#define EDT_GET_PROP(name, reg) { \
|
|
|
++ u32 val; \
|
|
|
++ if (of_property_read_u32(np, #name, &val) == 0) \
|
|
|
++ edt_ft5x06_register_write(tsdata, reg, val); \
|
|
|
++}
|
|
|
++
|
|
|
++static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np,
|
|
|
++ struct edt_ft5x06_ts_data *tsdata)
|
|
|
+ {
|
|
|
+ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
|
|
|
+- u32 val;
|
|
|
+- int error;
|
|
|
+
|
|
|
+- error = device_property_read_u32(dev, "threshold", &val);
|
|
|
+- if (!error) {
|
|
|
+- edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, val);
|
|
|
+- tsdata->threshold = val;
|
|
|
+- }
|
|
|
++ switch (tsdata->version) {
|
|
|
++ case M12:
|
|
|
++ EDT_GET_PROP(threshold_M12, reg_addr->reg_threshold);
|
|
|
++ EDT_GET_PROP(gain_M12, reg_addr->reg_gain);
|
|
|
++ EDT_GET_PROP(offset_M12, reg_addr->reg_offset);
|
|
|
++ break;
|
|
|
++
|
|
|
++ case M09:
|
|
|
++ EDT_GET_PROP(threshold_M09, reg_addr->reg_threshold);
|
|
|
++ EDT_GET_PROP(gain_M09, reg_addr->reg_gain);
|
|
|
++ EDT_GET_PROP(offset_M09, reg_addr->reg_offset);
|
|
|
++ break;
|
|
|
++
|
|
|
++ case M06:
|
|
|
++ EDT_GET_PROP(threshold_M06, reg_addr->reg_threshold);
|
|
|
++ EDT_GET_PROP(gain_M06, reg_addr->reg_gain);
|
|
|
++ EDT_GET_PROP(offset_M06, reg_addr->reg_offset);
|
|
|
++ break;
|
|
|
++ }
|
|
|
++}
|
|
|
+
|
|
|
+- error = device_property_read_u32(dev, "gain", &val);
|
|
|
+- if (!error) {
|
|
|
+- edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, val);
|
|
|
+- tsdata->gain = val;
|
|
|
+- }
|
|
|
++static void
|
|
|
++edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
|
|
|
++ const struct edt_ft5x06_platform_data *pdata)
|
|
|
++{
|
|
|
++ struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
|
|
|
+
|
|
|
+- error = device_property_read_u32(dev, "offset", &val);
|
|
|
+- if (!error) {
|
|
|
+- edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val);
|
|
|
+- tsdata->offset = val;
|
|
|
+- }
|
|
|
++ if (!pdata->use_parameters)
|
|
|
++ return;
|
|
|
++
|
|
|
++ /* pick up defaults from the platform data */
|
|
|
++ EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold);
|
|
|
++ EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain);
|
|
|
++ EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset);
|
|
|
++ if (reg_addr->reg_report_rate != NO_REGISTER)
|
|
|
++ EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate);
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+@@ -855,6 +1000,12 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
|
|
|
+ reg_addr->reg_report_rate);
|
|
|
+ tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x);
|
|
|
+ tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y);
|
|
|
++
|
|
|
++ if(tsdata->max_x == 0)
|
|
|
++ tsdata->max_x = tsdata->num_x * 64;
|
|
|
++ if(tsdata->max_y == 0)
|
|
|
++ tsdata->max_y = tsdata->num_y * 64;
|
|
|
++
|
|
|
+ }
|
|
|
+
|
|
|
+ static void
|
|
|
+@@ -872,6 +1023,15 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
|
|
|
+ reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
|
|
|
+ break;
|
|
|
+
|
|
|
++ case M12:
|
|
|
++ reg_addr->reg_threshold = M12_REGISTER_THRESHOLD;
|
|
|
++ reg_addr->reg_report_rate = M12_REGISTER_REPORT_RATE;
|
|
|
++ reg_addr->reg_gain = M12_REGISTER_GAIN;
|
|
|
++ reg_addr->reg_offset = M12_REGISTER_OFFSET;
|
|
|
++ reg_addr->reg_num_x = M12_REGISTER_NUM_X;
|
|
|
++ reg_addr->reg_num_y = M12_REGISTER_NUM_Y;
|
|
|
++ break;
|
|
|
++
|
|
|
+ case M09:
|
|
|
+ reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
|
|
|
+ reg_addr->reg_gain = M09_REGISTER_GAIN;
|
|
|
+@@ -882,13 +1042,48 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
++#ifdef CONFIG_OF
|
|
|
++static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
|
|
|
++ struct edt_ft5x06_ts_data *tsdata)
|
|
|
++{
|
|
|
++ struct device_node *np = dev->of_node;
|
|
|
++ /*
|
|
|
++ * irq_pin is not needed for DT setup.
|
|
|
++ * irq is associated via 'interrupts' property in DT
|
|
|
++ */
|
|
|
++ tsdata->irq_pin = -EINVAL;
|
|
|
++ tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
|
|
|
++ tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0);
|
|
|
++
|
|
|
++ tsdata->swap_xy = of_property_read_bool(np, "swap_xy")?1:0;
|
|
|
++ tsdata->invert_x = of_property_read_bool(np, "invert_x")?1:0;
|
|
|
++ tsdata->invert_y = of_property_read_bool(np, "invert_y")?1:0;
|
|
|
++
|
|
|
++ if(of_property_read_u32(np, "max_x", &tsdata->max_x))
|
|
|
++ tsdata->max_x = 0;
|
|
|
++ if(of_property_read_u32(np, "max_y", &tsdata->max_y))
|
|
|
++ tsdata->max_y = 0;
|
|
|
++
|
|
|
++ if(tsdata->swap_xy) {
|
|
|
++ printk(KERN_INFO "EDT-TC:: %s :: SWAP_XY\n", __func__);
|
|
|
++ }
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++#else
|
|
|
++static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev,
|
|
|
++ struct edt_ft5x06_ts_data *tsdata)
|
|
|
++{
|
|
|
++ return -ENODEV;
|
|
|
++}
|
|
|
++#endif
|
|
|
++
|
|
|
+ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
|
|
+ const struct i2c_device_id *id)
|
|
|
+ {
|
|
|
+- const struct edt_i2c_chip_data *chip_data;
|
|
|
++ const struct edt_ft5x06_platform_data *pdata =
|
|
|
++ dev_get_platdata(&client->dev);
|
|
|
+ struct edt_ft5x06_ts_data *tsdata;
|
|
|
+ struct input_dev *input;
|
|
|
+- unsigned long irq_flags;
|
|
|
+ int error;
|
|
|
+ char fw_version[EDT_NAME_LEN];
|
|
|
+
|
|
|
+@@ -900,43 +1095,32 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+- chip_data = of_device_get_match_data(&client->dev);
|
|
|
+- if (!chip_data)
|
|
|
+- chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
|
|
|
+- if (!chip_data || !chip_data->max_support_points) {
|
|
|
+- dev_err(&client->dev, "invalid or missing chip data\n");
|
|
|
+- return -EINVAL;
|
|
|
+- }
|
|
|
+-
|
|
|
+- tsdata->max_support_points = chip_data->max_support_points;
|
|
|
+-
|
|
|
+- tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
|
|
|
+- "reset", GPIOD_OUT_HIGH);
|
|
|
+- if (IS_ERR(tsdata->reset_gpio)) {
|
|
|
+- error = PTR_ERR(tsdata->reset_gpio);
|
|
|
+- dev_err(&client->dev,
|
|
|
+- "Failed to request GPIO reset pin, error %d\n", error);
|
|
|
+- return error;
|
|
|
++ if (!pdata) {
|
|
|
++ error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata);
|
|
|
++ if (error) {
|
|
|
++ dev_err(&client->dev,
|
|
|
++ "DT probe failed and no platform data present\n");
|
|
|
++ return error;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ tsdata->reset_pin = pdata->reset_pin;
|
|
|
++ tsdata->irq_pin = pdata->irq_pin;
|
|
|
++ tsdata->wake_pin = -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+- tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev,
|
|
|
+- "wake", GPIOD_OUT_LOW);
|
|
|
+- if (IS_ERR(tsdata->wake_gpio)) {
|
|
|
+- error = PTR_ERR(tsdata->wake_gpio);
|
|
|
+- dev_err(&client->dev,
|
|
|
+- "Failed to request GPIO wake pin, error %d\n", error);
|
|
|
++ error = edt_ft5x06_ts_reset(client, tsdata);
|
|
|
++ if (error)
|
|
|
+ return error;
|
|
|
+- }
|
|
|
+
|
|
|
+- if (tsdata->wake_gpio) {
|
|
|
+- usleep_range(5000, 6000);
|
|
|
+- gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (tsdata->reset_gpio) {
|
|
|
+- usleep_range(5000, 6000);
|
|
|
+- gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
|
|
|
+- msleep(300);
|
|
|
++ if (gpio_is_valid(tsdata->irq_pin)) {
|
|
|
++ error = devm_gpio_request_one(&client->dev, tsdata->irq_pin,
|
|
|
++ GPIOF_IN, "edt-ft5x06 irq");
|
|
|
++ if (error) {
|
|
|
++ dev_err(&client->dev,
|
|
|
++ "Failed to request GPIO %d, error %d\n",
|
|
|
++ tsdata->irq_pin, error);
|
|
|
++ return error;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ input = devm_input_allocate_device(&client->dev);
|
|
|
+@@ -957,7 +1141,12 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
|
|
+ }
|
|
|
+
|
|
|
+ edt_ft5x06_ts_set_regs(tsdata);
|
|
|
+- edt_ft5x06_ts_get_defaults(&client->dev, tsdata);
|
|
|
++
|
|
|
++ if (!pdata)
|
|
|
++ edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata);
|
|
|
++ else
|
|
|
++ edt_ft5x06_ts_get_defaults(tsdata, pdata);
|
|
|
++
|
|
|
+ edt_ft5x06_ts_get_parameters(tsdata);
|
|
|
+
|
|
|
+ dev_dbg(&client->dev,
|
|
|
+@@ -968,15 +1157,18 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
|
|
+ input->id.bustype = BUS_I2C;
|
|
|
+ input->dev.parent = &client->dev;
|
|
|
+
|
|
|
++ __set_bit(EV_SYN, input->evbit);
|
|
|
++ __set_bit(EV_KEY, input->evbit);
|
|
|
++ __set_bit(EV_ABS, input->evbit);
|
|
|
++ __set_bit(BTN_TOUCH, input->keybit);
|
|
|
++
|
|
|
++ input_set_abs_params(input, ABS_X, 0, tsdata->max_x - 1, 0, 0);
|
|
|
++ input_set_abs_params(input, ABS_Y, 0, tsdata->max_y - 1, 0, 0);
|
|
|
+ input_set_abs_params(input, ABS_MT_POSITION_X,
|
|
|
+- 0, tsdata->num_x * 64 - 1, 0, 0);
|
|
|
++ 0, tsdata->max_x - 1, 0, 0);
|
|
|
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
|
|
|
+- 0, tsdata->num_y * 64 - 1, 0, 0);
|
|
|
+-
|
|
|
+- touchscreen_parse_properties(input, true, &tsdata->prop);
|
|
|
+-
|
|
|
+- error = input_mt_init_slots(input, tsdata->max_support_points,
|
|
|
+- INPUT_MT_DIRECT);
|
|
|
++ 0, tsdata->max_y - 1, 0, 0);
|
|
|
++ error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
|
|
|
+ if (error) {
|
|
|
+ dev_err(&client->dev, "Unable to init MT slots.\n");
|
|
|
+ return error;
|
|
|
+@@ -985,13 +1177,9 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
|
|
+ input_set_drvdata(input, tsdata);
|
|
|
+ i2c_set_clientdata(client, tsdata);
|
|
|
+
|
|
|
+- irq_flags = irq_get_trigger_type(client->irq);
|
|
|
+- if (irq_flags == IRQF_TRIGGER_NONE)
|
|
|
+- irq_flags = IRQF_TRIGGER_FALLING;
|
|
|
+- irq_flags |= IRQF_ONESHOT;
|
|
|
+-
|
|
|
+- error = devm_request_threaded_irq(&client->dev, client->irq,
|
|
|
+- NULL, edt_ft5x06_ts_isr, irq_flags,
|
|
|
++ error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
|
|
++ edt_ft5x06_ts_isr,
|
|
|
++ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
|
+ client->name, tsdata);
|
|
|
+ if (error) {
|
|
|
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
|
|
|
+@@ -1011,9 +1199,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
|
|
+
|
|
|
+ dev_dbg(&client->dev,
|
|
|
+ "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
|
|
|
+- client->irq,
|
|
|
+- tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1,
|
|
|
+- tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
|
|
|
++ client->irq, tsdata->wake_pin, tsdata->reset_pin);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+@@ -1032,7 +1218,8 @@ static int edt_ft5x06_ts_remove(struct i2c_client *client)
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+-static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
|
|
|
++#ifdef CONFIG_PM_SLEEP
|
|
|
++static int edt_ft5x06_ts_suspend(struct device *dev)
|
|
|
+ {
|
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
|
+
|
|
|
+@@ -1042,7 +1229,7 @@ static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+-static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
|
|
|
++static int edt_ft5x06_ts_resume(struct device *dev)
|
|
|
+ {
|
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
|
+
|
|
|
+@@ -1051,31 +1238,22 @@ static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
++#endif
|
|
|
+
|
|
|
+ static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
|
|
|
+ edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
|
|
|
+
|
|
|
+-static const struct edt_i2c_chip_data edt_ft5x06_data = {
|
|
|
+- .max_support_points = 5,
|
|
|
+-};
|
|
|
+-
|
|
|
+-static const struct edt_i2c_chip_data edt_ft5506_data = {
|
|
|
+- .max_support_points = 10,
|
|
|
+-};
|
|
|
+-
|
|
|
+ static const struct i2c_device_id edt_ft5x06_ts_id[] = {
|
|
|
+- { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
|
|
|
+- { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
|
|
|
++ { "edt-ft5x06", 0, },
|
|
|
+ { /* sentinel */ }
|
|
|
+ };
|
|
|
+ MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
|
|
|
+
|
|
|
+ #ifdef CONFIG_OF
|
|
|
+ static const struct of_device_id edt_ft5x06_of_match[] = {
|
|
|
+- { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data },
|
|
|
+- { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
|
|
|
+- { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
|
|
|
+- { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
|
|
|
++ { .compatible = "edt,edt-ft5206", },
|
|
|
++ { .compatible = "edt,edt-ft5306", },
|
|
|
++ { .compatible = "edt,edt-ft5406", },
|
|
|
+ { /* sentinel */ }
|
|
|
+ };
|
|
|
+ MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
|
|
|
+@@ -1083,6 +1261,7 @@ MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
|
|
|
+
|
|
|
+ static struct i2c_driver edt_ft5x06_ts_driver = {
|
|
|
+ .driver = {
|
|
|
++ .owner = THIS_MODULE,
|
|
|
+ .name = "edt_ft5x06",
|
|
|
+ .of_match_table = of_match_ptr(edt_ft5x06_of_match),
|
|
|
+ .pm = &edt_ft5x06_ts_pm_ops,
|
|
|
+diff --git a/include/linux/input/edt-ft5x06.h b/include/linux/input/edt-ft5x06.h
|
|
|
+new file mode 100644
|
|
|
+index 0000000..8a1e0d1
|
|
|
+--- /dev/null
|
|
|
++++ b/include/linux/input/edt-ft5x06.h
|
|
|
+@@ -0,0 +1,24 @@
|
|
|
++#ifndef _EDT_FT5X06_H
|
|
|
++#define _EDT_FT5X06_H
|
|
|
++
|
|
|
++/*
|
|
|
++ * Copyright (c) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
|
|
|
++ *
|
|
|
++ * This program is free software; you can redistribute it and/or modify it
|
|
|
++ * under the terms of the GNU General Public License version 2 as published by
|
|
|
++ * the Free Software Foundation.
|
|
|
++ */
|
|
|
++
|
|
|
++struct edt_ft5x06_platform_data {
|
|
|
++ int irq_pin;
|
|
|
++ int reset_pin;
|
|
|
++
|
|
|
++ /* startup defaults for operational parameters */
|
|
|
++ bool use_parameters;
|
|
|
++ u8 gain;
|
|
|
++ u8 threshold;
|
|
|
++ u8 offset;
|
|
|
++ u8 report_rate;
|
|
|
++};
|
|
|
++
|
|
|
++#endif /* _EDT_FT5X06_H */
|