16d3a41abSAndy Shevchenko // SPDX-License-Identifier: GPL-2.0 243c4d13eSSimon Budig /* 343c4d13eSSimon Budig * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> 4fd335ab0SLothar Waßmann * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support) 5dac90dc2SLothar Waßmann * Lothar Waßmann <LW@KARO-electronics.de> (DT support) 643c4d13eSSimon Budig */ 743c4d13eSSimon Budig 843c4d13eSSimon Budig /* 943c4d13eSSimon Budig * This is a driver for the EDT "Polytouch" family of touch controllers 1043c4d13eSSimon Budig * based on the FocalTech FT5x06 line of chips. 1143c4d13eSSimon Budig * 1243c4d13eSSimon Budig * Development of this driver has been sponsored by Glyn: 1343c4d13eSSimon Budig * http://www.glyn.com/Products/Displays 1443c4d13eSSimon Budig */ 1543c4d13eSSimon Budig 168726e4c9SMarco Felsch #include <linux/debugfs.h> 178726e4c9SMarco Felsch #include <linux/delay.h> 188726e4c9SMarco Felsch #include <linux/gpio/consumer.h> 198726e4c9SMarco Felsch #include <linux/i2c.h> 2043c4d13eSSimon Budig #include <linux/interrupt.h> 2143c4d13eSSimon Budig #include <linux/input.h> 2243c4d13eSSimon Budig #include <linux/input/mt.h> 232c005598SMaxime Ripard #include <linux/input/touchscreen.h> 248726e4c9SMarco Felsch #include <linux/irq.h> 258726e4c9SMarco Felsch #include <linux/kernel.h> 268726e4c9SMarco Felsch #include <linux/module.h> 278726e4c9SMarco Felsch #include <linux/ratelimit.h> 287448bfecSMylène Josserand #include <linux/regulator/consumer.h> 298726e4c9SMarco Felsch #include <linux/slab.h> 308726e4c9SMarco Felsch #include <linux/uaccess.h> 318726e4c9SMarco Felsch 328726e4c9SMarco Felsch #include <asm/unaligned.h> 3343c4d13eSSimon Budig 3443c4d13eSSimon Budig #define WORK_REGISTER_THRESHOLD 0x00 3543c4d13eSSimon Budig #define WORK_REGISTER_REPORT_RATE 0x08 3643c4d13eSSimon Budig #define WORK_REGISTER_GAIN 0x30 3743c4d13eSSimon Budig #define WORK_REGISTER_OFFSET 0x31 3843c4d13eSSimon Budig #define WORK_REGISTER_NUM_X 0x33 3943c4d13eSSimon Budig #define WORK_REGISTER_NUM_Y 0x34 4043c4d13eSSimon Budig 4121d1611aSMarco Felsch #define PMOD_REGISTER_ACTIVE 0x00 4221d1611aSMarco Felsch #define PMOD_REGISTER_HIBERNATE 0x03 4321d1611aSMarco Felsch 44fd335ab0SLothar Waßmann #define M09_REGISTER_THRESHOLD 0x80 45fd335ab0SLothar Waßmann #define M09_REGISTER_GAIN 0x92 46fd335ab0SLothar Waßmann #define M09_REGISTER_OFFSET 0x93 47fd335ab0SLothar Waßmann #define M09_REGISTER_NUM_X 0x94 48fd335ab0SLothar Waßmann #define M09_REGISTER_NUM_Y 0x95 49fd335ab0SLothar Waßmann 50a2f39dacSMarco Felsch #define EV_REGISTER_THRESHOLD 0x40 51a2f39dacSMarco Felsch #define EV_REGISTER_GAIN 0x41 52b6eba860SMarco Felsch #define EV_REGISTER_OFFSET_Y 0x45 53b6eba860SMarco Felsch #define EV_REGISTER_OFFSET_X 0x46 54a2f39dacSMarco Felsch 55fd335ab0SLothar Waßmann #define NO_REGISTER 0xff 56fd335ab0SLothar Waßmann 5743c4d13eSSimon Budig #define WORK_REGISTER_OPMODE 0x3c 5843c4d13eSSimon Budig #define FACTORY_REGISTER_OPMODE 0x01 5921d1611aSMarco Felsch #define PMOD_REGISTER_OPMODE 0xa5 6043c4d13eSSimon Budig 6143c4d13eSSimon Budig #define TOUCH_EVENT_DOWN 0x00 6243c4d13eSSimon Budig #define TOUCH_EVENT_UP 0x01 6343c4d13eSSimon Budig #define TOUCH_EVENT_ON 0x02 6443c4d13eSSimon Budig #define TOUCH_EVENT_RESERVED 0x03 6543c4d13eSSimon Budig 6643c4d13eSSimon Budig #define EDT_NAME_LEN 23 6743c4d13eSSimon Budig #define EDT_SWITCH_MODE_RETRIES 10 6843c4d13eSSimon Budig #define EDT_SWITCH_MODE_DELAY 5 /* msec */ 6943c4d13eSSimon Budig #define EDT_RAW_DATA_RETRIES 100 700eeecf60SAniroop Mathur #define EDT_RAW_DATA_DELAY 1000 /* usec */ 7143c4d13eSSimon Budig 7221d1611aSMarco Felsch enum edt_pmode { 7321d1611aSMarco Felsch EDT_PMODE_NOT_SUPPORTED, 7421d1611aSMarco Felsch EDT_PMODE_HIBERNATE, 7521d1611aSMarco Felsch EDT_PMODE_POWEROFF, 7621d1611aSMarco Felsch }; 7721d1611aSMarco Felsch 78fd335ab0SLothar Waßmann enum edt_ver { 79169110c3SSimon Budig EDT_M06, 80169110c3SSimon Budig EDT_M09, 81aed5d0eeSSimon Budig EDT_M12, 82a2f39dacSMarco Felsch EV_FT, 83169110c3SSimon Budig GENERIC_FT, 84fd335ab0SLothar Waßmann }; 85fd335ab0SLothar Waßmann 86fd335ab0SLothar Waßmann struct edt_reg_addr { 87fd335ab0SLothar Waßmann int reg_threshold; 88fd335ab0SLothar Waßmann int reg_report_rate; 89fd335ab0SLothar Waßmann int reg_gain; 90fd335ab0SLothar Waßmann int reg_offset; 91b6eba860SMarco Felsch int reg_offset_x; 92b6eba860SMarco Felsch int reg_offset_y; 93fd335ab0SLothar Waßmann int reg_num_x; 94fd335ab0SLothar Waßmann int reg_num_y; 95fd335ab0SLothar Waßmann }; 96fd335ab0SLothar Waßmann 9743c4d13eSSimon Budig struct edt_ft5x06_ts_data { 9843c4d13eSSimon Budig struct i2c_client *client; 9943c4d13eSSimon Budig struct input_dev *input; 100ad368eb2SHans de Goede struct touchscreen_properties prop; 10143c4d13eSSimon Budig u16 num_x; 10243c4d13eSSimon Budig u16 num_y; 1037448bfecSMylène Josserand struct regulator *vcc; 10443c4d13eSSimon Budig 10513c23cd1SFranklin S Cooper Jr struct gpio_desc *reset_gpio; 10613c23cd1SFranklin S Cooper Jr struct gpio_desc *wake_gpio; 107dac90dc2SLothar Waßmann 10843c4d13eSSimon Budig #if defined(CONFIG_DEBUG_FS) 10943c4d13eSSimon Budig struct dentry *debug_dir; 11043c4d13eSSimon Budig u8 *raw_buffer; 11143c4d13eSSimon Budig size_t raw_bufsize; 11243c4d13eSSimon Budig #endif 11343c4d13eSSimon Budig 11443c4d13eSSimon Budig struct mutex mutex; 11543c4d13eSSimon Budig bool factory_mode; 11621d1611aSMarco Felsch enum edt_pmode suspend_mode; 11743c4d13eSSimon Budig int threshold; 11843c4d13eSSimon Budig int gain; 11943c4d13eSSimon Budig int offset; 120b6eba860SMarco Felsch int offset_x; 121b6eba860SMarco Felsch int offset_y; 12243c4d13eSSimon Budig int report_rate; 123b1d2a3ecSFranklin S Cooper Jr int max_support_points; 12443c4d13eSSimon Budig 12543c4d13eSSimon Budig char name[EDT_NAME_LEN]; 126fd335ab0SLothar Waßmann 127fd335ab0SLothar Waßmann struct edt_reg_addr reg_addr; 128fd335ab0SLothar Waßmann enum edt_ver version; 12943c4d13eSSimon Budig }; 13043c4d13eSSimon Budig 131b1d2a3ecSFranklin S Cooper Jr struct edt_i2c_chip_data { 132b1d2a3ecSFranklin S Cooper Jr int max_support_points; 133b1d2a3ecSFranklin S Cooper Jr }; 134b1d2a3ecSFranklin S Cooper Jr 13543c4d13eSSimon Budig static int edt_ft5x06_ts_readwrite(struct i2c_client *client, 13643c4d13eSSimon Budig u16 wr_len, u8 *wr_buf, 13743c4d13eSSimon Budig u16 rd_len, u8 *rd_buf) 13843c4d13eSSimon Budig { 13943c4d13eSSimon Budig struct i2c_msg wrmsg[2]; 14043c4d13eSSimon Budig int i = 0; 14143c4d13eSSimon Budig int ret; 14243c4d13eSSimon Budig 14343c4d13eSSimon Budig if (wr_len) { 14443c4d13eSSimon Budig wrmsg[i].addr = client->addr; 14543c4d13eSSimon Budig wrmsg[i].flags = 0; 14643c4d13eSSimon Budig wrmsg[i].len = wr_len; 14743c4d13eSSimon Budig wrmsg[i].buf = wr_buf; 14843c4d13eSSimon Budig i++; 14943c4d13eSSimon Budig } 15043c4d13eSSimon Budig if (rd_len) { 15143c4d13eSSimon Budig wrmsg[i].addr = client->addr; 15243c4d13eSSimon Budig wrmsg[i].flags = I2C_M_RD; 15343c4d13eSSimon Budig wrmsg[i].len = rd_len; 15443c4d13eSSimon Budig wrmsg[i].buf = rd_buf; 15543c4d13eSSimon Budig i++; 15643c4d13eSSimon Budig } 15743c4d13eSSimon Budig 15843c4d13eSSimon Budig ret = i2c_transfer(client->adapter, wrmsg, i); 15943c4d13eSSimon Budig if (ret < 0) 16043c4d13eSSimon Budig return ret; 16143c4d13eSSimon Budig if (ret != i) 16243c4d13eSSimon Budig return -EIO; 16343c4d13eSSimon Budig 16443c4d13eSSimon Budig return 0; 16543c4d13eSSimon Budig } 16643c4d13eSSimon Budig 16743c4d13eSSimon Budig static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, 16843c4d13eSSimon Budig u8 *buf, int buflen) 16943c4d13eSSimon Budig { 17043c4d13eSSimon Budig int i; 17143c4d13eSSimon Budig u8 crc = 0; 17243c4d13eSSimon Budig 17343c4d13eSSimon Budig for (i = 0; i < buflen - 1; i++) 17443c4d13eSSimon Budig crc ^= buf[i]; 17543c4d13eSSimon Budig 17643c4d13eSSimon Budig if (crc != buf[buflen-1]) { 17743c4d13eSSimon Budig dev_err_ratelimited(&tsdata->client->dev, 17843c4d13eSSimon Budig "crc error: 0x%02x expected, got 0x%02x\n", 17943c4d13eSSimon Budig crc, buf[buflen-1]); 18043c4d13eSSimon Budig return false; 18143c4d13eSSimon Budig } 18243c4d13eSSimon Budig 18343c4d13eSSimon Budig return true; 18443c4d13eSSimon Budig } 18543c4d13eSSimon Budig 18643c4d13eSSimon Budig static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) 18743c4d13eSSimon Budig { 18843c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata = dev_id; 18943c4d13eSSimon Budig struct device *dev = &tsdata->client->dev; 190fd335ab0SLothar Waßmann u8 cmd; 1919378c025SFranklin S Cooper Jr u8 rdbuf[63]; 19243c4d13eSSimon Budig int i, type, x, y, id; 193c789f1fbSFranklin S Cooper Jr int offset, tplen, datalen, crclen; 19443c4d13eSSimon Budig int error; 19543c4d13eSSimon Budig 196fd335ab0SLothar Waßmann switch (tsdata->version) { 197169110c3SSimon Budig case EDT_M06: 198fd335ab0SLothar Waßmann cmd = 0xf9; /* tell the controller to send touch data */ 199fd335ab0SLothar Waßmann offset = 5; /* where the actual touch data starts */ 200fd335ab0SLothar Waßmann tplen = 4; /* data comes in so called frames */ 201c789f1fbSFranklin S Cooper Jr crclen = 1; /* length of the crc data */ 202fd335ab0SLothar Waßmann break; 203fd335ab0SLothar Waßmann 204169110c3SSimon Budig case EDT_M09: 205aed5d0eeSSimon Budig case EDT_M12: 206a2f39dacSMarco Felsch case EV_FT: 207169110c3SSimon Budig case GENERIC_FT: 2089378c025SFranklin S Cooper Jr cmd = 0x0; 2099378c025SFranklin S Cooper Jr offset = 3; 210fd335ab0SLothar Waßmann tplen = 6; 211c789f1fbSFranklin S Cooper Jr crclen = 0; 212fd335ab0SLothar Waßmann break; 213fd335ab0SLothar Waßmann 214fd335ab0SLothar Waßmann default: 215fd335ab0SLothar Waßmann goto out; 216fd335ab0SLothar Waßmann } 217fd335ab0SLothar Waßmann 21843c4d13eSSimon Budig memset(rdbuf, 0, sizeof(rdbuf)); 219b1d2a3ecSFranklin S Cooper Jr datalen = tplen * tsdata->max_support_points + offset + crclen; 22043c4d13eSSimon Budig 22143c4d13eSSimon Budig error = edt_ft5x06_ts_readwrite(tsdata->client, 22243c4d13eSSimon Budig sizeof(cmd), &cmd, 223fd335ab0SLothar Waßmann datalen, rdbuf); 22443c4d13eSSimon Budig if (error) { 22543c4d13eSSimon Budig dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", 22643c4d13eSSimon Budig error); 22743c4d13eSSimon Budig goto out; 22843c4d13eSSimon Budig } 22943c4d13eSSimon Budig 230aed5d0eeSSimon Budig /* M09/M12 does not send header or CRC */ 231169110c3SSimon Budig if (tsdata->version == EDT_M06) { 232fd335ab0SLothar Waßmann if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || 233fd335ab0SLothar Waßmann rdbuf[2] != datalen) { 234fd335ab0SLothar Waßmann dev_err_ratelimited(dev, 235fd335ab0SLothar Waßmann "Unexpected header: %02x%02x%02x!\n", 23643c4d13eSSimon Budig rdbuf[0], rdbuf[1], rdbuf[2]); 23743c4d13eSSimon Budig goto out; 23843c4d13eSSimon Budig } 23943c4d13eSSimon Budig 240fd335ab0SLothar Waßmann if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) 24143c4d13eSSimon Budig goto out; 242fd335ab0SLothar Waßmann } 24343c4d13eSSimon Budig 244b1d2a3ecSFranklin S Cooper Jr for (i = 0; i < tsdata->max_support_points; i++) { 245fd335ab0SLothar Waßmann u8 *buf = &rdbuf[i * tplen + offset]; 24643c4d13eSSimon Budig 24743c4d13eSSimon Budig type = buf[0] >> 6; 24843c4d13eSSimon Budig /* ignore Reserved events */ 24943c4d13eSSimon Budig if (type == TOUCH_EVENT_RESERVED) 25043c4d13eSSimon Budig continue; 25143c4d13eSSimon Budig 252fd335ab0SLothar Waßmann /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */ 253169110c3SSimon Budig if (tsdata->version == EDT_M06 && type == TOUCH_EVENT_DOWN) 254ee3e946eSLothar Waßmann continue; 255ee3e946eSLothar Waßmann 2561b9c698cSDmitry Torokhov x = get_unaligned_be16(buf) & 0x0fff; 2571b9c698cSDmitry Torokhov y = get_unaligned_be16(buf + 2) & 0x0fff; 258a2f39dacSMarco Felsch /* The FT5x26 send the y coordinate first */ 259a2f39dacSMarco Felsch if (tsdata->version == EV_FT) 260a2f39dacSMarco Felsch swap(x, y); 261a2f39dacSMarco Felsch 26243c4d13eSSimon Budig id = (buf[2] >> 4) & 0x0f; 26343c4d13eSSimon Budig 26443c4d13eSSimon Budig input_mt_slot(tsdata->input, id); 26517b92927SDmitry Torokhov if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, 26617b92927SDmitry Torokhov type != TOUCH_EVENT_UP)) 26717b92927SDmitry Torokhov touchscreen_report_pos(tsdata->input, &tsdata->prop, 26817b92927SDmitry Torokhov x, y, true); 26943c4d13eSSimon Budig } 27043c4d13eSSimon Budig 27143c4d13eSSimon Budig input_mt_report_pointer_emulation(tsdata->input, true); 27243c4d13eSSimon Budig input_sync(tsdata->input); 27343c4d13eSSimon Budig 27443c4d13eSSimon Budig out: 27543c4d13eSSimon Budig return IRQ_HANDLED; 27643c4d13eSSimon Budig } 27743c4d13eSSimon Budig 27843c4d13eSSimon Budig static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, 27943c4d13eSSimon Budig u8 addr, u8 value) 28043c4d13eSSimon Budig { 28143c4d13eSSimon Budig u8 wrbuf[4]; 28243c4d13eSSimon Budig 283fd335ab0SLothar Waßmann switch (tsdata->version) { 284169110c3SSimon Budig case EDT_M06: 28543c4d13eSSimon Budig wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; 28643c4d13eSSimon Budig wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; 28743c4d13eSSimon Budig wrbuf[2] = value; 28843c4d13eSSimon Budig wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; 289fd335ab0SLothar Waßmann return edt_ft5x06_ts_readwrite(tsdata->client, 4, 290fd335ab0SLothar Waßmann wrbuf, 0, NULL); 291a2f39dacSMarco Felsch /* fallthrough */ 292169110c3SSimon Budig case EDT_M09: 293aed5d0eeSSimon Budig case EDT_M12: 294a2f39dacSMarco Felsch case EV_FT: 295169110c3SSimon Budig case GENERIC_FT: 296fd335ab0SLothar Waßmann wrbuf[0] = addr; 297fd335ab0SLothar Waßmann wrbuf[1] = value; 29843c4d13eSSimon Budig 299cc071acaSRobert Woerle return edt_ft5x06_ts_readwrite(tsdata->client, 2, 300fd335ab0SLothar Waßmann wrbuf, 0, NULL); 301fd335ab0SLothar Waßmann 302fd335ab0SLothar Waßmann default: 303fd335ab0SLothar Waßmann return -EINVAL; 304fd335ab0SLothar Waßmann } 30543c4d13eSSimon Budig } 30643c4d13eSSimon Budig 30743c4d13eSSimon Budig static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, 30843c4d13eSSimon Budig u8 addr) 30943c4d13eSSimon Budig { 31043c4d13eSSimon Budig u8 wrbuf[2], rdbuf[2]; 31143c4d13eSSimon Budig int error; 31243c4d13eSSimon Budig 313fd335ab0SLothar Waßmann switch (tsdata->version) { 314169110c3SSimon Budig case EDT_M06: 31543c4d13eSSimon Budig wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; 31643c4d13eSSimon Budig wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; 31743c4d13eSSimon Budig wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; 31843c4d13eSSimon Budig 319e2c3ecf0SDan Carpenter error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, 320e2c3ecf0SDan Carpenter rdbuf); 321e2c3ecf0SDan Carpenter if (error) 32243c4d13eSSimon Budig return error; 32343c4d13eSSimon Budig 32443c4d13eSSimon Budig if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { 32543c4d13eSSimon Budig dev_err(&tsdata->client->dev, 32643c4d13eSSimon Budig "crc error: 0x%02x expected, got 0x%02x\n", 327fd335ab0SLothar Waßmann wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], 328fd335ab0SLothar Waßmann rdbuf[1]); 32943c4d13eSSimon Budig return -EIO; 33043c4d13eSSimon Budig } 331fd335ab0SLothar Waßmann break; 332fd335ab0SLothar Waßmann 333a2f39dacSMarco Felsch /* fallthrough */ 334169110c3SSimon Budig case EDT_M09: 335aed5d0eeSSimon Budig case EDT_M12: 336a2f39dacSMarco Felsch case EV_FT: 337169110c3SSimon Budig case GENERIC_FT: 338fd335ab0SLothar Waßmann wrbuf[0] = addr; 339fd335ab0SLothar Waßmann error = edt_ft5x06_ts_readwrite(tsdata->client, 1, 340fd335ab0SLothar Waßmann wrbuf, 1, rdbuf); 341fd335ab0SLothar Waßmann if (error) 342fd335ab0SLothar Waßmann return error; 343fd335ab0SLothar Waßmann break; 344fd335ab0SLothar Waßmann 345fd335ab0SLothar Waßmann default: 346fd335ab0SLothar Waßmann return -EINVAL; 347fd335ab0SLothar Waßmann } 34843c4d13eSSimon Budig 34943c4d13eSSimon Budig return rdbuf[0]; 35043c4d13eSSimon Budig } 35143c4d13eSSimon Budig 35243c4d13eSSimon Budig struct edt_ft5x06_attribute { 35343c4d13eSSimon Budig struct device_attribute dattr; 35443c4d13eSSimon Budig size_t field_offset; 35543c4d13eSSimon Budig u8 limit_low; 35643c4d13eSSimon Budig u8 limit_high; 357fd335ab0SLothar Waßmann u8 addr_m06; 358fd335ab0SLothar Waßmann u8 addr_m09; 3592ebc1919SMarco Felsch u8 addr_ev; 36043c4d13eSSimon Budig }; 36143c4d13eSSimon Budig 3622ebc1919SMarco Felsch #define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev, \ 363fd335ab0SLothar Waßmann _limit_low, _limit_high) \ 36443c4d13eSSimon Budig struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ 36543c4d13eSSimon Budig .dattr = __ATTR(_field, _mode, \ 36643c4d13eSSimon Budig edt_ft5x06_setting_show, \ 36743c4d13eSSimon Budig edt_ft5x06_setting_store), \ 368fd335ab0SLothar Waßmann .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ 369fd335ab0SLothar Waßmann .addr_m06 = _addr_m06, \ 370fd335ab0SLothar Waßmann .addr_m09 = _addr_m09, \ 3712ebc1919SMarco Felsch .addr_ev = _addr_ev, \ 37243c4d13eSSimon Budig .limit_low = _limit_low, \ 37343c4d13eSSimon Budig .limit_high = _limit_high, \ 37443c4d13eSSimon Budig } 37543c4d13eSSimon Budig 37643c4d13eSSimon Budig static ssize_t edt_ft5x06_setting_show(struct device *dev, 37743c4d13eSSimon Budig struct device_attribute *dattr, 37843c4d13eSSimon Budig char *buf) 37943c4d13eSSimon Budig { 38043c4d13eSSimon Budig struct i2c_client *client = to_i2c_client(dev); 38143c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); 38243c4d13eSSimon Budig struct edt_ft5x06_attribute *attr = 38343c4d13eSSimon Budig container_of(dattr, struct edt_ft5x06_attribute, dattr); 3841730d814SLothar Waßmann u8 *field = (u8 *)tsdata + attr->field_offset; 38543c4d13eSSimon Budig int val; 38643c4d13eSSimon Budig size_t count = 0; 38743c4d13eSSimon Budig int error = 0; 388fd335ab0SLothar Waßmann u8 addr; 38943c4d13eSSimon Budig 39043c4d13eSSimon Budig mutex_lock(&tsdata->mutex); 39143c4d13eSSimon Budig 39243c4d13eSSimon Budig if (tsdata->factory_mode) { 39343c4d13eSSimon Budig error = -EIO; 39443c4d13eSSimon Budig goto out; 39543c4d13eSSimon Budig } 39643c4d13eSSimon Budig 397fd335ab0SLothar Waßmann switch (tsdata->version) { 398169110c3SSimon Budig case EDT_M06: 399fd335ab0SLothar Waßmann addr = attr->addr_m06; 400fd335ab0SLothar Waßmann break; 401fd335ab0SLothar Waßmann 402169110c3SSimon Budig case EDT_M09: 403aed5d0eeSSimon Budig case EDT_M12: 404169110c3SSimon Budig case GENERIC_FT: 405fd335ab0SLothar Waßmann addr = attr->addr_m09; 406fd335ab0SLothar Waßmann break; 407fd335ab0SLothar Waßmann 4082ebc1919SMarco Felsch case EV_FT: 4092ebc1919SMarco Felsch addr = attr->addr_ev; 4102ebc1919SMarco Felsch break; 4112ebc1919SMarco Felsch 412fd335ab0SLothar Waßmann default: 413fd335ab0SLothar Waßmann error = -ENODEV; 414fd335ab0SLothar Waßmann goto out; 415fd335ab0SLothar Waßmann } 416fd335ab0SLothar Waßmann 417fd335ab0SLothar Waßmann if (addr != NO_REGISTER) { 418fd335ab0SLothar Waßmann val = edt_ft5x06_register_read(tsdata, addr); 41943c4d13eSSimon Budig if (val < 0) { 42043c4d13eSSimon Budig error = val; 42143c4d13eSSimon Budig dev_err(&tsdata->client->dev, 42243c4d13eSSimon Budig "Failed to fetch attribute %s, error %d\n", 42343c4d13eSSimon Budig dattr->attr.name, error); 42443c4d13eSSimon Budig goto out; 42543c4d13eSSimon Budig } 426fd335ab0SLothar Waßmann } else { 427fd335ab0SLothar Waßmann val = *field; 428fd335ab0SLothar Waßmann } 42943c4d13eSSimon Budig 43043c4d13eSSimon Budig if (val != *field) { 43143c4d13eSSimon Budig dev_warn(&tsdata->client->dev, 43243c4d13eSSimon Budig "%s: read (%d) and stored value (%d) differ\n", 43343c4d13eSSimon Budig dattr->attr.name, val, *field); 43443c4d13eSSimon Budig *field = val; 43543c4d13eSSimon Budig } 43643c4d13eSSimon Budig 43743c4d13eSSimon Budig count = scnprintf(buf, PAGE_SIZE, "%d\n", val); 43843c4d13eSSimon Budig out: 43943c4d13eSSimon Budig mutex_unlock(&tsdata->mutex); 44043c4d13eSSimon Budig return error ?: count; 44143c4d13eSSimon Budig } 44243c4d13eSSimon Budig 44343c4d13eSSimon Budig static ssize_t edt_ft5x06_setting_store(struct device *dev, 44443c4d13eSSimon Budig struct device_attribute *dattr, 44543c4d13eSSimon Budig const char *buf, size_t count) 44643c4d13eSSimon Budig { 44743c4d13eSSimon Budig struct i2c_client *client = to_i2c_client(dev); 44843c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); 44943c4d13eSSimon Budig struct edt_ft5x06_attribute *attr = 45043c4d13eSSimon Budig container_of(dattr, struct edt_ft5x06_attribute, dattr); 4511730d814SLothar Waßmann u8 *field = (u8 *)tsdata + attr->field_offset; 45243c4d13eSSimon Budig unsigned int val; 45343c4d13eSSimon Budig int error; 454fd335ab0SLothar Waßmann u8 addr; 45543c4d13eSSimon Budig 45643c4d13eSSimon Budig mutex_lock(&tsdata->mutex); 45743c4d13eSSimon Budig 45843c4d13eSSimon Budig if (tsdata->factory_mode) { 45943c4d13eSSimon Budig error = -EIO; 46043c4d13eSSimon Budig goto out; 46143c4d13eSSimon Budig } 46243c4d13eSSimon Budig 46343c4d13eSSimon Budig error = kstrtouint(buf, 0, &val); 46443c4d13eSSimon Budig if (error) 46543c4d13eSSimon Budig goto out; 46643c4d13eSSimon Budig 46743c4d13eSSimon Budig if (val < attr->limit_low || val > attr->limit_high) { 46843c4d13eSSimon Budig error = -ERANGE; 46943c4d13eSSimon Budig goto out; 47043c4d13eSSimon Budig } 47143c4d13eSSimon Budig 472fd335ab0SLothar Waßmann switch (tsdata->version) { 473169110c3SSimon Budig case EDT_M06: 474fd335ab0SLothar Waßmann addr = attr->addr_m06; 475fd335ab0SLothar Waßmann break; 476fd335ab0SLothar Waßmann 477169110c3SSimon Budig case EDT_M09: 478aed5d0eeSSimon Budig case EDT_M12: 479169110c3SSimon Budig case GENERIC_FT: 480fd335ab0SLothar Waßmann addr = attr->addr_m09; 481fd335ab0SLothar Waßmann break; 482fd335ab0SLothar Waßmann 4832ebc1919SMarco Felsch case EV_FT: 4842ebc1919SMarco Felsch addr = attr->addr_ev; 4852ebc1919SMarco Felsch break; 4862ebc1919SMarco Felsch 487fd335ab0SLothar Waßmann default: 488fd335ab0SLothar Waßmann error = -ENODEV; 489fd335ab0SLothar Waßmann goto out; 490fd335ab0SLothar Waßmann } 491fd335ab0SLothar Waßmann 492fd335ab0SLothar Waßmann if (addr != NO_REGISTER) { 493fd335ab0SLothar Waßmann error = edt_ft5x06_register_write(tsdata, addr, val); 49443c4d13eSSimon Budig if (error) { 49543c4d13eSSimon Budig dev_err(&tsdata->client->dev, 49643c4d13eSSimon Budig "Failed to update attribute %s, error: %d\n", 49743c4d13eSSimon Budig dattr->attr.name, error); 49843c4d13eSSimon Budig goto out; 49943c4d13eSSimon Budig } 500fd335ab0SLothar Waßmann } 50143c4d13eSSimon Budig *field = val; 50243c4d13eSSimon Budig 50343c4d13eSSimon Budig out: 50443c4d13eSSimon Budig mutex_unlock(&tsdata->mutex); 50543c4d13eSSimon Budig return error ?: count; 50643c4d13eSSimon Budig } 50743c4d13eSSimon Budig 508aed5d0eeSSimon Budig /* m06, m09: range 0-31, m12: range 0-5 */ 509fd335ab0SLothar Waßmann static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 5102ebc1919SMarco Felsch M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31); 511aed5d0eeSSimon Budig /* m06, m09: range 0-31, m12: range 0-16 */ 512fd335ab0SLothar Waßmann static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 5132ebc1919SMarco Felsch M09_REGISTER_OFFSET, NO_REGISTER, 0, 31); 514b6eba860SMarco Felsch /* m06, m09, m12: no supported, ev_ft: range 0-80 */ 515b6eba860SMarco Felsch static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, 516b6eba860SMarco Felsch EV_REGISTER_OFFSET_X, 0, 80); 517b6eba860SMarco Felsch /* m06, m09, m12: no supported, ev_ft: range 0-80 */ 518b6eba860SMarco Felsch static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, 519b6eba860SMarco Felsch EV_REGISTER_OFFSET_Y, 0, 80); 520aed5d0eeSSimon Budig /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ 521fd335ab0SLothar Waßmann static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, 5222ebc1919SMarco Felsch M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); 523aed5d0eeSSimon Budig /* m06: range 3 to 14, m12: (0x64: 100Hz) */ 524fd335ab0SLothar Waßmann static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, 5252ebc1919SMarco Felsch NO_REGISTER, NO_REGISTER, 0, 255); 52643c4d13eSSimon Budig 52743c4d13eSSimon Budig static struct attribute *edt_ft5x06_attrs[] = { 52843c4d13eSSimon Budig &edt_ft5x06_attr_gain.dattr.attr, 52943c4d13eSSimon Budig &edt_ft5x06_attr_offset.dattr.attr, 530b6eba860SMarco Felsch &edt_ft5x06_attr_offset_x.dattr.attr, 531b6eba860SMarco Felsch &edt_ft5x06_attr_offset_y.dattr.attr, 53243c4d13eSSimon Budig &edt_ft5x06_attr_threshold.dattr.attr, 53343c4d13eSSimon Budig &edt_ft5x06_attr_report_rate.dattr.attr, 53443c4d13eSSimon Budig NULL 53543c4d13eSSimon Budig }; 53643c4d13eSSimon Budig 53743c4d13eSSimon Budig static const struct attribute_group edt_ft5x06_attr_group = { 53843c4d13eSSimon Budig .attrs = edt_ft5x06_attrs, 53943c4d13eSSimon Budig }; 54043c4d13eSSimon Budig 541f4ee52f3SMarco Felsch static void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata) 542f4ee52f3SMarco Felsch { 543f4ee52f3SMarco Felsch struct edt_reg_addr *reg_addr = &tsdata->reg_addr; 544f4ee52f3SMarco Felsch 545f4ee52f3SMarco Felsch edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, 546f4ee52f3SMarco Felsch tsdata->threshold); 547f4ee52f3SMarco Felsch edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, 548f4ee52f3SMarco Felsch tsdata->gain); 549f4ee52f3SMarco Felsch if (reg_addr->reg_offset != NO_REGISTER) 550f4ee52f3SMarco Felsch edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, 551f4ee52f3SMarco Felsch tsdata->offset); 552f4ee52f3SMarco Felsch if (reg_addr->reg_offset_x != NO_REGISTER) 553f4ee52f3SMarco Felsch edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, 554f4ee52f3SMarco Felsch tsdata->offset_x); 555f4ee52f3SMarco Felsch if (reg_addr->reg_offset_y != NO_REGISTER) 556f4ee52f3SMarco Felsch edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, 557f4ee52f3SMarco Felsch tsdata->offset_y); 558f4ee52f3SMarco Felsch if (reg_addr->reg_report_rate != NO_REGISTER) 559f4ee52f3SMarco Felsch edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, 560f4ee52f3SMarco Felsch tsdata->report_rate); 561f4ee52f3SMarco Felsch 562f4ee52f3SMarco Felsch } 563f4ee52f3SMarco Felsch 56443c4d13eSSimon Budig #ifdef CONFIG_DEBUG_FS 56543c4d13eSSimon Budig static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) 56643c4d13eSSimon Budig { 56743c4d13eSSimon Budig struct i2c_client *client = tsdata->client; 56843c4d13eSSimon Budig int retries = EDT_SWITCH_MODE_RETRIES; 56943c4d13eSSimon Budig int ret; 57043c4d13eSSimon Budig int error; 57143c4d13eSSimon Budig 5724b3e910dSDmitry Torokhov if (tsdata->version != EDT_M06) { 5734b3e910dSDmitry Torokhov dev_err(&client->dev, 5744b3e910dSDmitry Torokhov "No factory mode support for non-M06 devices\n"); 5754b3e910dSDmitry Torokhov return -EINVAL; 5764b3e910dSDmitry Torokhov } 5774b3e910dSDmitry Torokhov 57843c4d13eSSimon Budig disable_irq(client->irq); 57943c4d13eSSimon Budig 58043c4d13eSSimon Budig if (!tsdata->raw_buffer) { 58143c4d13eSSimon Budig tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y * 58243c4d13eSSimon Budig sizeof(u16); 58343c4d13eSSimon Budig tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL); 58443c4d13eSSimon Budig if (!tsdata->raw_buffer) { 58543c4d13eSSimon Budig error = -ENOMEM; 58643c4d13eSSimon Budig goto err_out; 58743c4d13eSSimon Budig } 58843c4d13eSSimon Budig } 58943c4d13eSSimon Budig 59043c4d13eSSimon Budig /* mode register is 0x3c when in the work mode */ 59143c4d13eSSimon Budig error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); 59243c4d13eSSimon Budig if (error) { 59343c4d13eSSimon Budig dev_err(&client->dev, 59443c4d13eSSimon Budig "failed to switch to factory mode, error %d\n", error); 59543c4d13eSSimon Budig goto err_out; 59643c4d13eSSimon Budig } 59743c4d13eSSimon Budig 59843c4d13eSSimon Budig tsdata->factory_mode = true; 59943c4d13eSSimon Budig do { 60043c4d13eSSimon Budig mdelay(EDT_SWITCH_MODE_DELAY); 60143c4d13eSSimon Budig /* mode register is 0x01 when in factory mode */ 60243c4d13eSSimon Budig ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE); 60343c4d13eSSimon Budig if (ret == 0x03) 60443c4d13eSSimon Budig break; 60543c4d13eSSimon Budig } while (--retries > 0); 60643c4d13eSSimon Budig 60743c4d13eSSimon Budig if (retries == 0) { 60843c4d13eSSimon Budig dev_err(&client->dev, "not in factory mode after %dms.\n", 60943c4d13eSSimon Budig EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); 61043c4d13eSSimon Budig error = -EIO; 61143c4d13eSSimon Budig goto err_out; 61243c4d13eSSimon Budig } 61343c4d13eSSimon Budig 61443c4d13eSSimon Budig return 0; 61543c4d13eSSimon Budig 61643c4d13eSSimon Budig err_out: 61743c4d13eSSimon Budig kfree(tsdata->raw_buffer); 61843c4d13eSSimon Budig tsdata->raw_buffer = NULL; 61943c4d13eSSimon Budig tsdata->factory_mode = false; 62043c4d13eSSimon Budig enable_irq(client->irq); 62143c4d13eSSimon Budig 62243c4d13eSSimon Budig return error; 62343c4d13eSSimon Budig } 62443c4d13eSSimon Budig 62543c4d13eSSimon Budig static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) 62643c4d13eSSimon Budig { 62743c4d13eSSimon Budig struct i2c_client *client = tsdata->client; 62843c4d13eSSimon Budig int retries = EDT_SWITCH_MODE_RETRIES; 62943c4d13eSSimon Budig int ret; 63043c4d13eSSimon Budig int error; 63143c4d13eSSimon Budig 63243c4d13eSSimon Budig /* mode register is 0x01 when in the factory mode */ 63343c4d13eSSimon Budig error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1); 63443c4d13eSSimon Budig if (error) { 63543c4d13eSSimon Budig dev_err(&client->dev, 63643c4d13eSSimon Budig "failed to switch to work mode, error: %d\n", error); 63743c4d13eSSimon Budig return error; 63843c4d13eSSimon Budig } 63943c4d13eSSimon Budig 64043c4d13eSSimon Budig tsdata->factory_mode = false; 64143c4d13eSSimon Budig 64243c4d13eSSimon Budig do { 64343c4d13eSSimon Budig mdelay(EDT_SWITCH_MODE_DELAY); 64443c4d13eSSimon Budig /* mode register is 0x01 when in factory mode */ 64543c4d13eSSimon Budig ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE); 64643c4d13eSSimon Budig if (ret == 0x01) 64743c4d13eSSimon Budig break; 64843c4d13eSSimon Budig } while (--retries > 0); 64943c4d13eSSimon Budig 65043c4d13eSSimon Budig if (retries == 0) { 65143c4d13eSSimon Budig dev_err(&client->dev, "not in work mode after %dms.\n", 65243c4d13eSSimon Budig EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); 65343c4d13eSSimon Budig tsdata->factory_mode = true; 65443c4d13eSSimon Budig return -EIO; 65543c4d13eSSimon Budig } 65643c4d13eSSimon Budig 65743c4d13eSSimon Budig kfree(tsdata->raw_buffer); 65843c4d13eSSimon Budig tsdata->raw_buffer = NULL; 65943c4d13eSSimon Budig 660f4ee52f3SMarco Felsch edt_ft5x06_restore_reg_parameters(tsdata); 66143c4d13eSSimon Budig enable_irq(client->irq); 66243c4d13eSSimon Budig 66343c4d13eSSimon Budig return 0; 66443c4d13eSSimon Budig } 66543c4d13eSSimon Budig 66643c4d13eSSimon Budig static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) 66743c4d13eSSimon Budig { 66843c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata = data; 66943c4d13eSSimon Budig 67043c4d13eSSimon Budig *mode = tsdata->factory_mode; 67143c4d13eSSimon Budig 67243c4d13eSSimon Budig return 0; 67343c4d13eSSimon Budig }; 67443c4d13eSSimon Budig 67543c4d13eSSimon Budig static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) 67643c4d13eSSimon Budig { 67743c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata = data; 67843c4d13eSSimon Budig int retval = 0; 67943c4d13eSSimon Budig 68043c4d13eSSimon Budig if (mode > 1) 68143c4d13eSSimon Budig return -ERANGE; 68243c4d13eSSimon Budig 68343c4d13eSSimon Budig mutex_lock(&tsdata->mutex); 68443c4d13eSSimon Budig 68543c4d13eSSimon Budig if (mode != tsdata->factory_mode) { 68643c4d13eSSimon Budig retval = mode ? edt_ft5x06_factory_mode(tsdata) : 68743c4d13eSSimon Budig edt_ft5x06_work_mode(tsdata); 68843c4d13eSSimon Budig } 68943c4d13eSSimon Budig 69043c4d13eSSimon Budig mutex_unlock(&tsdata->mutex); 69143c4d13eSSimon Budig 69243c4d13eSSimon Budig return retval; 69343c4d13eSSimon Budig }; 69443c4d13eSSimon Budig 69543c4d13eSSimon Budig DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, 69643c4d13eSSimon Budig edt_ft5x06_debugfs_mode_set, "%llu\n"); 69743c4d13eSSimon Budig 69843c4d13eSSimon Budig static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, 69943c4d13eSSimon Budig char __user *buf, size_t count, loff_t *off) 70043c4d13eSSimon Budig { 70143c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata = file->private_data; 70243c4d13eSSimon Budig struct i2c_client *client = tsdata->client; 70343c4d13eSSimon Budig int retries = EDT_RAW_DATA_RETRIES; 70443c4d13eSSimon Budig int val, i, error; 70543c4d13eSSimon Budig size_t read = 0; 70643c4d13eSSimon Budig int colbytes; 70743c4d13eSSimon Budig char wrbuf[3]; 70843c4d13eSSimon Budig u8 *rdbuf; 70943c4d13eSSimon Budig 71043c4d13eSSimon Budig if (*off < 0 || *off >= tsdata->raw_bufsize) 71143c4d13eSSimon Budig return 0; 71243c4d13eSSimon Budig 71343c4d13eSSimon Budig mutex_lock(&tsdata->mutex); 71443c4d13eSSimon Budig 71543c4d13eSSimon Budig if (!tsdata->factory_mode || !tsdata->raw_buffer) { 71643c4d13eSSimon Budig error = -EIO; 71743c4d13eSSimon Budig goto out; 71843c4d13eSSimon Budig } 71943c4d13eSSimon Budig 72043c4d13eSSimon Budig error = edt_ft5x06_register_write(tsdata, 0x08, 0x01); 72143c4d13eSSimon Budig if (error) { 72243c4d13eSSimon Budig dev_dbg(&client->dev, 72343c4d13eSSimon Budig "failed to write 0x08 register, error %d\n", error); 72443c4d13eSSimon Budig goto out; 72543c4d13eSSimon Budig } 72643c4d13eSSimon Budig 72743c4d13eSSimon Budig do { 7280eeecf60SAniroop Mathur usleep_range(EDT_RAW_DATA_DELAY, EDT_RAW_DATA_DELAY + 100); 72943c4d13eSSimon Budig val = edt_ft5x06_register_read(tsdata, 0x08); 73043c4d13eSSimon Budig if (val < 1) 73143c4d13eSSimon Budig break; 73243c4d13eSSimon Budig } while (--retries > 0); 73343c4d13eSSimon Budig 73443c4d13eSSimon Budig if (val < 0) { 73543c4d13eSSimon Budig error = val; 73643c4d13eSSimon Budig dev_dbg(&client->dev, 73743c4d13eSSimon Budig "failed to read 0x08 register, error %d\n", error); 73843c4d13eSSimon Budig goto out; 73943c4d13eSSimon Budig } 74043c4d13eSSimon Budig 74143c4d13eSSimon Budig if (retries == 0) { 74243c4d13eSSimon Budig dev_dbg(&client->dev, 74343c4d13eSSimon Budig "timed out waiting for register to settle\n"); 74443c4d13eSSimon Budig error = -ETIMEDOUT; 74543c4d13eSSimon Budig goto out; 74643c4d13eSSimon Budig } 74743c4d13eSSimon Budig 74843c4d13eSSimon Budig rdbuf = tsdata->raw_buffer; 74943c4d13eSSimon Budig colbytes = tsdata->num_y * sizeof(u16); 75043c4d13eSSimon Budig 75143c4d13eSSimon Budig wrbuf[0] = 0xf5; 75243c4d13eSSimon Budig wrbuf[1] = 0x0e; 75343c4d13eSSimon Budig for (i = 0; i < tsdata->num_x; i++) { 75443c4d13eSSimon Budig wrbuf[2] = i; /* column index */ 75543c4d13eSSimon Budig error = edt_ft5x06_ts_readwrite(tsdata->client, 75643c4d13eSSimon Budig sizeof(wrbuf), wrbuf, 75743c4d13eSSimon Budig colbytes, rdbuf); 75843c4d13eSSimon Budig if (error) 75943c4d13eSSimon Budig goto out; 76043c4d13eSSimon Budig 76143c4d13eSSimon Budig rdbuf += colbytes; 76243c4d13eSSimon Budig } 76343c4d13eSSimon Budig 76443c4d13eSSimon Budig read = min_t(size_t, count, tsdata->raw_bufsize - *off); 76535b1da4eSAxel Lin if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) { 76635b1da4eSAxel Lin error = -EFAULT; 76735b1da4eSAxel Lin goto out; 76835b1da4eSAxel Lin } 76935b1da4eSAxel Lin 77043c4d13eSSimon Budig *off += read; 77143c4d13eSSimon Budig out: 77243c4d13eSSimon Budig mutex_unlock(&tsdata->mutex); 77343c4d13eSSimon Budig return error ?: read; 77443c4d13eSSimon Budig }; 77543c4d13eSSimon Budig 77643c4d13eSSimon Budig static const struct file_operations debugfs_raw_data_fops = { 777f6c0df6aSWei Yongjun .open = simple_open, 77843c4d13eSSimon Budig .read = edt_ft5x06_debugfs_raw_data_read, 77943c4d13eSSimon Budig }; 78043c4d13eSSimon Budig 78121d1611aSMarco Felsch static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, 78243c4d13eSSimon Budig const char *debugfs_name) 78343c4d13eSSimon Budig { 78443c4d13eSSimon Budig tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); 78543c4d13eSSimon Budig 78643c4d13eSSimon Budig debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); 78743c4d13eSSimon Budig debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); 78843c4d13eSSimon Budig 78943c4d13eSSimon Budig debugfs_create_file("mode", S_IRUSR | S_IWUSR, 79043c4d13eSSimon Budig tsdata->debug_dir, tsdata, &debugfs_mode_fops); 79143c4d13eSSimon Budig debugfs_create_file("raw_data", S_IRUSR, 79243c4d13eSSimon Budig tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); 79343c4d13eSSimon Budig } 79443c4d13eSSimon Budig 79521d1611aSMarco Felsch static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) 79643c4d13eSSimon Budig { 79743c4d13eSSimon Budig debugfs_remove_recursive(tsdata->debug_dir); 798a1d0fa77SGuenter Roeck kfree(tsdata->raw_buffer); 79943c4d13eSSimon Budig } 80043c4d13eSSimon Budig 80143c4d13eSSimon Budig #else 80243c4d13eSSimon Budig 80321d1611aSMarco Felsch static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) 80421d1611aSMarco Felsch { 80521d1611aSMarco Felsch return -ENOSYS; 80621d1611aSMarco Felsch } 80721d1611aSMarco Felsch 80821d1611aSMarco Felsch static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, 80943c4d13eSSimon Budig const char *debugfs_name) 81043c4d13eSSimon Budig { 81143c4d13eSSimon Budig } 81243c4d13eSSimon Budig 81321d1611aSMarco Felsch static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) 81443c4d13eSSimon Budig { 81543c4d13eSSimon Budig } 81643c4d13eSSimon Budig 81743c4d13eSSimon Budig #endif /* CONFIG_DEBUGFS */ 81843c4d13eSSimon Budig 8195298cc4cSBill Pemberton static int edt_ft5x06_ts_identify(struct i2c_client *client, 820fd335ab0SLothar Waßmann struct edt_ft5x06_ts_data *tsdata, 82143c4d13eSSimon Budig char *fw_version) 82243c4d13eSSimon Budig { 82343c4d13eSSimon Budig u8 rdbuf[EDT_NAME_LEN]; 82443c4d13eSSimon Budig char *p; 82543c4d13eSSimon Budig int error; 826fd335ab0SLothar Waßmann char *model_name = tsdata->name; 82743c4d13eSSimon Budig 828fd335ab0SLothar Waßmann /* see what we find if we assume it is a M06 * 829fd335ab0SLothar Waßmann * if we get less than EDT_NAME_LEN, we don't want 830fd335ab0SLothar Waßmann * to have garbage in there 831fd335ab0SLothar Waßmann */ 832fd335ab0SLothar Waßmann memset(rdbuf, 0, sizeof(rdbuf)); 833aed5d0eeSSimon Budig error = edt_ft5x06_ts_readwrite(client, 1, "\xBB", 83443c4d13eSSimon Budig EDT_NAME_LEN - 1, rdbuf); 83543c4d13eSSimon Budig if (error) 83643c4d13eSSimon Budig return error; 83743c4d13eSSimon Budig 838aed5d0eeSSimon Budig /* Probe content for something consistent. 839aed5d0eeSSimon Budig * M06 starts with a response byte, M12 gives the data directly. 840aed5d0eeSSimon Budig * M09/Generic does not provide model number information. 841fd335ab0SLothar Waßmann */ 842aed5d0eeSSimon Budig if (!strncasecmp(rdbuf + 1, "EP0", 3)) { 843169110c3SSimon Budig tsdata->version = EDT_M06; 844fd335ab0SLothar Waßmann 84543c4d13eSSimon Budig /* remove last '$' end marker */ 84643c4d13eSSimon Budig rdbuf[EDT_NAME_LEN - 1] = '\0'; 84743c4d13eSSimon Budig if (rdbuf[EDT_NAME_LEN - 2] == '$') 84843c4d13eSSimon Budig rdbuf[EDT_NAME_LEN - 2] = '\0'; 84943c4d13eSSimon Budig 85043c4d13eSSimon Budig /* look for Model/Version separator */ 85143c4d13eSSimon Budig p = strchr(rdbuf, '*'); 85243c4d13eSSimon Budig if (p) 85343c4d13eSSimon Budig *p++ = '\0'; 85443c4d13eSSimon Budig strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); 85543c4d13eSSimon Budig strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); 856aed5d0eeSSimon Budig } else if (!strncasecmp(rdbuf, "EP0", 3)) { 857aed5d0eeSSimon Budig tsdata->version = EDT_M12; 858aed5d0eeSSimon Budig 859aed5d0eeSSimon Budig /* remove last '$' end marker */ 860aed5d0eeSSimon Budig rdbuf[EDT_NAME_LEN - 2] = '\0'; 861aed5d0eeSSimon Budig if (rdbuf[EDT_NAME_LEN - 3] == '$') 862aed5d0eeSSimon Budig rdbuf[EDT_NAME_LEN - 3] = '\0'; 863aed5d0eeSSimon Budig 864aed5d0eeSSimon Budig /* look for Model/Version separator */ 865aed5d0eeSSimon Budig p = strchr(rdbuf, '*'); 866aed5d0eeSSimon Budig if (p) 867aed5d0eeSSimon Budig *p++ = '\0'; 868aed5d0eeSSimon Budig strlcpy(model_name, rdbuf, EDT_NAME_LEN); 869aed5d0eeSSimon Budig strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); 870fd335ab0SLothar Waßmann } else { 871aed5d0eeSSimon Budig /* If it is not an EDT M06/M12 touchscreen, then the model 872169110c3SSimon Budig * detection is a bit hairy. The different ft5x06 873169110c3SSimon Budig * firmares around don't reliably implement the 874169110c3SSimon Budig * identification registers. Well, we'll take a shot. 875169110c3SSimon Budig * 876169110c3SSimon Budig * The main difference between generic focaltec based 877169110c3SSimon Budig * touches and EDT M09 is that we know how to retrieve 878169110c3SSimon Budig * the max coordinates for the latter. 879169110c3SSimon Budig */ 880169110c3SSimon Budig tsdata->version = GENERIC_FT; 881fd335ab0SLothar Waßmann 882fd335ab0SLothar Waßmann error = edt_ft5x06_ts_readwrite(client, 1, "\xA6", 883fd335ab0SLothar Waßmann 2, rdbuf); 884fd335ab0SLothar Waßmann if (error) 885fd335ab0SLothar Waßmann return error; 886fd335ab0SLothar Waßmann 887fd335ab0SLothar Waßmann strlcpy(fw_version, rdbuf, 2); 888fd335ab0SLothar Waßmann 889fd335ab0SLothar Waßmann error = edt_ft5x06_ts_readwrite(client, 1, "\xA8", 890fd335ab0SLothar Waßmann 1, rdbuf); 891fd335ab0SLothar Waßmann if (error) 892fd335ab0SLothar Waßmann return error; 893fd335ab0SLothar Waßmann 894169110c3SSimon Budig /* This "model identification" is not exact. Unfortunately 895169110c3SSimon Budig * not all firmwares for the ft5x06 put useful values in 896169110c3SSimon Budig * the identification registers. 897169110c3SSimon Budig */ 898169110c3SSimon Budig switch (rdbuf[0]) { 899169110c3SSimon Budig case 0x35: /* EDT EP0350M09 */ 900169110c3SSimon Budig case 0x43: /* EDT EP0430M09 */ 901169110c3SSimon Budig case 0x50: /* EDT EP0500M09 */ 902169110c3SSimon Budig case 0x57: /* EDT EP0570M09 */ 903169110c3SSimon Budig case 0x70: /* EDT EP0700M09 */ 904169110c3SSimon Budig tsdata->version = EDT_M09; 905fd335ab0SLothar Waßmann snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09", 906fd335ab0SLothar Waßmann rdbuf[0] >> 4, rdbuf[0] & 0x0F); 907169110c3SSimon Budig break; 908169110c3SSimon Budig case 0xa1: /* EDT EP1010ML00 */ 909169110c3SSimon Budig tsdata->version = EDT_M09; 910169110c3SSimon Budig snprintf(model_name, EDT_NAME_LEN, "EP%i%i0ML00", 911169110c3SSimon Budig rdbuf[0] >> 4, rdbuf[0] & 0x0F); 912169110c3SSimon Budig break; 913169110c3SSimon Budig case 0x5a: /* Solomon Goldentek Display */ 914169110c3SSimon Budig snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0"); 915169110c3SSimon Budig break; 916a2f39dacSMarco Felsch case 0x59: /* Evervision Display with FT5xx6 TS */ 917a2f39dacSMarco Felsch tsdata->version = EV_FT; 918a2f39dacSMarco Felsch error = edt_ft5x06_ts_readwrite(client, 1, "\x53", 919a2f39dacSMarco Felsch 1, rdbuf); 920a2f39dacSMarco Felsch if (error) 921a2f39dacSMarco Felsch return error; 922a2f39dacSMarco Felsch strlcpy(fw_version, rdbuf, 1); 923a2f39dacSMarco Felsch snprintf(model_name, EDT_NAME_LEN, 924a2f39dacSMarco Felsch "EVERVISION-FT5726NEi"); 925a2f39dacSMarco Felsch break; 926169110c3SSimon Budig default: 927169110c3SSimon Budig snprintf(model_name, EDT_NAME_LEN, 928169110c3SSimon Budig "generic ft5x06 (%02x)", 929169110c3SSimon Budig rdbuf[0]); 930169110c3SSimon Budig break; 931169110c3SSimon Budig } 932fd335ab0SLothar Waßmann } 93343c4d13eSSimon Budig 93443c4d13eSSimon Budig return 0; 93543c4d13eSSimon Budig } 93643c4d13eSSimon Budig 9372e23b7a9SDmitry Torokhov static void edt_ft5x06_ts_get_defaults(struct device *dev, 938dac90dc2SLothar Waßmann struct edt_ft5x06_ts_data *tsdata) 939dac90dc2SLothar Waßmann { 940fd335ab0SLothar Waßmann struct edt_reg_addr *reg_addr = &tsdata->reg_addr; 9412e23b7a9SDmitry Torokhov u32 val; 9422e23b7a9SDmitry Torokhov int error; 943fd335ab0SLothar Waßmann 9442e23b7a9SDmitry Torokhov error = device_property_read_u32(dev, "threshold", &val); 945dc262dfaSPhilipp Zabel if (!error) { 946dc262dfaSPhilipp Zabel edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, val); 947dc262dfaSPhilipp Zabel tsdata->threshold = val; 948dc262dfaSPhilipp Zabel } 9492e23b7a9SDmitry Torokhov 9502e23b7a9SDmitry Torokhov error = device_property_read_u32(dev, "gain", &val); 951dc262dfaSPhilipp Zabel if (!error) { 952dc262dfaSPhilipp Zabel edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, val); 953dc262dfaSPhilipp Zabel tsdata->gain = val; 954dc262dfaSPhilipp Zabel } 9552e23b7a9SDmitry Torokhov 9562e23b7a9SDmitry Torokhov error = device_property_read_u32(dev, "offset", &val); 957dc262dfaSPhilipp Zabel if (!error) { 958255cdaf7SMarco Felsch if (reg_addr->reg_offset != NO_REGISTER) 959255cdaf7SMarco Felsch edt_ft5x06_register_write(tsdata, 960255cdaf7SMarco Felsch reg_addr->reg_offset, val); 961dc262dfaSPhilipp Zabel tsdata->offset = val; 962dc262dfaSPhilipp Zabel } 963b6eba860SMarco Felsch 964b6eba860SMarco Felsch error = device_property_read_u32(dev, "offset-x", &val); 965b6eba860SMarco Felsch if (!error) { 966255cdaf7SMarco Felsch if (reg_addr->reg_offset_x != NO_REGISTER) 967255cdaf7SMarco Felsch edt_ft5x06_register_write(tsdata, 968255cdaf7SMarco Felsch reg_addr->reg_offset_x, val); 969b6eba860SMarco Felsch tsdata->offset_x = val; 970b6eba860SMarco Felsch } 971b6eba860SMarco Felsch 972b6eba860SMarco Felsch error = device_property_read_u32(dev, "offset-y", &val); 973b6eba860SMarco Felsch if (!error) { 974255cdaf7SMarco Felsch if (reg_addr->reg_offset_y != NO_REGISTER) 975255cdaf7SMarco Felsch edt_ft5x06_register_write(tsdata, 976255cdaf7SMarco Felsch reg_addr->reg_offset_y, val); 977b6eba860SMarco Felsch tsdata->offset_y = val; 978b6eba860SMarco Felsch } 979dac90dc2SLothar Waßmann } 980dac90dc2SLothar Waßmann 9815298cc4cSBill Pemberton static void 98243c4d13eSSimon Budig edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) 98343c4d13eSSimon Budig { 984fd335ab0SLothar Waßmann struct edt_reg_addr *reg_addr = &tsdata->reg_addr; 985fd335ab0SLothar Waßmann 98643c4d13eSSimon Budig tsdata->threshold = edt_ft5x06_register_read(tsdata, 987fd335ab0SLothar Waßmann reg_addr->reg_threshold); 988fd335ab0SLothar Waßmann tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); 989a2f39dacSMarco Felsch if (reg_addr->reg_offset != NO_REGISTER) 990a2f39dacSMarco Felsch tsdata->offset = 991a2f39dacSMarco Felsch edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); 992b6eba860SMarco Felsch if (reg_addr->reg_offset_x != NO_REGISTER) 993b6eba860SMarco Felsch tsdata->offset_x = edt_ft5x06_register_read(tsdata, 994b6eba860SMarco Felsch reg_addr->reg_offset_x); 995b6eba860SMarco Felsch if (reg_addr->reg_offset_y != NO_REGISTER) 996b6eba860SMarco Felsch tsdata->offset_y = edt_ft5x06_register_read(tsdata, 997b6eba860SMarco Felsch reg_addr->reg_offset_y); 998fd335ab0SLothar Waßmann if (reg_addr->reg_report_rate != NO_REGISTER) 99943c4d13eSSimon Budig tsdata->report_rate = edt_ft5x06_register_read(tsdata, 1000fd335ab0SLothar Waßmann reg_addr->reg_report_rate); 1001169110c3SSimon Budig if (tsdata->version == EDT_M06 || 1002aed5d0eeSSimon Budig tsdata->version == EDT_M09 || 1003aed5d0eeSSimon Budig tsdata->version == EDT_M12) { 1004169110c3SSimon Budig tsdata->num_x = edt_ft5x06_register_read(tsdata, 1005169110c3SSimon Budig reg_addr->reg_num_x); 1006169110c3SSimon Budig tsdata->num_y = edt_ft5x06_register_read(tsdata, 1007169110c3SSimon Budig reg_addr->reg_num_y); 1008169110c3SSimon Budig } else { 1009169110c3SSimon Budig tsdata->num_x = -1; 1010169110c3SSimon Budig tsdata->num_y = -1; 1011169110c3SSimon Budig } 1012fd335ab0SLothar Waßmann } 1013fd335ab0SLothar Waßmann 1014fd335ab0SLothar Waßmann static void 1015fd335ab0SLothar Waßmann edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) 1016fd335ab0SLothar Waßmann { 1017fd335ab0SLothar Waßmann struct edt_reg_addr *reg_addr = &tsdata->reg_addr; 1018fd335ab0SLothar Waßmann 1019fd335ab0SLothar Waßmann switch (tsdata->version) { 1020169110c3SSimon Budig case EDT_M06: 1021fd335ab0SLothar Waßmann reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD; 1022fd335ab0SLothar Waßmann reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; 1023fd335ab0SLothar Waßmann reg_addr->reg_gain = WORK_REGISTER_GAIN; 1024fd335ab0SLothar Waßmann reg_addr->reg_offset = WORK_REGISTER_OFFSET; 1025b6eba860SMarco Felsch reg_addr->reg_offset_x = NO_REGISTER; 1026b6eba860SMarco Felsch reg_addr->reg_offset_y = NO_REGISTER; 1027fd335ab0SLothar Waßmann reg_addr->reg_num_x = WORK_REGISTER_NUM_X; 1028fd335ab0SLothar Waßmann reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; 1029fd335ab0SLothar Waßmann break; 1030fd335ab0SLothar Waßmann 1031169110c3SSimon Budig case EDT_M09: 1032aed5d0eeSSimon Budig case EDT_M12: 1033fd335ab0SLothar Waßmann reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; 103447014752SLuca Ceresoli reg_addr->reg_report_rate = NO_REGISTER; 1035fd335ab0SLothar Waßmann reg_addr->reg_gain = M09_REGISTER_GAIN; 1036fd335ab0SLothar Waßmann reg_addr->reg_offset = M09_REGISTER_OFFSET; 1037b6eba860SMarco Felsch reg_addr->reg_offset_x = NO_REGISTER; 1038b6eba860SMarco Felsch reg_addr->reg_offset_y = NO_REGISTER; 1039fd335ab0SLothar Waßmann reg_addr->reg_num_x = M09_REGISTER_NUM_X; 1040fd335ab0SLothar Waßmann reg_addr->reg_num_y = M09_REGISTER_NUM_Y; 1041fd335ab0SLothar Waßmann break; 1042169110c3SSimon Budig 1043a2f39dacSMarco Felsch case EV_FT: 1044a2f39dacSMarco Felsch reg_addr->reg_threshold = EV_REGISTER_THRESHOLD; 1045a2f39dacSMarco Felsch reg_addr->reg_gain = EV_REGISTER_GAIN; 1046a2f39dacSMarco Felsch reg_addr->reg_offset = NO_REGISTER; 1047b6eba860SMarco Felsch reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X; 1048b6eba860SMarco Felsch reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y; 1049a2f39dacSMarco Felsch reg_addr->reg_num_x = NO_REGISTER; 1050a2f39dacSMarco Felsch reg_addr->reg_num_y = NO_REGISTER; 1051a2f39dacSMarco Felsch reg_addr->reg_report_rate = NO_REGISTER; 1052a2f39dacSMarco Felsch break; 1053a2f39dacSMarco Felsch 1054169110c3SSimon Budig case GENERIC_FT: 1055169110c3SSimon Budig /* this is a guesswork */ 1056169110c3SSimon Budig reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; 1057169110c3SSimon Budig reg_addr->reg_gain = M09_REGISTER_GAIN; 1058169110c3SSimon Budig reg_addr->reg_offset = M09_REGISTER_OFFSET; 1059b6eba860SMarco Felsch reg_addr->reg_offset_x = NO_REGISTER; 1060b6eba860SMarco Felsch reg_addr->reg_offset_y = NO_REGISTER; 1061169110c3SSimon Budig break; 1062fd335ab0SLothar Waßmann } 106343c4d13eSSimon Budig } 106443c4d13eSSimon Budig 10657448bfecSMylène Josserand static void edt_ft5x06_disable_regulator(void *arg) 10667448bfecSMylène Josserand { 10677448bfecSMylène Josserand struct edt_ft5x06_ts_data *data = arg; 10687448bfecSMylène Josserand 10697448bfecSMylène Josserand regulator_disable(data->vcc); 10707448bfecSMylène Josserand } 10717448bfecSMylène Josserand 10725298cc4cSBill Pemberton static int edt_ft5x06_ts_probe(struct i2c_client *client, 107343c4d13eSSimon Budig const struct i2c_device_id *id) 107443c4d13eSSimon Budig { 1075b1d2a3ecSFranklin S Cooper Jr const struct edt_i2c_chip_data *chip_data; 107643c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata; 1077e112324cSPhilipp Zabel u8 buf[2] = { 0xfc, 0x00 }; 107843c4d13eSSimon Budig struct input_dev *input; 1079f0bef75cSDmitry Torokhov unsigned long irq_flags; 108043c4d13eSSimon Budig int error; 108143c4d13eSSimon Budig char fw_version[EDT_NAME_LEN]; 108243c4d13eSSimon Budig 108343c4d13eSSimon Budig dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); 108443c4d13eSSimon Budig 108502300bd6SLothar Waßmann tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); 108602300bd6SLothar Waßmann if (!tsdata) { 108743c4d13eSSimon Budig dev_err(&client->dev, "failed to allocate driver data.\n"); 108802300bd6SLothar Waßmann return -ENOMEM; 108902300bd6SLothar Waßmann } 109002300bd6SLothar Waßmann 1091fc226eb2SAndy Shevchenko chip_data = device_get_match_data(&client->dev); 1092b1d2a3ecSFranklin S Cooper Jr if (!chip_data) 1093b1d2a3ecSFranklin S Cooper Jr chip_data = (const struct edt_i2c_chip_data *)id->driver_data; 1094b1d2a3ecSFranklin S Cooper Jr if (!chip_data || !chip_data->max_support_points) { 1095b1d2a3ecSFranklin S Cooper Jr dev_err(&client->dev, "invalid or missing chip data\n"); 1096b1d2a3ecSFranklin S Cooper Jr return -EINVAL; 1097b1d2a3ecSFranklin S Cooper Jr } 1098b1d2a3ecSFranklin S Cooper Jr 1099b1d2a3ecSFranklin S Cooper Jr tsdata->max_support_points = chip_data->max_support_points; 1100b1d2a3ecSFranklin S Cooper Jr 11017448bfecSMylène Josserand tsdata->vcc = devm_regulator_get(&client->dev, "vcc"); 11027448bfecSMylène Josserand if (IS_ERR(tsdata->vcc)) { 11037448bfecSMylène Josserand error = PTR_ERR(tsdata->vcc); 11047448bfecSMylène Josserand if (error != -EPROBE_DEFER) 11057448bfecSMylène Josserand dev_err(&client->dev, 11067448bfecSMylène Josserand "failed to request regulator: %d\n", error); 11077448bfecSMylène Josserand return error; 11087448bfecSMylène Josserand } 11097448bfecSMylène Josserand 11107448bfecSMylène Josserand error = regulator_enable(tsdata->vcc); 11117448bfecSMylène Josserand if (error < 0) { 11127448bfecSMylène Josserand dev_err(&client->dev, "failed to enable vcc: %d\n", error); 11137448bfecSMylène Josserand return error; 11147448bfecSMylène Josserand } 11157448bfecSMylène Josserand 11167448bfecSMylène Josserand error = devm_add_action_or_reset(&client->dev, 11177448bfecSMylène Josserand edt_ft5x06_disable_regulator, 11187448bfecSMylène Josserand tsdata); 11197448bfecSMylène Josserand if (error) 11207448bfecSMylène Josserand return error; 11217448bfecSMylène Josserand 112213c23cd1SFranklin S Cooper Jr tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev, 112313c23cd1SFranklin S Cooper Jr "reset", GPIOD_OUT_HIGH); 112413c23cd1SFranklin S Cooper Jr if (IS_ERR(tsdata->reset_gpio)) { 112513c23cd1SFranklin S Cooper Jr error = PTR_ERR(tsdata->reset_gpio); 1126dac90dc2SLothar Waßmann dev_err(&client->dev, 112713c23cd1SFranklin S Cooper Jr "Failed to request GPIO reset pin, error %d\n", error); 1128dac90dc2SLothar Waßmann return error; 1129dac90dc2SLothar Waßmann } 1130dac90dc2SLothar Waßmann 113113c23cd1SFranklin S Cooper Jr tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev, 113213c23cd1SFranklin S Cooper Jr "wake", GPIOD_OUT_LOW); 113313c23cd1SFranklin S Cooper Jr if (IS_ERR(tsdata->wake_gpio)) { 113413c23cd1SFranklin S Cooper Jr error = PTR_ERR(tsdata->wake_gpio); 1135dac90dc2SLothar Waßmann dev_err(&client->dev, 113613c23cd1SFranklin S Cooper Jr "Failed to request GPIO wake pin, error %d\n", error); 1137dac90dc2SLothar Waßmann return error; 1138dac90dc2SLothar Waßmann } 113913c23cd1SFranklin S Cooper Jr 114021d1611aSMarco Felsch /* 114121d1611aSMarco Felsch * Check which sleep modes we can support. Power-off requieres the 114221d1611aSMarco Felsch * reset-pin to ensure correct power-down/power-up behaviour. Start with 114321d1611aSMarco Felsch * the EDT_PMODE_POWEROFF test since this is the deepest possible sleep 114421d1611aSMarco Felsch * mode. 114521d1611aSMarco Felsch */ 114621d1611aSMarco Felsch if (tsdata->reset_gpio) 114721d1611aSMarco Felsch tsdata->suspend_mode = EDT_PMODE_POWEROFF; 114821d1611aSMarco Felsch else if (tsdata->wake_gpio) 114921d1611aSMarco Felsch tsdata->suspend_mode = EDT_PMODE_HIBERNATE; 115021d1611aSMarco Felsch else 115121d1611aSMarco Felsch tsdata->suspend_mode = EDT_PMODE_NOT_SUPPORTED; 115221d1611aSMarco Felsch 115313c23cd1SFranklin S Cooper Jr if (tsdata->wake_gpio) { 115413c23cd1SFranklin S Cooper Jr usleep_range(5000, 6000); 115513c23cd1SFranklin S Cooper Jr gpiod_set_value_cansleep(tsdata->wake_gpio, 1); 115613c23cd1SFranklin S Cooper Jr } 115713c23cd1SFranklin S Cooper Jr 115813c23cd1SFranklin S Cooper Jr if (tsdata->reset_gpio) { 115913c23cd1SFranklin S Cooper Jr usleep_range(5000, 6000); 116013c23cd1SFranklin S Cooper Jr gpiod_set_value_cansleep(tsdata->reset_gpio, 0); 116113c23cd1SFranklin S Cooper Jr msleep(300); 1162dac90dc2SLothar Waßmann } 1163dac90dc2SLothar Waßmann 116402300bd6SLothar Waßmann input = devm_input_allocate_device(&client->dev); 116502300bd6SLothar Waßmann if (!input) { 116602300bd6SLothar Waßmann dev_err(&client->dev, "failed to allocate input device.\n"); 116702300bd6SLothar Waßmann return -ENOMEM; 116843c4d13eSSimon Budig } 116943c4d13eSSimon Budig 117043c4d13eSSimon Budig mutex_init(&tsdata->mutex); 117143c4d13eSSimon Budig tsdata->client = client; 117243c4d13eSSimon Budig tsdata->input = input; 117343c4d13eSSimon Budig tsdata->factory_mode = false; 117443c4d13eSSimon Budig 1175fd335ab0SLothar Waßmann error = edt_ft5x06_ts_identify(client, tsdata, fw_version); 117643c4d13eSSimon Budig if (error) { 117743c4d13eSSimon Budig dev_err(&client->dev, "touchscreen probe failed\n"); 117802300bd6SLothar Waßmann return error; 117943c4d13eSSimon Budig } 118043c4d13eSSimon Budig 1181e112324cSPhilipp Zabel /* 1182e112324cSPhilipp Zabel * Dummy read access. EP0700MLP1 returns bogus data on the first 1183e112324cSPhilipp Zabel * register read access and ignores writes. 1184e112324cSPhilipp Zabel */ 1185e112324cSPhilipp Zabel edt_ft5x06_ts_readwrite(tsdata->client, 2, buf, 2, buf); 1186e112324cSPhilipp Zabel 1187fd335ab0SLothar Waßmann edt_ft5x06_ts_set_regs(tsdata); 11882e23b7a9SDmitry Torokhov edt_ft5x06_ts_get_defaults(&client->dev, tsdata); 118943c4d13eSSimon Budig edt_ft5x06_ts_get_parameters(tsdata); 119043c4d13eSSimon Budig 119143c4d13eSSimon Budig dev_dbg(&client->dev, 119243c4d13eSSimon Budig "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", 119343c4d13eSSimon Budig tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); 119443c4d13eSSimon Budig 119543c4d13eSSimon Budig input->name = tsdata->name; 119643c4d13eSSimon Budig input->id.bustype = BUS_I2C; 119743c4d13eSSimon Budig input->dev.parent = &client->dev; 119843c4d13eSSimon Budig 1199169110c3SSimon Budig if (tsdata->version == EDT_M06 || 1200aed5d0eeSSimon Budig tsdata->version == EDT_M09 || 1201aed5d0eeSSimon Budig tsdata->version == EDT_M12) { 120243c4d13eSSimon Budig input_set_abs_params(input, ABS_MT_POSITION_X, 120343c4d13eSSimon Budig 0, tsdata->num_x * 64 - 1, 0, 0); 120443c4d13eSSimon Budig input_set_abs_params(input, ABS_MT_POSITION_Y, 120543c4d13eSSimon Budig 0, tsdata->num_y * 64 - 1, 0, 0); 1206169110c3SSimon Budig } else { 1207169110c3SSimon Budig /* Unknown maximum values. Specify via devicetree */ 1208169110c3SSimon Budig input_set_abs_params(input, ABS_MT_POSITION_X, 1209169110c3SSimon Budig 0, 65535, 0, 0); 1210169110c3SSimon Budig input_set_abs_params(input, ABS_MT_POSITION_Y, 1211169110c3SSimon Budig 0, 65535, 0, 0); 1212169110c3SSimon Budig } 12132c005598SMaxime Ripard 1214ad368eb2SHans de Goede touchscreen_parse_properties(input, true, &tsdata->prop); 12152c005598SMaxime Ripard 1216b1d2a3ecSFranklin S Cooper Jr error = input_mt_init_slots(input, tsdata->max_support_points, 1217b1d2a3ecSFranklin S Cooper Jr INPUT_MT_DIRECT); 121843c4d13eSSimon Budig if (error) { 121943c4d13eSSimon Budig dev_err(&client->dev, "Unable to init MT slots.\n"); 122002300bd6SLothar Waßmann return error; 122143c4d13eSSimon Budig } 122243c4d13eSSimon Budig 122343c4d13eSSimon Budig i2c_set_clientdata(client, tsdata); 122443c4d13eSSimon Budig 1225f0bef75cSDmitry Torokhov irq_flags = irq_get_trigger_type(client->irq); 1226f0bef75cSDmitry Torokhov if (irq_flags == IRQF_TRIGGER_NONE) 1227f0bef75cSDmitry Torokhov irq_flags = IRQF_TRIGGER_FALLING; 1228f0bef75cSDmitry Torokhov irq_flags |= IRQF_ONESHOT; 1229f0bef75cSDmitry Torokhov 1230f0bef75cSDmitry Torokhov error = devm_request_threaded_irq(&client->dev, client->irq, 1231f0bef75cSDmitry Torokhov NULL, edt_ft5x06_ts_isr, irq_flags, 123243c4d13eSSimon Budig client->name, tsdata); 123343c4d13eSSimon Budig if (error) { 123443c4d13eSSimon Budig dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); 123502300bd6SLothar Waßmann return error; 123643c4d13eSSimon Budig } 123743c4d13eSSimon Budig 1238e3adf559SAndi Shyti error = devm_device_add_group(&client->dev, &edt_ft5x06_attr_group); 123943c4d13eSSimon Budig if (error) 124002300bd6SLothar Waßmann return error; 124143c4d13eSSimon Budig 124243c4d13eSSimon Budig error = input_register_device(input); 1243dac90dc2SLothar Waßmann if (error) 1244e3adf559SAndi Shyti return error; 124543c4d13eSSimon Budig 124643c4d13eSSimon Budig edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); 124743c4d13eSSimon Budig 124843c4d13eSSimon Budig dev_dbg(&client->dev, 1249dac90dc2SLothar Waßmann "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", 12508b58cc36SFranklin S Cooper Jr client->irq, 12518b58cc36SFranklin S Cooper Jr tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1, 12528b58cc36SFranklin S Cooper Jr tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1); 125343c4d13eSSimon Budig 125443c4d13eSSimon Budig return 0; 125543c4d13eSSimon Budig } 125643c4d13eSSimon Budig 1257e2619cf7SBill Pemberton static int edt_ft5x06_ts_remove(struct i2c_client *client) 125843c4d13eSSimon Budig { 125943c4d13eSSimon Budig struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); 126043c4d13eSSimon Budig 126143c4d13eSSimon Budig edt_ft5x06_ts_teardown_debugfs(tsdata); 126243c4d13eSSimon Budig 126343c4d13eSSimon Budig return 0; 126443c4d13eSSimon Budig } 126543c4d13eSSimon Budig 126621d1611aSMarco Felsch static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev) 126721d1611aSMarco Felsch { 126821d1611aSMarco Felsch struct i2c_client *client = to_i2c_client(dev); 126921d1611aSMarco Felsch struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); 127021d1611aSMarco Felsch struct gpio_desc *reset_gpio = tsdata->reset_gpio; 127121d1611aSMarco Felsch int ret; 127221d1611aSMarco Felsch 127321d1611aSMarco Felsch if (device_may_wakeup(dev)) 127421d1611aSMarco Felsch return 0; 127521d1611aSMarco Felsch 127621d1611aSMarco Felsch if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED) 127721d1611aSMarco Felsch return 0; 127821d1611aSMarco Felsch 127921d1611aSMarco Felsch /* Enter hibernate mode. */ 128021d1611aSMarco Felsch ret = edt_ft5x06_register_write(tsdata, PMOD_REGISTER_OPMODE, 128121d1611aSMarco Felsch PMOD_REGISTER_HIBERNATE); 128221d1611aSMarco Felsch if (ret) 128321d1611aSMarco Felsch dev_warn(dev, "Failed to set hibernate mode\n"); 128421d1611aSMarco Felsch 128521d1611aSMarco Felsch if (tsdata->suspend_mode == EDT_PMODE_HIBERNATE) 128621d1611aSMarco Felsch return 0; 128721d1611aSMarco Felsch 128821d1611aSMarco Felsch /* 128921d1611aSMarco Felsch * Power-off according the datasheet. Cut the power may leaf the irq 129021d1611aSMarco Felsch * line in an undefined state depending on the host pull resistor 129121d1611aSMarco Felsch * settings. Disable the irq to avoid adjusting each host till the 129221d1611aSMarco Felsch * device is back in a full functional state. 129321d1611aSMarco Felsch */ 129421d1611aSMarco Felsch disable_irq(tsdata->client->irq); 129521d1611aSMarco Felsch 129621d1611aSMarco Felsch gpiod_set_value_cansleep(reset_gpio, 1); 129721d1611aSMarco Felsch usleep_range(1000, 2000); 129821d1611aSMarco Felsch 129921d1611aSMarco Felsch ret = regulator_disable(tsdata->vcc); 130021d1611aSMarco Felsch if (ret) 130121d1611aSMarco Felsch dev_warn(dev, "Failed to disable vcc\n"); 130221d1611aSMarco Felsch 130321d1611aSMarco Felsch return 0; 130421d1611aSMarco Felsch } 130521d1611aSMarco Felsch 130621d1611aSMarco Felsch static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev) 130721d1611aSMarco Felsch { 130821d1611aSMarco Felsch struct i2c_client *client = to_i2c_client(dev); 130921d1611aSMarco Felsch struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); 131021d1611aSMarco Felsch int ret = 0; 131121d1611aSMarco Felsch 131221d1611aSMarco Felsch if (device_may_wakeup(dev)) 131321d1611aSMarco Felsch return 0; 131421d1611aSMarco Felsch 131521d1611aSMarco Felsch if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED) 131621d1611aSMarco Felsch return 0; 131721d1611aSMarco Felsch 131821d1611aSMarco Felsch if (tsdata->suspend_mode == EDT_PMODE_POWEROFF) { 131921d1611aSMarco Felsch struct gpio_desc *reset_gpio = tsdata->reset_gpio; 132021d1611aSMarco Felsch 132121d1611aSMarco Felsch /* 132221d1611aSMarco Felsch * We can't check if the regulator is a dummy or a real 132321d1611aSMarco Felsch * regulator. So we need to specify the 5ms reset time (T_rst) 132421d1611aSMarco Felsch * here instead of the 100us T_rtp time. We also need to wait 132521d1611aSMarco Felsch * 300ms in case it was a real supply and the power was cutted 132621d1611aSMarco Felsch * of. Toggle the reset pin is also a way to exit the hibernate 132721d1611aSMarco Felsch * mode. 132821d1611aSMarco Felsch */ 132921d1611aSMarco Felsch gpiod_set_value_cansleep(reset_gpio, 1); 133021d1611aSMarco Felsch usleep_range(5000, 6000); 133121d1611aSMarco Felsch 133221d1611aSMarco Felsch ret = regulator_enable(tsdata->vcc); 133321d1611aSMarco Felsch if (ret) { 133421d1611aSMarco Felsch dev_err(dev, "Failed to enable vcc\n"); 133521d1611aSMarco Felsch return ret; 133621d1611aSMarco Felsch } 133721d1611aSMarco Felsch 133821d1611aSMarco Felsch usleep_range(1000, 2000); 133921d1611aSMarco Felsch gpiod_set_value_cansleep(reset_gpio, 0); 134021d1611aSMarco Felsch msleep(300); 134121d1611aSMarco Felsch 134221d1611aSMarco Felsch edt_ft5x06_restore_reg_parameters(tsdata); 134321d1611aSMarco Felsch enable_irq(tsdata->client->irq); 134421d1611aSMarco Felsch 134521d1611aSMarco Felsch if (tsdata->factory_mode) 134621d1611aSMarco Felsch ret = edt_ft5x06_factory_mode(tsdata); 134721d1611aSMarco Felsch } else { 134821d1611aSMarco Felsch struct gpio_desc *wake_gpio = tsdata->wake_gpio; 134921d1611aSMarco Felsch 135021d1611aSMarco Felsch gpiod_set_value_cansleep(wake_gpio, 0); 135121d1611aSMarco Felsch usleep_range(5000, 6000); 135221d1611aSMarco Felsch gpiod_set_value_cansleep(wake_gpio, 1); 135321d1611aSMarco Felsch } 135421d1611aSMarco Felsch 135521d1611aSMarco Felsch 135621d1611aSMarco Felsch return ret; 135721d1611aSMarco Felsch } 135821d1611aSMarco Felsch 135921d1611aSMarco Felsch static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, 136021d1611aSMarco Felsch edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); 136121d1611aSMarco Felsch 1362b1d2a3ecSFranklin S Cooper Jr static const struct edt_i2c_chip_data edt_ft5x06_data = { 1363b1d2a3ecSFranklin S Cooper Jr .max_support_points = 5, 1364b1d2a3ecSFranklin S Cooper Jr }; 1365b1d2a3ecSFranklin S Cooper Jr 1366af33e0adSFranklin S Cooper Jr static const struct edt_i2c_chip_data edt_ft5506_data = { 1367af33e0adSFranklin S Cooper Jr .max_support_points = 10, 1368af33e0adSFranklin S Cooper Jr }; 1369af33e0adSFranklin S Cooper Jr 1370d1871654SHans de Goede static const struct edt_i2c_chip_data edt_ft6236_data = { 1371d1871654SHans de Goede .max_support_points = 2, 1372d1871654SHans de Goede }; 1373d1871654SHans de Goede 137443c4d13eSSimon Budig static const struct i2c_device_id edt_ft5x06_ts_id[] = { 1375b1d2a3ecSFranklin S Cooper Jr { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, 1376af33e0adSFranklin S Cooper Jr { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, 1377a2f39dacSMarco Felsch { .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data }, 1378d1871654SHans de Goede /* Note no edt- prefix for compatibility with the ft6236.c driver */ 1379d1871654SHans de Goede { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, 13801730d814SLothar Waßmann { /* sentinel */ } 138143c4d13eSSimon Budig }; 138243c4d13eSSimon Budig MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); 138343c4d13eSSimon Budig 1384dac90dc2SLothar Waßmann static const struct of_device_id edt_ft5x06_of_match[] = { 1385b1d2a3ecSFranklin S Cooper Jr { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data }, 1386b1d2a3ecSFranklin S Cooper Jr { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, 1387b1d2a3ecSFranklin S Cooper Jr { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, 1388af33e0adSFranklin S Cooper Jr { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, 1389a2f39dacSMarco Felsch { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data }, 1390d1871654SHans de Goede /* Note focaltech vendor prefix for compatibility with ft6236.c */ 1391d1871654SHans de Goede { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, 1392dac90dc2SLothar Waßmann { /* sentinel */ } 1393dac90dc2SLothar Waßmann }; 1394dac90dc2SLothar Waßmann MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); 1395dac90dc2SLothar Waßmann 139643c4d13eSSimon Budig static struct i2c_driver edt_ft5x06_ts_driver = { 139743c4d13eSSimon Budig .driver = { 139843c4d13eSSimon Budig .name = "edt_ft5x06", 1399fc226eb2SAndy Shevchenko .of_match_table = edt_ft5x06_of_match, 140021d1611aSMarco Felsch .pm = &edt_ft5x06_ts_pm_ops, 14010f58daaaSAhmad Fatoum .probe_type = PROBE_PREFER_ASYNCHRONOUS, 140243c4d13eSSimon Budig }, 140343c4d13eSSimon Budig .id_table = edt_ft5x06_ts_id, 140443c4d13eSSimon Budig .probe = edt_ft5x06_ts_probe, 14051cb0aa88SBill Pemberton .remove = edt_ft5x06_ts_remove, 140643c4d13eSSimon Budig }; 140743c4d13eSSimon Budig 140843c4d13eSSimon Budig module_i2c_driver(edt_ft5x06_ts_driver); 140943c4d13eSSimon Budig 141043c4d13eSSimon Budig MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>"); 141143c4d13eSSimon Budig MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); 14126d3a41abSAndy Shevchenko MODULE_LICENSE("GPL v2"); 1413