Pastebin
API
tools
faq
paste
Login
Sign up
Please fix the following errors:
New Paste
Syntax Highlighting
From 1ac1694be1a98d899247ff1597866a06422dce27 Mon Sep 17 00:00:00 2001 From: Robert Woerle <robert@linuxdevelopment.de> Date: Thu, 30 Jun 2016 14:01:59 +0200 Subject: backport edt touch Signed-off-by: Robert Woerle <robert@linuxdevelopment.de> --- drivers/input/touchscreen/edt-ft5x06.c | 761 +++++++++++++++++++++++++-------- 1 file changed, 571 insertions(+), 190 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 83fa1b1..f0c036c 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> + * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support) + * Lothar Waßmann <LW@KARO-electronics.de> (DT support) * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -33,10 +35,12 @@ #include <linux/debugfs.h> #include <linux/slab.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> #include <linux/input/mt.h> #include <linux/input/edt-ft5x06.h> -#define MAX_SUPPORT_POINTS 5 +#define DRIVER_VERSION "v1.0.1" #define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_REPORT_RATE 0x08 @@ -45,6 +49,14 @@ #define WORK_REGISTER_NUM_X 0x33 #define WORK_REGISTER_NUM_Y 0x34 +#define M09_REGISTER_THRESHOLD 0x80 +#define M09_REGISTER_GAIN 0x92 +#define M09_REGISTER_OFFSET 0x93 +#define M09_REGISTER_NUM_X 0x94 +#define M09_REGISTER_NUM_Y 0x95 + +#define NO_REGISTER 0xff + #define WORK_REGISTER_OPMODE 0x3c #define FACTORY_REGISTER_OPMODE 0x01 @@ -59,12 +71,34 @@ #define EDT_RAW_DATA_RETRIES 100 #define EDT_RAW_DATA_DELAY 1 /* msec */ +enum edt_ver { + M06, + M09, +}; + +struct edt_reg_addr { + int reg_threshold; + int reg_report_rate; + int reg_gain; + int reg_offset; + int reg_num_x; + int reg_num_y; +}; + struct edt_ft5x06_ts_data { struct i2c_client *client; struct input_dev *input; u16 num_x; u16 num_y; +/* struct gpio_desc *reset_pin; + struct gpio_desc *wake_pin; + struct gpio_desc *irq_pin; +*/ + int reset_pin; + int wake_pin; + int irq_pin; + #if defined(CONFIG_DEBUG_FS) struct dentry *debug_dir; u8 *raw_buffer; @@ -77,8 +111,25 @@ struct edt_ft5x06_ts_data { int gain; int offset; int report_rate; + int max_support_points; + + unsigned char rdbuf[20][61]; + + int queue_size; + int queue_ptn; + int invalidate_queue; + struct timer_list queue_up_timer; + bool events_valid; + int filter_cnt; 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, @@ -132,37 +183,26 @@ static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, return true; } -static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) +static void edt_ft5x06_report_event( void *dev_id) { struct edt_ft5x06_ts_data *tsdata = dev_id; - struct device *dev = &tsdata->client->dev; - u8 cmd = 0xf9; - u8 rdbuf[26]; - int i, type, x, y, id; - int error; - - memset(rdbuf, 0, sizeof(rdbuf)); - - error = edt_ft5x06_ts_readwrite(tsdata->client, - sizeof(cmd), &cmd, - sizeof(rdbuf), rdbuf); - if (error) { - dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", - error); + int i, type, x, y, id, tplen, offset; + + switch (tsdata->version){ + case M06: + offset = 5; + tplen = 4; + break; + case M09: + offset = 3; + tplen = 6; + break; + default: goto out; } - if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) { - dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", - rdbuf[0], rdbuf[1], rdbuf[2]); - goto out; - } - - if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26)) - goto out; - - for (i = 0; i < MAX_SUPPORT_POINTS; i++) { - u8 *buf = &rdbuf[i * 4 + 5]; + for (i = 0; i < tsdata->max_support_points; i++) { + u8 *buf = &tsdata->rdbuf[tsdata->queue_ptn][i * tplen + offset]; bool down; type = buf[0] >> 6; @@ -170,10 +210,14 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (type == TOUCH_EVENT_RESERVED) continue; + /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */ + if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN) + continue; + x = ((buf[0] << 8) | buf[1]) & 0x0fff; y = ((buf[2] << 8) | buf[3]) & 0x0fff; id = (buf[2] >> 4) & 0x0f; - down = (type != TOUCH_EVENT_UP); + down = type != TOUCH_EVENT_UP; input_mt_slot(tsdata->input, id); input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); @@ -189,20 +233,122 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) input_sync(tsdata->input); out: + return; +} + +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; + int offset, tplen, datalen; + int error; + + switch (tsdata->version) { + case M06: + 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 */ + + /* how many bytes to listen for */ + datalen = tplen * tsdata->max_support_points + offset + 1; + break; + + case M09: + cmd = 0x0; + offset = 3; + tplen = 6; + datalen = tplen * tsdata->max_support_points + 1 - cmd; + break; + + default: + goto out; + } + + memset(tsdata->rdbuf[tsdata->queue_ptn], 0, sizeof(tsdata->rdbuf[tsdata->queue_ptn])); + + error = edt_ft5x06_ts_readwrite(tsdata->client, + sizeof(cmd), &cmd, + datalen, tsdata->rdbuf[tsdata->queue_ptn]); + if (error) { + dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", + error); + goto out; + } + + /* M09 does not send header or CRC */ + if (tsdata->version == M06) { + if (tsdata->rdbuf[tsdata->queue_ptn][0] != 0xaa + || tsdata->rdbuf[tsdata->queue_ptn][1] != 0xaa + || tsdata->rdbuf[tsdata->queue_ptn][2] != datalen) { + dev_err_ratelimited(dev, + "Unexpected header: %02x%02x%02x!\n", + tsdata->rdbuf[tsdata->queue_ptn][0], + tsdata->rdbuf[tsdata->queue_ptn][1], + tsdata->rdbuf[tsdata->queue_ptn][2]); + goto out; + } + + if (!edt_ft5x06_ts_check_crc(tsdata, tsdata->rdbuf[tsdata->queue_ptn], datalen)) + goto out; + } + + if (!tsdata->events_valid) + tsdata->queue_ptn++; + else + edt_ft5x06_report_event(dev_id); + + if (tsdata->queue_ptn >= tsdata->queue_size) { + for (tsdata->queue_ptn = 0; tsdata->queue_ptn < tsdata->queue_size; tsdata->queue_ptn++) { + mod_timer(&tsdata->queue_up_timer, jiffies + msecs_to_jiffies(tsdata->invalidate_queue)); + edt_ft5x06_report_event(dev_id); + } + tsdata->events_valid = 1; + tsdata->queue_ptn = 0; + } + mod_timer(&tsdata->queue_up_timer, jiffies + msecs_to_jiffies(tsdata->invalidate_queue)); + +out: return IRQ_HANDLED; } +static void edt_ft5x06_queue_up_timer(unsigned long data) +{ + struct edt_ft5x06_ts_data *tsdata = (void *)data; + + /* count up if filter timeout came but had no valid amount */ + if (!tsdata->events_valid) + tsdata->filter_cnt++; + + tsdata->events_valid = 0; + tsdata->queue_ptn = 0; + /* clear queue again */ + memset(tsdata->rdbuf, 0, sizeof(tsdata->rdbuf)); +} + static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, u8 addr, u8 value) { u8 wrbuf[4]; - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - 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); + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + 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); + case M09: + wrbuf[0] = addr; + wrbuf[1] = value; + + return edt_ft5x06_ts_readwrite(tsdata->client, 2, + wrbuf, 0, NULL); + + default: + return -EINVAL; + } } static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, @@ -211,19 +357,36 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, u8 wrbuf[2], rdbuf[2]; int error; - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; - error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf); - if (error) - return error; + error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, + rdbuf); + if (error) + return error; - if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { - dev_err(&tsdata->client->dev, - "crc error: 0x%02x expected, got 0x%02x\n", - wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]); - return -EIO; + if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { + dev_err(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], + rdbuf[1]); + return -EIO; + } + break; + + case M09: + wrbuf[0] = addr; + error = edt_ft5x06_ts_readwrite(tsdata->client, 1, + wrbuf, 1, rdbuf); + if (error) + return error; + break; + + default: + return -EINVAL; } return rdbuf[0]; @@ -234,19 +397,21 @@ struct edt_ft5x06_attribute { size_t field_offset; u8 limit_low; u8 limit_high; - u8 addr; + u8 addr_m06; + u8 addr_m09; }; -#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \ +#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \ + _limit_low, _limit_high) \ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ .dattr = __ATTR(_field, _mode, \ edt_ft5x06_setting_show, \ edt_ft5x06_setting_store), \ - .field_offset = \ - offsetof(struct edt_ft5x06_ts_data, _field), \ + .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ + .addr_m06 = _addr_m06, \ + .addr_m09 = _addr_m09, \ .limit_low = _limit_low, \ .limit_high = _limit_high, \ - .addr = _addr, \ } static ssize_t edt_ft5x06_setting_show(struct device *dev, @@ -257,10 +422,11 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); struct edt_ft5x06_attribute *attr = container_of(dattr, struct edt_ft5x06_attribute, dattr); - u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + u8 *field = (u8 *)tsdata + attr->field_offset; int val; size_t count = 0; int error = 0; + u8 addr; mutex_lock(&tsdata->mutex); @@ -269,15 +435,33 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, goto out; } - val = edt_ft5x06_register_read(tsdata, attr->addr); - if (val < 0) { - error = val; - dev_err(&tsdata->client->dev, - "Failed to fetch attribute %s, error %d\n", - dattr->attr.name, error); + switch (tsdata->version) { + case M06: + addr = attr->addr_m06; + break; + + case M09: + addr = attr->addr_m09; + break; + + default: + error = -ENODEV; goto out; } + if (addr != NO_REGISTER) { + val = edt_ft5x06_register_read(tsdata, addr); + if (val < 0) { + error = val; + dev_err(&tsdata->client->dev, + "Failed to fetch attribute %s, error %d\n", + dattr->attr.name, error); + goto out; + } + } else { + val = *field; + } + if (val != *field) { dev_warn(&tsdata->client->dev, "%s: read (%d) and stored value (%d) differ\n", @@ -299,9 +483,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); struct edt_ft5x06_attribute *attr = container_of(dattr, struct edt_ft5x06_attribute, dattr); - u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + u8 *field = (u8 *)tsdata + attr->field_offset; unsigned int val; int error; + u8 addr; mutex_lock(&tsdata->mutex); @@ -319,14 +504,29 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, goto out; } - error = edt_ft5x06_register_write(tsdata, attr->addr, val); - if (error) { - dev_err(&tsdata->client->dev, - "Failed to update attribute %s, error: %d\n", - dattr->attr.name, error); + switch (tsdata->version) { + case M06: + addr = attr->addr_m06; + break; + + case M09: + addr = attr->addr_m09; + break; + + default: + error = -ENODEV; goto out; } + if (addr != NO_REGISTER) { + error = edt_ft5x06_register_write(tsdata, addr, val); + if (error) { + dev_err(&tsdata->client->dev, + "Failed to update attribute %s, error: %d\n", + dattr->attr.name, error); + goto out; + } + } *field = val; out: @@ -334,18 +534,29 @@ out: return error ?: count; } -static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31); -static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31); -static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, - WORK_REGISTER_THRESHOLD, 20, 80); -static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, - WORK_REGISTER_REPORT_RATE, 3, 14); +static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, + M09_REGISTER_GAIN, 0, 31); +static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, + M09_REGISTER_OFFSET, 0, 31); +static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, + M09_REGISTER_THRESHOLD, 20, 80); +static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, + NO_REGISTER, 3, 14); +static EDT_ATTR(queue_size, S_IWUSR | S_IRUGO, + NO_REGISTER, NO_REGISTER, 0, 20); +static EDT_ATTR(invalidate_queue, S_IWUSR | S_IRUGO, + NO_REGISTER, NO_REGISTER, 28, 100); +static EDT_ATTR(filter_cnt, S_IWUSR | S_IRUGO, + NO_REGISTER, NO_REGISTER, 0, 255); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, &edt_ft5x06_attr_offset.dattr.attr, &edt_ft5x06_attr_threshold.dattr.attr, &edt_ft5x06_attr_report_rate.dattr.attr, + &edt_ft5x06_attr_queue_size.dattr.attr, + &edt_ft5x06_attr_invalidate_queue.dattr.attr, + &edt_ft5x06_attr_filter_cnt.dattr.attr, NULL }; @@ -374,6 +585,9 @@ 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) + goto m09_out; + error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); if (error) { dev_err(&client->dev, @@ -406,12 +620,18 @@ err_out: enable_irq(client->irq); return error; + +m09_out: + dev_err(&client->dev, "No factory mode support for M09\n"); + return -EINVAL; + } static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) { struct i2c_client *client = tsdata->client; int retries = EDT_SWITCH_MODE_RETRIES; + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; int ret; int error; @@ -444,13 +664,14 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) tsdata->raw_buffer = NULL; /* restore parameters */ - edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD, + edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, tsdata->threshold); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN, + edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, tsdata->gain); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET, + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, tsdata->offset); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE, + if (reg_addr->reg_report_rate) + edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, tsdata->report_rate); enable_irq(client->irq); @@ -479,7 +700,7 @@ static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) if (mode != tsdata->factory_mode) { retval = mode ? edt_ft5x06_factory_mode(tsdata) : - edt_ft5x06_work_mode(tsdata); + edt_ft5x06_work_mode(tsdata); } mutex_unlock(&tsdata->mutex); @@ -568,7 +789,6 @@ out: return error ?: read; }; - static const struct file_operations debugfs_raw_data_fops = { .open = simple_open, .read = edt_ft5x06_debugfs_raw_data_read, @@ -594,8 +814,7 @@ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) { - if (tsdata->debug_dir) - debugfs_remove_recursive(tsdata->debug_dir); + debugfs_remove_recursive(tsdata->debug_dir); kfree(tsdata->raw_buffer); } @@ -614,131 +833,261 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ - - -static int edt_ft5x06_ts_reset(struct i2c_client *client, - int reset_pin) -{ - int error; - - if (gpio_is_valid(reset_pin)) { - /* this pulls reset down, enabling the low active reset */ - error = gpio_request_one(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", - reset_pin, error); - return error; - } - - mdelay(50); - gpio_set_value(reset_pin, 1); - mdelay(100); - } - - return 0; -} - static int edt_ft5x06_ts_identify(struct i2c_client *client, - char *model_name, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata, + char *fw_version) { u8 rdbuf[EDT_NAME_LEN]; char *p; int error; + char *model_name = tsdata->name; + /* see what we find if we assume it is a M06 * + * if we get less than EDT_NAME_LEN, we don't want + * to have garbage in there + */ + memset(rdbuf, 0, sizeof(rdbuf)); error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", EDT_NAME_LEN - 1, rdbuf); if (error) return error; - /* remove last '$' end marker */ - rdbuf[EDT_NAME_LEN - 1] = '\0'; - if (rdbuf[EDT_NAME_LEN - 2] == '$') - rdbuf[EDT_NAME_LEN - 2] = '\0'; + /* if we find something consistent, stay with that assumption + * at least M09 won't send 3 bytes here + */ + if (!(strncasecmp(rdbuf + 1, "EP0", 3))) { + tsdata->version = M06; + + /* remove last '$' end marker */ + rdbuf[EDT_NAME_LEN - 1] = '\0'; + if (rdbuf[EDT_NAME_LEN - 2] == '$') + rdbuf[EDT_NAME_LEN - 2] = '\0'; + + /* look for Model/Version separator */ + p = strchr(rdbuf, '*'); + if (p) + *p++ = '\0'; + strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); + strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + } else { + /* since there are only two versions around (M06, M09) */ + tsdata->version = M09; + + error = edt_ft5x06_ts_readwrite(client, 1, "\xA6", + 2, rdbuf); + if (error) + return error; + + strlcpy(fw_version, rdbuf, 2); - /* look for Model/Version separator */ - p = strchr(rdbuf, '*'); - if (p) - *p++ = '\0'; + error = edt_ft5x06_ts_readwrite(client, 1, "\xA8", + 1, rdbuf); + if (error) + return error; - strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); - strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09", + rdbuf[0] >> 4, rdbuf[0] & 0x0F); + } return 0; } #define EDT_ATTR_CHECKSET(name, reg) \ +do { \ 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) + edt_ft5x06_register_write(tsdata, reg, pdata->name); \ +} while (0) + +#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; + + EDT_GET_PROP(threshold, reg_addr->reg_threshold); + EDT_GET_PROP(gain, reg_addr->reg_gain); + EDT_GET_PROP(offset, reg_addr->reg_offset); +} 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; + if (!pdata->use_parameters) return; /* pick up defaults from the platform data */ - EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD); - EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN); - EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET); - EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE); + 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 edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) { + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + tsdata->threshold = edt_ft5x06_register_read(tsdata, - WORK_REGISTER_THRESHOLD); - tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN); - tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET); - tsdata->report_rate = edt_ft5x06_register_read(tsdata, - WORK_REGISTER_REPORT_RATE); - tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X); - tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); + reg_addr->reg_threshold); + tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); + tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); + if (reg_addr->reg_report_rate != NO_REGISTER) + tsdata->report_rate = edt_ft5x06_register_read(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); +} + +static void +edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) +{ + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + + switch (tsdata->version) { + case M06: + reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD; + reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; + reg_addr->reg_gain = WORK_REGISTER_GAIN; + reg_addr->reg_offset = WORK_REGISTER_OFFSET; + reg_addr->reg_num_x = WORK_REGISTER_NUM_X; + reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; + break; + + case M09: + reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; + reg_addr->reg_gain = M09_REGISTER_GAIN; + reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_num_x = M09_REGISTER_NUM_X; + reg_addr->reg_num_y = M09_REGISTER_NUM_Y; + break; + } +} + +#ifdef CONFIG_OF + +static const struct of_device_id edt_ft5x06_of_match[]; + +static void edt_ft5x06_ts_set_max_support_points(struct device *dev, + struct edt_ft5x06_ts_data *tsdata) +{ + struct edt_i2c_chip_data *chip_data; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(edt_ft5x06_of_match), dev); + + if (match) { + chip_data = (struct edt_i2c_chip_data *)match->data; + tsdata->max_support_points = chip_data->max_support_points; + } else { + tsdata->max_support_points = 5; + } + +} +#else +static void edt_ft5x06_ts_set_max_support_points(struct device *dev, + struct edt_ft5x06_ts_data *tsdata) +{ } +#endif static int edt_ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct edt_ft5x06_platform_data *pdata = - client->dev.platform_data; + dev_get_platdata(&client->dev); + struct device *dev = &client->dev; struct edt_ft5x06_ts_data *tsdata; struct input_dev *input; - int error; + int error, i; char fw_version[EDT_NAME_LEN]; - dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + dev_info(&client->dev, "%s probing for EDT FT5x06 I2C\n", DRIVER_VERSION); - if (!pdata) { - dev_err(&client->dev, "no platform data?\n"); - return -EINVAL; + tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); + if (!tsdata) { + dev_err(&client->dev, "failed to allocate driver data.\n"); + return -ENOMEM; } - error = edt_ft5x06_ts_reset(client, pdata->reset_pin); - if (error) - return error; + tsdata->reset_pin = 157; + tsdata->irq_pin = 140; - if (gpio_is_valid(pdata->irq_pin)) { - error = gpio_request_one(pdata->irq_pin, - GPIOF_IN, "edt-ft5x06 irq"); + if (gpio_is_valid(tsdata->reset_pin)) { + /* this pulls reset down, enabling the low active reset */ + error = gpio_request_one(tsdata->reset_pin, GPIOF_OUT_INIT_LOW, + "edt-ft5x06 reset"); if (error) { dev_err(&client->dev, - "Failed to request GPIO %d, error %d\n", - pdata->irq_pin, error); + "Failed to request GPIO %d as reset pin, error %d\n", + tsdata->reset_pin, error); return error; } + + mdelay(50); + gpio_set_value(tsdata->reset_pin, 1); + mdelay(300); } - tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL); - input = input_allocate_device(); - if (!tsdata || !input) { - dev_err(&client->dev, "failed to allocate driver data.\n"); - error = -ENOMEM; - goto err_free_mem; + + edt_ft5x06_ts_set_max_support_points(dev, tsdata); +/* + gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) { + error = PTR_ERR(gpio); + dev_err(&client->dev, + "Failed to request GPIO reset pin, error %d\n", error); + return error; + } + + tsdata->reset_pin = gpio; + + gpio = devm_gpiod_get_optional(&client->dev, "wake", GPIOD_OUT_LOW); + + if (IS_ERR(gpio)) { + error = PTR_ERR(gpio); + dev_err(&client->dev, + "Failed to request GPIO wake pin, error %d\n", error); + return error; + } + + tsdata->wake_pin = gpio; + + gpio = devm_gpiod_get_optional(&client->dev, "irq", GPIOD_IN); + + if (IS_ERR(gpio)) { + error = PTR_ERR(gpio); + dev_err(&client->dev, + "Failed to request GPIO irq pin, error %d\n", error); + return error; + } + + tsdata->irq_pin = gpio; + + if (tsdata->wake_pin) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(tsdata->wake_pin, 1); + } + + if (tsdata->reset_pin) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(tsdata->reset_pin, 0); + msleep(300); + } +*/ + input = devm_input_allocate_device(&client->dev); + if (!input) { + dev_err(&client->dev, "failed to allocate input device.\n"); + return -ENOMEM; } mutex_init(&tsdata->mutex); @@ -746,16 +1095,28 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->input = input; tsdata->factory_mode = false; - error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version); + error = edt_ft5x06_ts_identify(client, tsdata, fw_version); if (error) { dev_err(&client->dev, "touchscreen probe failed\n"); - goto err_free_mem; + return error; } - edt_ft5x06_ts_get_defaults(tsdata, pdata); + /* default queue size to 5 */ + tsdata->queue_size = 5; + + /* default queue timeout to 35ms */ + tsdata->invalidate_queue = 35; + + edt_ft5x06_ts_set_regs(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, + dev_info(&client->dev, "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); @@ -766,33 +1127,48 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, __set_bit(EV_SYN, input->evbit); __set_bit(EV_KEY, input->evbit); __set_bit(EV_ABS, input->evbit); + __set_bit(INPUT_PROP_DIRECT, input->propbit); __set_bit(BTN_TOUCH, input->keybit); input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_X, - 0, tsdata->num_x * 64 - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y * 64 - 1, 0, 0); - error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, + 0, tsdata->num_x * 64 - 1, 0, 0); + + //if (!pdata) + // touchscreen_parse_properties(input, true); + + error = input_mt_init_slots(input, tsdata->max_support_points, 0); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); - goto err_free_mem; + return error; } input_set_drvdata(input, tsdata); i2c_set_clientdata(client, tsdata); - error = request_threaded_irq(client->irq, NULL, edt_ft5x06_ts_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - client->name, tsdata); + for (i = 0; i < tsdata->queue_size; i++) + memset(tsdata->rdbuf[i], 0, sizeof(tsdata->rdbuf[i])); + + setup_timer(&tsdata->queue_up_timer, edt_ft5x06_queue_up_timer, + (unsigned long)tsdata); + + tsdata->events_valid = 0; + tsdata->queue_ptn = 0; + + 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"); - goto err_free_mem; + return error; } error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); if (error) - goto err_free_irq; + return error; error = input_register_device(input); if (error) @@ -801,50 +1177,31 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); device_init_wakeup(&client->dev, 1); - dev_dbg(&client->dev, - "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n", - pdata->irq_pin, pdata->reset_pin); + dev_info(&client->dev, + "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", + client->irq, + tsdata->wake_pin, + tsdata->reset_pin); return 0; err_remove_attrs: sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); -err_free_irq: - free_irq(client->irq, tsdata); -err_free_mem: - input_free_device(input); - kfree(tsdata); - - if (gpio_is_valid(pdata->irq_pin)) - gpio_free(pdata->irq_pin); - return error; } static int edt_ft5x06_ts_remove(struct i2c_client *client) { - const struct edt_ft5x06_platform_data *pdata = - dev_get_platdata(&client->dev); struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); edt_ft5x06_ts_teardown_debugfs(tsdata); + del_timer_sync(&tsdata->queue_up_timer); sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); - free_irq(client->irq, tsdata); - input_unregister_device(tsdata->input); - - if (gpio_is_valid(pdata->irq_pin)) - gpio_free(pdata->irq_pin); - if (gpio_is_valid(pdata->reset_pin)) - gpio_free(pdata->reset_pin); - - kfree(tsdata); - return 0; } -#ifdef CONFIG_PM_SLEEP -static int edt_ft5x06_ts_suspend(struct device *dev) +static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -854,7 +1211,7 @@ static int edt_ft5x06_ts_suspend(struct device *dev) return 0; } -static int edt_ft5x06_ts_resume(struct device *dev) +static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -863,21 +1220,41 @@ static int 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 i2c_device_id edt_ft5x06_ts_id[] = { - { "edt-ft5x06", 0 }, - { } + { "edt-ft5x06", 0, }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); +#ifdef CONFIG_OF + +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 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}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); +#endif + 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, }, .id_table = edt_ft5x06_ts_id, @@ -887,6 +1264,10 @@ static struct i2c_driver edt_ft5x06_ts_driver = { module_i2c_driver(edt_ft5x06_ts_driver); +MODULE_ALIAS("i2c:edt-ft5206"); +MODULE_ALIAS("i2c:edt-ft5306"); +MODULE_ALIAS("i2c:edt-ft5406"); +MODULE_ALIAS("i2c:edt-ft5506"); MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>"); MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); MODULE_LICENSE("GPL"); -- cgit v0.11.0
Optional Paste Settings
Category:
None
Cryptocurrency
Cybersecurity
Fixit
Food
Gaming
Haiku
Help
History
Housing
Jokes
Legal
Money
Movies
Music
Pets
Photo
Science
Software
Source Code
Spirit
Sports
Travel
TV
Writing
Tags:
Syntax Highlighting:
None
Bash
C
C#
C++
CSS
HTML
JSON
Java
JavaScript
Lua
Markdown (PRO members only)
Objective C
PHP
Perl
Python
Ruby
Swift
4CS
6502 ACME Cross Assembler
6502 Kick Assembler
6502 TASM/64TASS
ABAP
AIMMS
ALGOL 68
APT Sources
ARM
ASM (NASM)
ASP
ActionScript
ActionScript 3
Ada
Apache Log
AppleScript
Arduino
Asymptote
AutoIt
Autohotkey
Avisynth
Awk
BASCOM AVR
BNF
BOO
Bash
Basic4GL
Batch
BibTeX
Blitz Basic
Blitz3D
BlitzMax
BrainFuck
C
C (WinAPI)
C Intermediate Language
C for Macs
C#
C++
C++ (WinAPI)
C++ (with Qt extensions)
C: Loadrunner
CAD DCL
CAD Lisp
CFDG
CMake
COBOL
CSS
Ceylon
ChaiScript
Chapel
Clojure
Clone C
Clone C++
CoffeeScript
ColdFusion
Cuesheet
D
DCL
DCPU-16
DCS
DIV
DOT
Dart
Delphi
Delphi Prism (Oxygene)
Diff
E
ECMAScript
EPC
Easytrieve
Eiffel
Email
Erlang
Euphoria
F#
FO Language
Falcon
Filemaker
Formula One
Fortran
FreeBasic
FreeSWITCH
GAMBAS
GDB
GDScript
Game Maker
Genero
Genie
GetText
Go
Godot GLSL
Groovy
GwBasic
HQ9 Plus
HTML
HTML 5
Haskell
Haxe
HicEst
IDL
INI file
INTERCAL
IO
ISPF Panel Definition
Icon
Inno Script
J
JCL
JSON
Java
Java 5
JavaScript
Julia
KSP (Kontakt Script)
KiXtart
Kotlin
LDIF
LLVM
LOL Code
LScript
Latex
Liberty BASIC
Linden Scripting
Lisp
Loco Basic
Logtalk
Lotus Formulas
Lotus Script
Lua
M68000 Assembler
MIX Assembler
MK-61/52
MPASM
MXML
MagikSF
Make
MapBasic
Markdown (PRO members only)
MatLab
Mercury
MetaPost
Modula 2
Modula 3
Motorola 68000 HiSoft Dev
MySQL
Nagios
NetRexx
Nginx
Nim
NullSoft Installer
OCaml
OCaml Brief
Oberon 2
Objeck Programming Langua
Objective C
Octave
Open Object Rexx
OpenBSD PACKET FILTER
OpenGL Shading
Openoffice BASIC
Oracle 11
Oracle 8
Oz
PARI/GP
PCRE
PHP
PHP Brief
PL/I
PL/SQL
POV-Ray
ParaSail
Pascal
Pawn
Per
Perl
Perl 6
Phix
Pic 16
Pike
Pixel Bender
PostScript
PostgreSQL
PowerBuilder
PowerShell
ProFTPd
Progress
Prolog
Properties
ProvideX
Puppet
PureBasic
PyCon
Python
Python for S60
QBasic
QML
R
RBScript
REBOL
REG
RPM Spec
Racket
Rails
Rexx
Robots
Roff Manpage
Ruby
Ruby Gnuplot
Rust
SAS
SCL
SPARK
SPARQL
SQF
SQL
SSH Config
Scala
Scheme
Scilab
SdlBasic
Smalltalk
Smarty
StandardML
StoneScript
SuperCollider
Swift
SystemVerilog
T-SQL
TCL
TeXgraph
Tera Term
TypeScript
TypoScript
UPC
Unicon
UnrealScript
Urbi
VB.NET
VBScript
VHDL
VIM
Vala
Vedit
VeriLog
Visual Pro Log
VisualBasic
VisualFoxPro
WHOIS
WhiteSpace
Winbatch
XBasic
XML
XPP
Xojo
Xorg Config
YAML
YARA
Z80 Assembler
ZXBasic
autoconf
jQuery
mIRC
newLISP
q/kdb+
thinBasic
Paste Expiration:
Never
Burn after read
10 Minutes
1 Hour
1 Day
1 Week
2 Weeks
1 Month
6 Months
1 Year
Paste Exposure:
Public
Unlisted
Private
Folder:
(members only)
Password
NEW
Enabled
Disabled
Burn after read
NEW
Paste Name / Title:
Create New Paste
Hello
Guest
Sign Up
or
Login
Sign in with Facebook
Sign in with Twitter
Sign in with Google
You are currently not logged in, this means you can not edit or delete anything you paste.
Sign Up
or
Login
Public Pastes
Untitled
39 min ago | 0.52 KB
P4IGNORE for Unreal Development
1 hour ago | 2.10 KB
Untitled
1 hour ago | 13.08 KB
Matthew Quote
1 hour ago | 0.18 KB
Input_AOC
5 hours ago | 18.01 KB
Untitled
19 hours ago | 13.15 KB
Analog GPUs: THE FUTURE
1 day ago | 8.88 KB
Quotes I believe to be true.
1 day ago | 0.16 KB
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the
Cookies Policy
.
OK, I Understand
Not a member of Pastebin yet?
Sign Up
, it unlocks many cool features!