diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-01-20 14:01:31 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-01-20 14:01:31 -0300 |
commit | b4b7ff4b08e691656c9d77c758fc355833128ac0 (patch) | |
tree | 82fcb00e6b918026dc9f2d1f05ed8eee83874cc0 /drivers/input/touchscreen | |
parent | 35acfa0fc609f2a2cd95cef4a6a9c3a5c38f1778 (diff) |
Linux-libre 4.4-gnupck-4.4-gnu
Diffstat (limited to 'drivers/input/touchscreen')
28 files changed, 3750 insertions, 1072 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index deb14c12a..53a97b379 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -295,6 +295,29 @@ config TOUCHSCREEN_EGALAX To compile this driver as a module, choose M here: the module will be called egalax_ts. +config TOUCHSCREEN_EGALAX_SERIAL + tristate "EETI eGalax serial touchscreen" + select SERIO + help + Say Y here to enable support for serial connected EETI + eGalax touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts_serial. + +config TOUCHSCREEN_FT6236 + tristate "FT6236 I2C touchscreen" + depends on I2C + depends on GPIOLIB || COMPILE_TEST + help + Say Y here to enable support for the I2C connected FT6x06 and + FT6x36 family of capacitive touchscreen drivers. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ft6236. + config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO @@ -311,6 +334,7 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" depends on I2C + depends on GPIOLIB help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your @@ -914,6 +938,22 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TS4800 + tristate "TS-4800 touchscreen" + depends on HAS_IOMEM && OF + select MFD_SYSCON + select INPUT_POLLDEV + help + Say Y here if you have a touchscreen on a TS-4800 board. + + On TS-4800, the touchscreen is not handled directly by Linux but by + a companion FPGA. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ts4800_ts. + config TOUCHSCREEN_TSC_SERIO tristate "TSC-10/25/40 serial touchscreen support" select SERIO @@ -926,10 +966,27 @@ config TOUCHSCREEN_TSC_SERIO To compile this driver as a module, choose M here: the module will be called tsc40. +config TOUCHSCREEN_TSC200X_CORE + tristate + +config TOUCHSCREEN_TSC2004 + tristate "TSC2004 based touchscreens" + depends on I2C + select REGMAP_I2C + select TOUCHSCREEN_TSC200X_CORE + help + Say Y here if you have a TSC2004 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2004. + config TOUCHSCREEN_TSC2005 tristate "TSC2005 based touchscreens" depends on SPI_MASTER select REGMAP_SPI + select TOUCHSCREEN_TSC200X_CORE help Say Y here if you have a TSC2005 based touchscreen. @@ -1065,4 +1122,15 @@ config TOUCHSCREEN_COLIBRI_VF50 To compile this driver as a module, choose M here: the module will be called colibri_vf50_ts. +config TOUCHSCREEN_ROHM_BU21023 + tristate "ROHM BU21023/24 Dual touch support resistive touchscreens" + depends on I2C + help + Say Y here if you have a touchscreen using ROHM BU21023/24. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bu21023_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 1b79cc097..968ff12e3 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -35,6 +35,8 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o +obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o @@ -67,7 +69,10 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o +obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o +obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o @@ -87,3 +92,4 @@ obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o +obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index da4e5bb5e..9c250ae78 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -843,7 +843,6 @@ static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume); static struct spi_driver ad7877_driver = { .driver = { .name = "ad7877", - .owner = THIS_MODULE, .pm = &ad7877_pm, }, .probe = ad7877_probe, diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 1a7b11435..48033c268 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -149,7 +149,6 @@ static int ad7879_spi_remove(struct spi_device *spi) static struct spi_driver ad7879_spi_driver = { .driver = { .name = "ad7879", - .owner = THIS_MODULE, .pm = &ad7879_pm_ops, }, .probe = ad7879_spi_probe, diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 04edc8f71..a61b2153a 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -529,10 +529,8 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) ts->hwmon = hwmon_device_register_with_groups(&spi->dev, spi->modalias, ts, ads7846_attr_groups); - if (IS_ERR(ts->hwmon)) - return PTR_ERR(ts->hwmon); - return 0; + return PTR_ERR_OR_ZERO(ts->hwmon); } static void ads784x_hwmon_unregister(struct spi_device *spi, @@ -1500,7 +1498,6 @@ static int ads7846_remove(struct spi_device *spi) static struct spi_driver ads7846_driver = { .driver = { .name = "ads7846", - .owner = THIS_MODULE, .pm = &ads7846_pm, .of_match_table = of_match_ptr(ads7846_dt_ids), }, diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7ca336ea9..726a83e11 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2486,6 +2486,31 @@ static struct mxt_acpi_platform_data samus_platform_data[] = { { } }; +static unsigned int chromebook_tp_buttons[] = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT +}; + +static struct mxt_acpi_platform_data chromebook_platform_data[] = { + { + /* Touchpad */ + .hid = "ATML0000", + .pdata = { + .t19_num_keys = ARRAY_SIZE(chromebook_tp_buttons), + .t19_keymap = chromebook_tp_buttons, + }, + }, + { + /* Touchscreen */ + .hid = "ATML0001", + }, + { } +}; + static const struct dmi_system_id mxt_dmi_table[] = { { /* 2015 Google Pixel */ @@ -2496,6 +2521,14 @@ static const struct dmi_system_id mxt_dmi_table[] = { }, .driver_data = samus_platform_data, }, + { + /* Other Google Chromebooks */ + .ident = "Chromebook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + }, + .driver_data = chromebook_platform_data, + }, { } }; @@ -2700,6 +2733,7 @@ static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, { "atmel_mxt_tp", 0 }, + { "maxtouch", 0 }, { "mXT224", 0 }, { } }; diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 38c06f754..6592fc5d4 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -399,13 +399,8 @@ static int auo_pixcir_stop(struct auo_pixcir_ts *ts) static int auo_pixcir_input_open(struct input_dev *dev) { struct auo_pixcir_ts *ts = input_get_drvdata(dev); - int ret; - - ret = auo_pixcir_start(ts); - if (ret) - return ret; - return 0; + return auo_pixcir_start(ts); } static void auo_pixcir_input_close(struct input_dev *dev) diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c index a9f95c7d3..564e49002 100644 --- a/drivers/input/touchscreen/cyttsp4_i2c.c +++ b/drivers/input/touchscreen/cyttsp4_i2c.c @@ -50,10 +50,7 @@ static int cyttsp4_i2c_probe(struct i2c_client *client, ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq, CYTTSP4_I2C_DATA_SIZE); - if (IS_ERR(ts)) - return PTR_ERR(ts); - - return 0; + return PTR_ERR_OR_ZERO(ts); } static int cyttsp4_i2c_remove(struct i2c_client *client) diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c index b19434ceb..ec5f7c74f 100644 --- a/drivers/input/touchscreen/cyttsp4_spi.c +++ b/drivers/input/touchscreen/cyttsp4_spi.c @@ -185,7 +185,6 @@ static int cyttsp4_spi_remove(struct spi_device *spi) static struct spi_driver cyttsp4_spi_driver = { .driver = { .name = CYTTSP4_SPI_NAME, - .owner = THIS_MODULE, .pm = &cyttsp4_pm_ops, }, .probe = cyttsp4_spi_probe, diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index 4728bcb19..bbeeb2488 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -182,7 +182,6 @@ static int cyttsp_spi_remove(struct spi_device *spi) static struct spi_driver cyttsp_spi_driver = { .driver = { .name = CY_SPI_NAME, - .owner = THIS_MODULE, .pm = &cyttsp_pm_ops, }, .probe = cyttsp_spi_probe, diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 48de1e8b3..0b0f8c17f 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/ratelimit.h> +#include <linux/irq.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/i2c.h> @@ -34,13 +35,10 @@ #include <linux/delay.h> #include <linux/debugfs.h> #include <linux/slab.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> -#include <linux/input/edt-ft5x06.h> - -#define MAX_SUPPORT_POINTS 5 +#include <linux/of_device.h> #define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_REPORT_RATE 0x08 @@ -91,9 +89,8 @@ struct edt_ft5x06_ts_data { u16 num_x; u16 num_y; - int reset_pin; - int irq_pin; - int wake_pin; + struct gpio_desc *reset_gpio; + struct gpio_desc *wake_gpio; #if defined(CONFIG_DEBUG_FS) struct dentry *debug_dir; @@ -107,6 +104,7 @@ struct edt_ft5x06_ts_data { int gain; int offset; int report_rate; + int max_support_points; char name[EDT_NAME_LEN]; @@ -114,6 +112,10 @@ struct edt_ft5x06_ts_data { enum edt_ver version; }; +struct edt_i2c_chip_data { + int max_support_points; +}; + static int edt_ft5x06_ts_readwrite(struct i2c_client *client, u16 wr_len, u8 *wr_buf, u16 rd_len, u8 *rd_buf) @@ -170,9 +172,9 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) struct edt_ft5x06_ts_data *tsdata = dev_id; struct device *dev = &tsdata->client->dev; u8 cmd; - u8 rdbuf[29]; + u8 rdbuf[63]; int i, type, x, y, id; - int offset, tplen, datalen; + int offset, tplen, datalen, crclen; int error; switch (tsdata->version) { @@ -180,14 +182,14 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) cmd = 0xf9; /* tell the controller to send touch data */ offset = 5; /* where the actual touch data starts */ tplen = 4; /* data comes in so called frames */ - datalen = 26; /* how much bytes to listen for */ + crclen = 1; /* length of the crc data */ break; case M09: - cmd = 0x02; - offset = 1; + cmd = 0x0; + offset = 3; tplen = 6; - datalen = 29; + crclen = 0; break; default: @@ -195,6 +197,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) } memset(rdbuf, 0, sizeof(rdbuf)); + datalen = tplen * tsdata->max_support_points + offset + crclen; error = edt_ft5x06_ts_readwrite(tsdata->client, sizeof(cmd), &cmd, @@ -219,7 +222,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) goto out; } - for (i = 0; i < MAX_SUPPORT_POINTS; i++) { + for (i = 0; i < tsdata->max_support_points; i++) { u8 *buf = &rdbuf[i * tplen + offset]; bool down; @@ -752,45 +755,6 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ -static int edt_ft5x06_ts_reset(struct i2c_client *client, - struct edt_ft5x06_ts_data *tsdata) -{ - int error; - - if (gpio_is_valid(tsdata->wake_pin)) { - error = devm_gpio_request_one(&client->dev, - tsdata->wake_pin, GPIOF_OUT_INIT_LOW, - "edt-ft5x06 wake"); - if (error) { - dev_err(&client->dev, - "Failed to request GPIO %d as wake pin, error %d\n", - tsdata->wake_pin, error); - return error; - } - - msleep(5); - gpio_set_value(tsdata->wake_pin, 1); - } - if (gpio_is_valid(tsdata->reset_pin)) { - /* this pulls reset down, enabling the low active reset */ - error = devm_gpio_request_one(&client->dev, - tsdata->reset_pin, GPIOF_OUT_INIT_LOW, - "edt-ft5x06 reset"); - if (error) { - dev_err(&client->dev, - "Failed to request GPIO %d as reset pin, error %d\n", - tsdata->reset_pin, error); - return error; - } - - msleep(5); - gpio_set_value(tsdata->reset_pin, 1); - msleep(300); - } - - return 0; -} - static int edt_ft5x06_ts_identify(struct i2c_client *client, struct edt_ft5x06_ts_data *tsdata, char *fw_version) @@ -850,44 +814,24 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, 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); \ -} 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) +static void edt_ft5x06_ts_get_defaults(struct device *dev, + struct edt_ft5x06_ts_data *tsdata) { struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + u32 val; + int error; - EDT_GET_PROP(threshold, reg_addr->reg_threshold); - EDT_GET_PROP(gain, reg_addr->reg_gain); - EDT_GET_PROP(offset, reg_addr->reg_offset); -} + error = device_property_read_u32(dev, "threshold", &val); + if (!error) + reg_addr->reg_threshold = val; -static void -edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, - const struct edt_ft5x06_platform_data *pdata) -{ - struct edt_reg_addr *reg_addr = &tsdata->reg_addr; - - if (!pdata->use_parameters) - return; + error = device_property_read_u32(dev, "gain", &val); + if (!error) + reg_addr->reg_gain = val; - /* pick up defaults from the platform data */ - EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold); - EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain); - EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset); - if (reg_addr->reg_report_rate != NO_REGISTER) - EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate); + error = device_property_read_u32(dev, "offset", &val); + if (!error) + reg_addr->reg_offset = val; } static void @@ -931,37 +875,13 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) } } -#ifdef CONFIG_OF -static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, - struct edt_ft5x06_ts_data *tsdata) -{ - struct device_node *np = dev->of_node; - - /* - * irq_pin is not needed for DT setup. - * irq is associated via 'interrupts' property in DT - */ - tsdata->irq_pin = -EINVAL; - tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0); - tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0); - - return 0; -} -#else -static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, - struct edt_ft5x06_ts_data *tsdata) -{ - return -ENODEV; -} -#endif - static int edt_ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct edt_ft5x06_platform_data *pdata = - dev_get_platdata(&client->dev); + const struct edt_i2c_chip_data *chip_data; struct edt_ft5x06_ts_data *tsdata; struct input_dev *input; + unsigned long irq_flags; int error; char fw_version[EDT_NAME_LEN]; @@ -973,32 +893,43 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return -ENOMEM; } - if (!pdata) { - error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata); - if (error) { - dev_err(&client->dev, - "DT probe failed and no platform data present\n"); - return error; - } - } else { - tsdata->reset_pin = pdata->reset_pin; - tsdata->irq_pin = pdata->irq_pin; - tsdata->wake_pin = -EINVAL; + chip_data = of_device_get_match_data(&client->dev); + if (!chip_data) + chip_data = (const struct edt_i2c_chip_data *)id->driver_data; + if (!chip_data || !chip_data->max_support_points) { + dev_err(&client->dev, "invalid or missing chip data\n"); + return -EINVAL; } - error = edt_ft5x06_ts_reset(client, tsdata); - if (error) + tsdata->max_support_points = chip_data->max_support_points; + + tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->reset_gpio)) { + error = PTR_ERR(tsdata->reset_gpio); + dev_err(&client->dev, + "Failed to request GPIO reset pin, error %d\n", error); + return error; + } + + tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev, + "wake", GPIOD_OUT_LOW); + if (IS_ERR(tsdata->wake_gpio)) { + error = PTR_ERR(tsdata->wake_gpio); + dev_err(&client->dev, + "Failed to request GPIO wake pin, error %d\n", error); return error; + } - if (gpio_is_valid(tsdata->irq_pin)) { - error = devm_gpio_request_one(&client->dev, tsdata->irq_pin, - GPIOF_IN, "edt-ft5x06 irq"); - if (error) { - dev_err(&client->dev, - "Failed to request GPIO %d, error %d\n", - tsdata->irq_pin, error); - return error; - } + if (tsdata->wake_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(tsdata->wake_gpio, 1); + } + + if (tsdata->reset_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(tsdata->reset_gpio, 0); + msleep(300); } input = devm_input_allocate_device(&client->dev); @@ -1019,12 +950,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, } 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_defaults(&client->dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); dev_dbg(&client->dev, @@ -1040,10 +966,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y * 64 - 1, 0, 0); - if (!pdata) - touchscreen_parse_properties(input, true); + touchscreen_parse_properties(input, true); - error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT); + error = input_mt_init_slots(input, tsdata->max_support_points, + INPUT_MT_DIRECT); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); return error; @@ -1052,9 +978,13 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input_set_drvdata(input, tsdata); i2c_set_clientdata(client, tsdata); - error = devm_request_threaded_irq(&client->dev, client->irq, NULL, - edt_ft5x06_ts_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + irq_flags = irq_get_trigger_type(client->irq); + if (irq_flags == IRQF_TRIGGER_NONE) + irq_flags = IRQF_TRIGGER_FALLING; + irq_flags |= IRQF_ONESHOT; + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, edt_ft5x06_ts_isr, irq_flags, client->name, tsdata); if (error) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); @@ -1074,7 +1004,9 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, dev_dbg(&client->dev, "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", - client->irq, tsdata->wake_pin, tsdata->reset_pin); + client->irq, + tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1, + tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1); return 0; @@ -1116,17 +1048,27 @@ static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); +static const struct edt_i2c_chip_data edt_ft5x06_data = { + .max_support_points = 5, +}; + +static const struct edt_i2c_chip_data edt_ft5506_data = { + .max_support_points = 10, +}; + static const struct i2c_device_id edt_ft5x06_ts_id[] = { - { "edt-ft5x06", 0, }, + { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, + { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); #ifdef CONFIG_OF static const struct of_device_id edt_ft5x06_of_match[] = { - { .compatible = "edt,edt-ft5206", }, - { .compatible = "edt,edt-ft5306", }, - { .compatible = "edt,edt-ft5406", }, + { .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); diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c new file mode 100644 index 000000000..657bbae60 --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts_serial.c @@ -0,0 +1,194 @@ +/* + * EETI Egalax serial touchscreen driver + * + * Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu> + * + * based on the + * + * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett) + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> + +#define DRIVER_DESC "EETI Egalax serial touchscreen driver" + +/* + * Definitions & global arrays. + */ + +#define EGALAX_FORMAT_MAX_LENGTH 6 +#define EGALAX_FORMAT_START_BIT BIT(7) +#define EGALAX_FORMAT_PRESSURE_BIT BIT(6) +#define EGALAX_FORMAT_TOUCH_BIT BIT(0) +#define EGALAX_FORMAT_RESOLUTION_MASK 0x06 + +#define EGALAX_MIN_XC 0 +#define EGALAX_MAX_XC 0x4000 +#define EGALAX_MIN_YC 0 +#define EGALAX_MAX_YC 0x4000 + +/* + * Per-touchscreen data. + */ +struct egalax { + struct input_dev *input; + struct serio *serio; + int idx; + u8 data[EGALAX_FORMAT_MAX_LENGTH]; + char phys[32]; +}; + +static void egalax_process_data(struct egalax *egalax) +{ + struct input_dev *dev = egalax->input; + u8 *data = egalax->data; + u16 x, y; + u8 shift; + u8 mask; + + shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1); + mask = 0xff >> (shift + 1); + + x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift; + y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift; + + input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_sync(dev); +} + +static irqreturn_t egalax_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct egalax *egalax = serio_get_drvdata(serio); + int pkt_len; + + egalax->data[egalax->idx++] = data; + + if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) { + pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5; + if (pkt_len == egalax->idx) { + egalax_process_data(egalax); + egalax->idx = 0; + } + } else { + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + egalax->data[0]); + egalax->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * egalax_connect() is the routine that is called when someone adds a + * new serio device that supports egalax protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ +static int egalax_connect(struct serio *serio, struct serio_driver *drv) +{ + struct egalax *egalax; + struct input_dev *input_dev; + int error; + + egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!egalax || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + egalax->serio = serio; + egalax->input = input_dev; + snprintf(egalax->phys, sizeof(egalax->phys), + "%s/input0", serio->phys); + + input_dev->name = "EETI eGalaxTouch Serial TouchScreen"; + input_dev->phys = egalax->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_EGALAX; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0); + + serio_set_drvdata(serio, egalax); + + error = serio_open(serio, drv); + if (error) + goto err_reset_drvdata; + + error = input_register_device(input_dev); + if (error) + goto err_close_serio; + + return 0; + +err_close_serio: + serio_close(serio); +err_reset_drvdata: + serio_set_drvdata(serio, NULL); +err_free_mem: + input_free_device(input_dev); + kfree(egalax); + return error; +} + +static void egalax_disconnect(struct serio *serio) +{ + struct egalax *egalax = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(egalax->input); + kfree(egalax); +} + +/* + * The serio driver structure. + */ + +static const struct serio_device_id egalax_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_EGALAX, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, egalax_serio_ids); + +static struct serio_driver egalax_drv = { + .driver = { + .name = "egalax", + }, + .description = DRIVER_DESC, + .id_table = egalax_serio_ids, + .interrupt = egalax_interrupt, + .connect = egalax_connect, + .disconnect = egalax_disconnect, +}; +module_serio_driver(egalax_drv); + +MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 64b816d79..b1291f56e 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -1316,7 +1316,13 @@ static int __maybe_unused elants_i2c_suspend(struct device *dev) disable_irq(client->irq); - if (device_may_wakeup(dev) || ts->keep_power_in_suspend) { + if (device_may_wakeup(dev)) { + /* + * The device will automatically enter idle mode + * that has reduced power consumption. + */ + ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0); + } else if (ts->keep_power_in_suspend) { for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { error = elants_i2c_send(client, set_sleep_cmd, sizeof(set_sleep_cmd)); @@ -1326,10 +1332,6 @@ static int __maybe_unused elants_i2c_suspend(struct device *dev) dev_err(&client->dev, "suspend command failed: %d\n", error); } - - if (device_may_wakeup(dev)) - ts->wake_irq_enabled = - (enable_irq_wake(client->irq) == 0); } else { elants_i2c_power_off(ts); } @@ -1345,10 +1347,11 @@ static int __maybe_unused elants_i2c_resume(struct device *dev) int retry_cnt; int error; - if (device_may_wakeup(dev) && ts->wake_irq_enabled) - disable_irq_wake(client->irq); - - if (ts->keep_power_in_suspend) { + if (device_may_wakeup(dev)) { + if (ts->wake_irq_enabled) + disable_irq_wake(client->irq); + elants_i2c_sw_reset(client); + } else if (ts->keep_power_in_suspend) { for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { error = elants_i2c_send(client, set_active_cmd, sizeof(set_active_cmd)); diff --git a/drivers/input/touchscreen/ft6236.c b/drivers/input/touchscreen/ft6236.c new file mode 100644 index 000000000..d240d2e21 --- /dev/null +++ b/drivers/input/touchscreen/ft6236.c @@ -0,0 +1,326 @@ +/* + * FocalTech FT6236 TouchScreen driver. + * + * Copyright (c) 2010 Focal tech Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/property.h> + +#define FT6236_MAX_TOUCH_POINTS 2 + +#define FT6236_REG_TH_GROUP 0x80 +#define FT6236_REG_PERIODACTIVE 0x88 +#define FT6236_REG_LIB_VER_H 0xa1 +#define FT6236_REG_LIB_VER_L 0xa2 +#define FT6236_REG_CIPHER 0xa3 +#define FT6236_REG_FIRMID 0xa6 +#define FT6236_REG_FOCALTECH_ID 0xa8 +#define FT6236_REG_RELEASE_CODE_ID 0xaf + +#define FT6236_EVENT_PRESS_DOWN 0 +#define FT6236_EVENT_LIFT_UP 1 +#define FT6236_EVENT_CONTACT 2 +#define FT6236_EVENT_NO_EVENT 3 + +struct ft6236_data { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *reset_gpio; + u32 max_x; + u32 max_y; + bool invert_x; + bool invert_y; + bool swap_xy; +}; + +/* + * This struct is a touchpoint as stored in hardware. Note that the id, + * as well as the event, are stored in the upper nybble of the hi byte. + */ +struct ft6236_touchpoint { + union { + u8 xhi; + u8 event; + }; + u8 xlo; + union { + u8 yhi; + u8 id; + }; + u8 ylo; + u8 weight; + u8 misc; +} __packed; + +/* This packet represents the register map as read from offset 0 */ +struct ft6236_packet { + u8 dev_mode; + u8 gest_id; + u8 touches; + struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS]; +} __packed; + +static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data) +{ + int error; + + error = i2c_smbus_read_i2c_block_data(client, reg, len, data); + if (error < 0) + return error; + + if (error != len) + return -EIO; + + return 0; +} + +static irqreturn_t ft6236_interrupt(int irq, void *dev_id) +{ + struct ft6236_data *ft6236 = dev_id; + struct device *dev = &ft6236->client->dev; + struct input_dev *input = ft6236->input; + struct ft6236_packet buf; + u8 touches; + int i, error; + + error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf); + if (error) { + dev_err(dev, "read touchdata failed %d\n", error); + return IRQ_HANDLED; + } + + touches = buf.touches & 0xf; + if (touches > FT6236_MAX_TOUCH_POINTS) { + dev_dbg(dev, + "%d touch points reported, only %d are supported\n", + touches, FT6236_MAX_TOUCH_POINTS); + touches = FT6236_MAX_TOUCH_POINTS; + } + + for (i = 0; i < touches; i++) { + struct ft6236_touchpoint *point = &buf.points[i]; + u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo; + u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo; + u8 event = point->event >> 6; + u8 id = point->id >> 4; + bool act = (event == FT6236_EVENT_PRESS_DOWN || + event == FT6236_EVENT_CONTACT); + + input_mt_slot(input, id); + input_mt_report_slot_state(input, MT_TOOL_FINGER, act); + if (!act) + continue; + + if (ft6236->invert_x) + x = ft6236->max_x - x; + + if (ft6236->invert_y) + y = ft6236->max_y - y; + + if (ft6236->swap_xy) { + input_report_abs(input, ABS_MT_POSITION_X, y); + input_report_abs(input, ABS_MT_POSITION_Y, x); + } else { + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_sync_frame(input); + input_sync(input); + + return IRQ_HANDLED; +} + +static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg) +{ + struct i2c_client *client = ft6236->client; + u8 val = 0; + int error; + + error = ft6236_read(client, reg, 1, &val); + if (error) + dev_dbg(&client->dev, + "error reading register 0x%02x: %d\n", reg, error); + + return val; +} + +static void ft6236_debug_info(struct ft6236_data *ft6236) +{ + struct device *dev = &ft6236->client->dev; + + dev_dbg(dev, "Touch threshold is %d\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4); + dev_dbg(dev, "Report rate is %dHz\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10); + dev_dbg(dev, "Firmware library version 0x%02x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H), + ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L)); + dev_dbg(dev, "Firmware version 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID)); + dev_dbg(dev, "Chip vendor ID 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER)); + dev_dbg(dev, "CTPM vendor ID 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID)); + dev_dbg(dev, "Release code version 0x%02x\n", + ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID)); +} + +static void ft6236_reset(struct ft6236_data *ft6236) +{ + if (!ft6236->reset_gpio) + return; + + gpiod_set_value_cansleep(ft6236->reset_gpio, 1); + usleep_range(5000, 20000); + gpiod_set_value_cansleep(ft6236->reset_gpio, 0); + msleep(300); +} + +static int ft6236_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ft6236_data *ft6236; + struct input_dev *input; + u32 fuzz_x = 0, fuzz_y = 0; + u8 val; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENXIO; + + if (!client->irq) { + dev_err(dev, "irq is missing\n"); + return -EINVAL; + } + + ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL); + if (!ft6236) + return -ENOMEM; + + ft6236->client = client; + ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ft6236->reset_gpio)) { + error = PTR_ERR(ft6236->reset_gpio); + if (error != -EPROBE_DEFER) + dev_err(dev, "error getting reset gpio: %d\n", error); + return error; + } + + ft6236_reset(ft6236); + + /* verify that the controller is present */ + error = ft6236_read(client, 0x00, 1, &val); + if (error) { + dev_err(dev, "failed to read from controller: %d\n", error); + return error; + } + + ft6236_debug_info(ft6236); + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + ft6236->input = input; + input->name = client->name; + input->id.bustype = BUS_I2C; + + if (device_property_read_u32(dev, "touchscreen-size-x", + &ft6236->max_x) || + device_property_read_u32(dev, "touchscreen-size-y", + &ft6236->max_y)) { + dev_err(dev, "touchscreen-size-x and/or -y missing\n"); + return -EINVAL; + } + + device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x); + device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y); + ft6236->invert_x = device_property_read_bool(dev, + "touchscreen-inverted-x"); + ft6236->invert_y = device_property_read_bool(dev, + "touchscreen-inverted-y"); + ft6236->swap_xy = device_property_read_bool(dev, + "touchscreen-swapped-x-y"); + + if (ft6236->swap_xy) { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + ft6236->max_y, fuzz_y, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + ft6236->max_x, fuzz_x, 0); + } else { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + ft6236->max_x, fuzz_x, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + ft6236->max_y, fuzz_y, 0); + } + + error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + error = devm_request_threaded_irq(dev, client->irq, NULL, + ft6236_interrupt, IRQF_ONESHOT, + client->name, ft6236); + if (error) { + dev_err(dev, "request irq %d failed: %d\n", client->irq, error); + return error; + } + + error = input_register_device(input); + if (error) { + dev_err(dev, "failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ft6236_of_match[] = { + { .compatible = "focaltech,ft6236", }, + { } +}; +MODULE_DEVICE_TABLE(of, ft6236_of_match); +#endif + +static const struct i2c_device_id ft6236_id[] = { + { "ft6236", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ft6236_id); + +static struct i2c_driver ft6236_driver = { + .driver = { + .name = "ft6236", + .of_match_table = of_match_ptr(ft6236_of_match), + }, + .probe = ft6236_probe, + .id_table = ft6236_id, +}; +module_i2c_driver(ft6236_driver); + +MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); +MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>"); +MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 4d113c9e4..240b16f3e 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -2,6 +2,7 @@ * Driver for Goodix Touchscreens * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2015 K. Merker <merker@debian.org> * * This code is based on gt9xx.c authored by andrew@goodix.com: * @@ -16,6 +17,8 @@ #include <linux/kernel.h> #include <linux/dmi.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> @@ -33,11 +36,24 @@ struct goodix_ts_data { struct input_dev *input_dev; int abs_x_max; int abs_y_max; + bool swapped_x_y; + bool inverted_x; + bool inverted_y; unsigned int max_touch_num; unsigned int int_trigger_type; - bool rotated_screen; + int cfg_len; + struct gpio_desc *gpiod_int; + struct gpio_desc *gpiod_rst; + u16 id; + u16 version; + const char *cfg_name; + struct completion firmware_loading_complete; + unsigned long irq_flags; }; +#define GOODIX_GPIO_INT_NAME "irq" +#define GOODIX_GPIO_RST_NAME "reset" + #define GOODIX_MAX_HEIGHT 4096 #define GOODIX_MAX_WIDTH 4096 #define GOODIX_INT_TRIGGER 1 @@ -45,8 +61,13 @@ struct goodix_ts_data { #define GOODIX_MAX_CONTACTS 10 #define GOODIX_CONFIG_MAX_LENGTH 240 +#define GOODIX_CONFIG_911_LENGTH 186 +#define GOODIX_CONFIG_967_LENGTH 228 /* Register defines */ +#define GOODIX_REG_COMMAND 0x8040 +#define GOODIX_CMD_SCREEN_OFF 0x05 + #define GOODIX_READ_COOR_ADDR 0x814E #define GOODIX_REG_CONFIG_DATA 0x8047 #define GOODIX_REG_ID 0x8140 @@ -115,6 +136,63 @@ static int goodix_i2c_read(struct i2c_client *client, return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); } +/** + * goodix_i2c_write - write data to a register of the i2c slave device. + * + * @client: i2c device. + * @reg: the register to write to. + * @buf: raw data buffer to write. + * @len: length of the buffer to write + */ +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, + unsigned len) +{ + u8 *addr_buf; + struct i2c_msg msg; + int ret; + + addr_buf = kmalloc(len + 2, GFP_KERNEL); + if (!addr_buf) + return -ENOMEM; + + addr_buf[0] = reg >> 8; + addr_buf[1] = reg & 0xFF; + memcpy(&addr_buf[2], buf, len); + + msg.flags = 0; + msg.addr = client->addr; + msg.buf = addr_buf; + msg.len = len + 2; + + ret = i2c_transfer(client->adapter, &msg, 1); + kfree(addr_buf); + return ret < 0 ? ret : (ret != 1 ? -EIO : 0); +} + +static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value) +{ + return goodix_i2c_write(client, reg, &value, sizeof(value)); +} + +static int goodix_get_cfg_len(u16 id) +{ + switch (id) { + case 911: + case 9271: + case 9110: + case 927: + case 928: + return GOODIX_CONFIG_911_LENGTH; + + case 912: + case 967: + return GOODIX_CONFIG_967_LENGTH; + + default: + return GOODIX_CONFIG_MAX_LENGTH; + } +} + static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) { int touch_num; @@ -155,10 +233,13 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) int input_y = get_unaligned_le16(&coor_data[3]); int input_w = get_unaligned_le16(&coor_data[5]); - if (ts->rotated_screen) { + /* Inversions have to happen before axis swapping */ + if (ts->inverted_x) input_x = ts->abs_x_max - input_x; + if (ts->inverted_y) input_y = ts->abs_y_max - input_y; - } + if (ts->swapped_x_y) + swap(input_x, input_y); input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); @@ -202,21 +283,195 @@ static void goodix_process_events(struct goodix_ts_data *ts) */ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) { - static const u8 end_cmd[] = { - GOODIX_READ_COOR_ADDR >> 8, - GOODIX_READ_COOR_ADDR & 0xff, - 0 - }; struct goodix_ts_data *ts = dev_id; goodix_process_events(ts); - if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0) + if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0) dev_err(&ts->client->dev, "I2C write end_cmd error\n"); return IRQ_HANDLED; } +static void goodix_free_irq(struct goodix_ts_data *ts) +{ + devm_free_irq(&ts->client->dev, ts->client->irq, ts); +} + +static int goodix_request_irq(struct goodix_ts_data *ts) +{ + return devm_request_threaded_irq(&ts->client->dev, ts->client->irq, + NULL, goodix_ts_irq_handler, + ts->irq_flags, ts->client->name, ts); +} + +/** + * goodix_check_cfg - Checks if config fw is valid + * + * @ts: goodix_ts_data pointer + * @cfg: firmware config data + */ +static int goodix_check_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int i, raw_cfg_len; + u8 check_sum = 0; + + if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) { + dev_err(&ts->client->dev, + "The length of the config fw is not correct"); + return -EINVAL; + } + + raw_cfg_len = cfg->size - 2; + for (i = 0; i < raw_cfg_len; i++) + check_sum += cfg->data[i]; + check_sum = (~check_sum) + 1; + if (check_sum != cfg->data[raw_cfg_len]) { + dev_err(&ts->client->dev, + "The checksum of the config fw is not correct"); + return -EINVAL; + } + + if (cfg->data[raw_cfg_len + 1] != 1) { + dev_err(&ts->client->dev, + "Config fw must have Config_Fresh register set"); + return -EINVAL; + } + + return 0; +} + +/** + * goodix_send_cfg - Write fw config to device + * + * @ts: goodix_ts_data pointer + * @cfg: config firmware to write to device + */ +static int goodix_send_cfg(struct goodix_ts_data *ts, + const struct firmware *cfg) +{ + int error; + + error = goodix_check_cfg(ts, cfg); + if (error) + return error; + + error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data, + cfg->size); + if (error) { + dev_err(&ts->client->dev, "Failed to write config data: %d", + error); + return error; + } + dev_dbg(&ts->client->dev, "Config sent successfully."); + + /* Let the firmware reconfigure itself, so sleep for 10ms */ + usleep_range(10000, 11000); + + return 0; +} + +static int goodix_int_sync(struct goodix_ts_data *ts) +{ + int error; + + error = gpiod_direction_output(ts->gpiod_int, 0); + if (error) + return error; + + msleep(50); /* T5: 50ms */ + + error = gpiod_direction_input(ts->gpiod_int); + if (error) + return error; + + return 0; +} + +/** + * goodix_reset - Reset device during power on + * + * @ts: goodix_ts_data pointer + */ +static int goodix_reset(struct goodix_ts_data *ts) +{ + int error; + + /* begin select I2C slave addr */ + error = gpiod_direction_output(ts->gpiod_rst, 0); + if (error) + return error; + + msleep(20); /* T2: > 10ms */ + + /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ + error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14); + if (error) + return error; + + usleep_range(100, 2000); /* T3: > 100us */ + + error = gpiod_direction_output(ts->gpiod_rst, 1); + if (error) + return error; + + usleep_range(6000, 10000); /* T4: > 5ms */ + + /* end select I2C slave addr */ + error = gpiod_direction_input(ts->gpiod_rst); + if (error) + return error; + + error = goodix_int_sync(ts); + if (error) + return error; + + return 0; +} + +/** + * goodix_get_gpio_config - Get GPIO config from ACPI/DT + * + * @ts: goodix_ts_data pointer + */ +static int goodix_get_gpio_config(struct goodix_ts_data *ts) +{ + int error; + struct device *dev; + struct gpio_desc *gpiod; + + if (!ts->client) + return -EINVAL; + dev = &ts->client->dev; + + /* Get the interrupt GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_INT_NAME, error); + return error; + } + + ts->gpiod_int = gpiod; + + /* Get the reset line GPIO pin number */ + gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_dbg(dev, "Failed to get %s GPIO: %d\n", + GOODIX_GPIO_RST_NAME, error); + return error; + } + + ts->gpiod_rst = gpiod; + + return 0; +} + /** * goodix_read_config - Read the embedded configuration of the panel * @@ -230,14 +485,15 @@ static void goodix_read_config(struct goodix_ts_data *ts) int error; error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, - config, - GOODIX_CONFIG_MAX_LENGTH); + config, ts->cfg_len); if (error) { dev_warn(&ts->client->dev, "Error reading config (%d), using defaults\n", error); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = GOODIX_INT_TRIGGER; ts->max_touch_num = GOODIX_MAX_CONTACTS; return; @@ -245,6 +501,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { @@ -252,42 +510,45 @@ static void goodix_read_config(struct goodix_ts_data *ts) "Invalid config, using defaults\n"); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + if (ts->swapped_x_y) + swap(ts->abs_x_max, ts->abs_y_max); ts->max_touch_num = GOODIX_MAX_CONTACTS; } - ts->rotated_screen = dmi_check_system(rotated_screen); - if (ts->rotated_screen) + if (dmi_check_system(rotated_screen)) { + ts->inverted_x = true; + ts->inverted_y = true; dev_dbg(&ts->client->dev, "Applying '180 degrees rotated screen' quirk\n"); + } } /** * goodix_read_version - Read goodix touchscreen version * - * @client: the i2c client - * @version: output buffer containing the version on success - * @id: output buffer containing the id on success + * @ts: our goodix_ts_data pointer */ -static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id) +static int goodix_read_version(struct goodix_ts_data *ts) { int error; u8 buf[6]; char id_str[5]; - error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf)); + error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf)); if (error) { - dev_err(&client->dev, "read version failed: %d\n", error); + dev_err(&ts->client->dev, "read version failed: %d\n", error); return error; } memcpy(id_str, buf, 4); id_str[4] = 0; - if (kstrtou16(id_str, 10, id)) - *id = 0x1001; + if (kstrtou16(id_str, 10, &ts->id)) + ts->id = 0x1001; - *version = get_unaligned_le16(&buf[4]); + ts->version = get_unaligned_le16(&buf[4]); - dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version); + dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id, + ts->version); return 0; } @@ -321,13 +582,10 @@ static int goodix_i2c_test(struct i2c_client *client) * goodix_request_input_dev - Allocate, populate and register the input device * * @ts: our goodix_ts_data pointer - * @version: device firmware version - * @id: device ID * * Must be called during probe */ -static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, - u16 id) +static int goodix_request_input_dev(struct goodix_ts_data *ts) { int error; @@ -351,8 +609,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, ts->input_dev->phys = "input/ts"; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0x0416; - ts->input_dev->id.product = id; - ts->input_dev->id.version = version; + ts->input_dev->id.product = ts->id; + ts->input_dev->id.version = ts->version; error = input_register_device(ts->input_dev); if (error) { @@ -364,13 +622,75 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, return 0; } +/** + * goodix_configure_dev - Finish device initialization + * + * @ts: our goodix_ts_data pointer + * + * Must be called from probe to finish initialization of the device. + * Contains the common initialization code for both devices that + * declare gpio pins and devices that do not. It is either called + * directly from probe or from request_firmware_wait callback. + */ +static int goodix_configure_dev(struct goodix_ts_data *ts) +{ + int error; + + ts->swapped_x_y = device_property_read_bool(&ts->client->dev, + "touchscreen-swapped-x-y"); + ts->inverted_x = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-x"); + ts->inverted_y = device_property_read_bool(&ts->client->dev, + "touchscreen-inverted-y"); + + goodix_read_config(ts); + + error = goodix_request_input_dev(ts); + if (error) + return error; + + ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = goodix_request_irq(ts); + if (error) { + dev_err(&ts->client->dev, "request IRQ failed: %d\n", error); + return error; + } + + return 0; +} + +/** + * goodix_config_cb - Callback to finish device init + * + * @ts: our goodix_ts_data pointer + * + * request_firmware_wait callback that finishes + * initialization of the device. + */ +static void goodix_config_cb(const struct firmware *cfg, void *ctx) +{ + struct goodix_ts_data *ts = ctx; + int error; + + if (cfg) { + /* send device configuration to the firmware */ + error = goodix_send_cfg(ts, cfg); + if (error) + goto err_release_cfg; + } + + goodix_configure_dev(ts); + +err_release_cfg: + release_firmware(cfg); + complete_all(&ts->firmware_loading_complete); +} + static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_data *ts; - unsigned long irq_flags; int error; - u16 version_info, id_info; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -385,6 +705,20 @@ static int goodix_ts_probe(struct i2c_client *client, ts->client = client; i2c_set_clientdata(client, ts); + init_completion(&ts->firmware_loading_complete); + + error = goodix_get_gpio_config(ts); + if (error) + return error; + + if (ts->gpiod_int && ts->gpiod_rst) { + /* reset the controller */ + error = goodix_reset(ts); + if (error) { + dev_err(&client->dev, "Controller reset failed.\n"); + return error; + } + } error = goodix_i2c_test(client); if (error) { @@ -392,30 +726,125 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } - error = goodix_read_version(client, &version_info, &id_info); + error = goodix_read_version(ts); if (error) { dev_err(&client->dev, "Read version failed.\n"); return error; } - goodix_read_config(ts); + ts->cfg_len = goodix_get_cfg_len(ts->id); + + if (ts->gpiod_int && ts->gpiod_rst) { + /* update device config */ + ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL, + "goodix_%d_cfg.bin", ts->id); + if (!ts->cfg_name) + return -ENOMEM; + + error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name, + &client->dev, GFP_KERNEL, ts, + goodix_config_cb); + if (error) { + dev_err(&client->dev, + "Failed to invoke firmware loader: %d\n", + error); + return error; + } - error = goodix_request_input_dev(ts, version_info, id_info); - if (error) - return error; + return 0; + } else { + error = goodix_configure_dev(ts); + if (error) + return error; + } + + return 0; +} + +static int goodix_ts_remove(struct i2c_client *client) +{ + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + if (ts->gpiod_int && ts->gpiod_rst) + wait_for_completion(&ts->firmware_loading_complete); + + return 0; +} + +static int __maybe_unused goodix_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; - irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; - error = devm_request_threaded_irq(&ts->client->dev, client->irq, - NULL, goodix_ts_irq_handler, - irq_flags, client->name, ts); + /* We need gpio pins to suspend/resume */ + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + wait_for_completion(&ts->firmware_loading_complete); + + /* Free IRQ as IRQ pin is used as output in the suspend sequence */ + goodix_free_irq(ts); + + /* Output LOW on the INT pin for 5 ms */ + error = gpiod_direction_output(ts->gpiod_int, 0); if (error) { - dev_err(&client->dev, "request IRQ failed: %d\n", error); + goodix_request_irq(ts); return error; } + usleep_range(5000, 6000); + + error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND, + GOODIX_CMD_SCREEN_OFF); + if (error) { + dev_err(&ts->client->dev, "Screen off command failed\n"); + gpiod_direction_input(ts->gpiod_int); + goodix_request_irq(ts); + return -EAGAIN; + } + + /* + * The datasheet specifies that the interval between sending screen-off + * command and wake-up should be longer than 58 ms. To avoid waking up + * sooner, delay 58ms here. + */ + msleep(58); + return 0; +} + +static int __maybe_unused goodix_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct goodix_ts_data *ts = i2c_get_clientdata(client); + int error; + + if (!ts->gpiod_int || !ts->gpiod_rst) + return 0; + + /* + * Exit sleep mode by outputting HIGH level to INT pin + * for 2ms~5ms. + */ + error = gpiod_direction_output(ts->gpiod_int, 1); + if (error) + return error; + + usleep_range(2000, 5000); + + error = goodix_int_sync(ts); + if (error) + return error; + + error = goodix_request_irq(ts); + if (error) + return error; + return 0; } +static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume); + static const struct i2c_device_id goodix_ts_id[] = { { "GDIX1001:00", 0 }, { } @@ -446,11 +875,13 @@ MODULE_DEVICE_TABLE(of, goodix_of_match); static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, + .remove = goodix_ts_remove, .id_table = goodix_ts_id, .driver = { .name = "Goodix-TS", .acpi_match_table = ACPI_PTR(goodix_acpi_match), .of_match_table = of_match_ptr(goodix_of_match), + .pm = &goodix_pm_ops, }, }; module_i2c_driver(goodix_ts_driver); diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c index 23a354a39..0e3fc419a 100644 --- a/drivers/input/touchscreen/pcap_ts.c +++ b/drivers/input/touchscreen/pcap_ts.c @@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2]) static void pcap_ts_work(struct work_struct *work) { - struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct delayed_work *dw = to_delayed_work(work); struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); u8 ch[2]; diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 91621725b..09523a3d3 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data { struct input_dev *input; struct gpio_desc *gpio_attb; struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_enable; + struct gpio_desc *gpio_wake; const struct pixcir_i2c_chip_data *chip; int max_fingers; /* Max fingers supported in this instance */ bool running; @@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, struct device *dev = &ts->client->dev; int ret; + if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 1); + } + ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE); if (ret < 0) { dev_err(dev, "%s: can't read reg 0x%x : %d\n", @@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, return ret; } + if (mode == PIXCIR_POWER_HALT) { + if (ts->gpio_wake) + gpiod_set_value_cansleep(ts->gpio_wake, 0); + } + return 0; } @@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts) struct device *dev = &ts->client->dev; int error; + if (ts->gpio_enable) { + gpiod_set_value_cansleep(ts->gpio_enable, 1); + msleep(100); + } + /* LEVEL_TOUCH interrupt with active low polarity */ error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0); if (error) { @@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts) /* Wait till running ISR is complete */ synchronize_irq(ts->client->irq); + if (ts->gpio_enable) + gpiod_set_value_cansleep(ts->gpio_enable, 0); + return 0; } @@ -377,8 +397,6 @@ static int __maybe_unused pixcir_i2c_ts_suspend(struct device *dev) goto unlock; } } - - enable_irq_wake(client->irq); } else if (input->users) { ret = pixcir_stop(ts); } @@ -399,7 +417,6 @@ static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev) mutex_lock(&input->mutex); if (device_may_wakeup(&client->dev)) { - disable_irq_wake(client->irq); if (!input->users) { ret = pixcir_stop(ts); @@ -537,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return error; } + tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_wake)) { + error = PTR_ERR(tsdata->gpio_wake); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get wake gpio: %d\n", error); + return error; + } + + tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(tsdata->gpio_enable)) { + error = PTR_ERR(tsdata->gpio_enable); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get enable gpio: %d\n", error); + return error; + } + + if (tsdata->gpio_enable) + msleep(100); + error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, tsdata); @@ -564,14 +602,6 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return error; i2c_set_clientdata(client, tsdata); - device_init_wakeup(&client->dev, 1); - - return 0; -} - -static int pixcir_i2c_ts_remove(struct i2c_client *client) -{ - device_init_wakeup(&client->dev, 0); return 0; } @@ -609,7 +639,6 @@ static struct i2c_driver pixcir_i2c_ts_driver = { .of_match_table = of_match_ptr(pixcir_of_match), }, .probe = pixcir_i2c_ts_probe, - .remove = pixcir_i2c_ts_remove, .id_table = pixcir_i2c_ts_id, }; diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c new file mode 100644 index 000000000..210640d00 --- /dev/null +++ b/drivers/input/touchscreen/rohm_bu21023.c @@ -0,0 +1,1218 @@ +/* + * ROHM BU21023/24 Dual touch support resistive touch screen driver + * Copyright (C) 2012 ROHM CO.,LTD. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> + +#define BU21023_NAME "bu21023_ts" +#define BU21023_FIRMWARE_NAME "/*(DEBLOBBED)*/" + +#define MAX_CONTACTS 2 + +#define AXIS_ADJUST 4 +#define AXIS_OFFSET 8 + +#define FIRMWARE_BLOCK_SIZE 32U +#define FIRMWARE_RETRY_MAX 4 + +#define SAMPLING_DELAY 12 /* msec */ + +#define CALIBRATION_RETRY_MAX 6 + +#define ROHM_TS_ABS_X_MIN 40 +#define ROHM_TS_ABS_X_MAX 990 +#define ROHM_TS_ABS_Y_MIN 160 +#define ROHM_TS_ABS_Y_MAX 920 +#define ROHM_TS_DISPLACEMENT_MAX 0 /* zero for infinite */ + +/* + * BU21023GUL/BU21023MUV/BU21024FV-M registers map + */ +#define VADOUT_YP_H 0x00 +#define VADOUT_YP_L 0x01 +#define VADOUT_XP_H 0x02 +#define VADOUT_XP_L 0x03 +#define VADOUT_YN_H 0x04 +#define VADOUT_YN_L 0x05 +#define VADOUT_XN_H 0x06 +#define VADOUT_XN_L 0x07 + +#define PRM1_X_H 0x08 +#define PRM1_X_L 0x09 +#define PRM1_Y_H 0x0a +#define PRM1_Y_L 0x0b +#define PRM2_X_H 0x0c +#define PRM2_X_L 0x0d +#define PRM2_Y_H 0x0e +#define PRM2_Y_L 0x0f + +#define MLT_PRM_MONI_X 0x10 +#define MLT_PRM_MONI_Y 0x11 + +#define DEBUG_MONI_1 0x12 +#define DEBUG_MONI_2 0x13 + +#define VADOUT_ZX_H 0x14 +#define VADOUT_ZX_L 0x15 +#define VADOUT_ZY_H 0x16 +#define VADOUT_ZY_L 0x17 + +#define Z_PARAM_H 0x18 +#define Z_PARAM_L 0x19 + +/* + * Value for VADOUT_*_L + */ +#define VADOUT_L_MASK 0x01 + +/* + * Value for PRM*_*_L + */ +#define PRM_L_MASK 0x01 + +#define POS_X1_H 0x20 +#define POS_X1_L 0x21 +#define POS_Y1_H 0x22 +#define POS_Y1_L 0x23 +#define POS_X2_H 0x24 +#define POS_X2_L 0x25 +#define POS_Y2_H 0x26 +#define POS_Y2_L 0x27 + +/* + * Value for POS_*_L + */ +#define POS_L_MASK 0x01 + +#define TOUCH 0x28 +#define TOUCH_DETECT 0x01 + +#define TOUCH_GESTURE 0x29 +#define SINGLE_TOUCH 0x01 +#define DUAL_TOUCH 0x03 +#define TOUCH_MASK 0x03 +#define CALIBRATION_REQUEST 0x04 +#define CALIBRATION_STATUS 0x08 +#define CALIBRATION_MASK 0x0c +#define GESTURE_SPREAD 0x10 +#define GESTURE_PINCH 0x20 +#define GESTURE_ROTATE_R 0x40 +#define GESTURE_ROTATE_L 0x80 + +#define INT_STATUS 0x2a +#define INT_MASK 0x3d +#define INT_CLEAR 0x3e + +/* + * Values for INT_* + */ +#define COORD_UPDATE 0x01 +#define CALIBRATION_DONE 0x02 +#define SLEEP_IN 0x04 +#define SLEEP_OUT 0x08 +#define PROGRAM_LOAD_DONE 0x10 +#define ERROR 0x80 +#define INT_ALL 0x9f + +#define ERR_STATUS 0x2b +#define ERR_MASK 0x3f + +/* + * Values for ERR_* + */ +#define ADC_TIMEOUT 0x01 +#define CPU_TIMEOUT 0x02 +#define CALIBRATION_ERR 0x04 +#define PROGRAM_LOAD_ERR 0x10 + +#define COMMON_SETUP1 0x30 +#define PROGRAM_LOAD_HOST 0x02 +#define PROGRAM_LOAD_EEPROM 0x03 +#define CENSOR_4PORT 0x04 +#define CENSOR_8PORT 0x00 /* Not supported by BU21023 */ +#define CALIBRATION_TYPE_DEFAULT 0x08 +#define CALIBRATION_TYPE_SPECIAL 0x00 +#define INT_ACTIVE_HIGH 0x10 +#define INT_ACTIVE_LOW 0x00 +#define AUTO_CALIBRATION 0x40 +#define MANUAL_CALIBRATION 0x00 +#define COMMON_SETUP1_DEFAULT 0x4e + +#define COMMON_SETUP2 0x31 +#define MAF_NONE 0x00 +#define MAF_1SAMPLE 0x01 +#define MAF_3SAMPLES 0x02 +#define MAF_5SAMPLES 0x03 +#define INV_Y 0x04 +#define INV_X 0x08 +#define SWAP_XY 0x10 + +#define COMMON_SETUP3 0x32 +#define EN_SLEEP 0x01 +#define EN_MULTI 0x02 +#define EN_GESTURE 0x04 +#define EN_INTVL 0x08 +#define SEL_STEP 0x10 +#define SEL_MULTI 0x20 +#define SEL_TBL_DEFAULT 0x40 + +#define INTERVAL_TIME 0x33 +#define INTERVAL_TIME_DEFAULT 0x10 + +#define STEP_X 0x34 +#define STEP_X_DEFAULT 0x41 + +#define STEP_Y 0x35 +#define STEP_Y_DEFAULT 0x8d + +#define OFFSET_X 0x38 +#define OFFSET_X_DEFAULT 0x0c + +#define OFFSET_Y 0x39 +#define OFFSET_Y_DEFAULT 0x0c + +#define THRESHOLD_TOUCH 0x3a +#define THRESHOLD_TOUCH_DEFAULT 0xa0 + +#define THRESHOLD_GESTURE 0x3b +#define THRESHOLD_GESTURE_DEFAULT 0x17 + +#define SYSTEM 0x40 +#define ANALOG_POWER_ON 0x01 +#define ANALOG_POWER_OFF 0x00 +#define CPU_POWER_ON 0x02 +#define CPU_POWER_OFF 0x00 + +#define FORCE_CALIBRATION 0x42 +#define FORCE_CALIBRATION_ON 0x01 +#define FORCE_CALIBRATION_OFF 0x00 + +#define CPU_FREQ 0x50 /* 10 / (reg + 1) MHz */ +#define CPU_FREQ_10MHZ 0x00 +#define CPU_FREQ_5MHZ 0x01 +#define CPU_FREQ_1MHZ 0x09 + +#define EEPROM_ADDR 0x51 + +#define CALIBRATION_ADJUST 0x52 +#define CALIBRATION_ADJUST_DEFAULT 0x00 + +#define THRESHOLD_SLEEP_IN 0x53 + +#define EVR_XY 0x56 +#define EVR_XY_DEFAULT 0x10 + +#define PRM_SWOFF_TIME 0x57 +#define PRM_SWOFF_TIME_DEFAULT 0x04 + +#define PROGRAM_VERSION 0x5f + +#define ADC_CTRL 0x60 +#define ADC_DIV_MASK 0x1f /* The minimum value is 4 */ +#define ADC_DIV_DEFAULT 0x08 + +#define ADC_WAIT 0x61 +#define ADC_WAIT_DEFAULT 0x0a + +#define SWCONT 0x62 +#define SWCONT_DEFAULT 0x0f + +#define EVR_X 0x63 +#define EVR_X_DEFAULT 0x86 + +#define EVR_Y 0x64 +#define EVR_Y_DEFAULT 0x64 + +#define TEST1 0x65 +#define DUALTOUCH_STABILIZE_ON 0x01 +#define DUALTOUCH_STABILIZE_OFF 0x00 +#define DUALTOUCH_REG_ON 0x20 +#define DUALTOUCH_REG_OFF 0x00 + +#define CALIBRATION_REG1 0x68 +#define CALIBRATION_REG1_DEFAULT 0xd9 + +#define CALIBRATION_REG2 0x69 +#define CALIBRATION_REG2_DEFAULT 0x36 + +#define CALIBRATION_REG3 0x6a +#define CALIBRATION_REG3_DEFAULT 0x32 + +#define EX_ADDR_H 0x70 +#define EX_ADDR_L 0x71 +#define EX_WDAT 0x72 +#define EX_RDAT 0x73 +#define EX_CHK_SUM1 0x74 +#define EX_CHK_SUM2 0x75 +#define EX_CHK_SUM3 0x76 + +struct rohm_ts_data { + struct i2c_client *client; + struct input_dev *input; + + bool initialized; + + unsigned int contact_count[MAX_CONTACTS + 1]; + int finger_count; + + u8 setup2; +}; + +/* + * rohm_i2c_burst_read - execute combined I2C message for ROHM BU21023/24 + * @client: Handle to ROHM BU21023/24 + * @start: Where to start read address from ROHM BU21023/24 + * @buf: Where to store read data from ROHM BU21023/24 + * @len: How many bytes to read + * + * Returns negative errno, else zero on success. + * + * Note + * In BU21023/24 burst read, stop condition is needed after "address write". + * Therefore, transmission is performed in 2 steps. + */ +static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf, + size_t len) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg[2]; + int i, ret = 0; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = buf; + + i2c_lock_adapter(adap); + + for (i = 0; i < 2; i++) { + if (__i2c_transfer(adap, &msg[i], 1) < 0) { + ret = -EIO; + break; + } + } + + i2c_unlock_adapter(adap); + + return ret; +} + +static int rohm_ts_manual_calibration(struct rohm_ts_data *ts) +{ + struct i2c_client *client = ts->client; + struct device *dev = &client->dev; + u8 buf[33]; /* for PRM1_X_H(0x08)-TOUCH(0x28) */ + + int retry; + bool success = false; + bool first_time = true; + bool calibration_done; + + u8 reg1, reg2, reg3; + s32 reg1_orig, reg2_orig, reg3_orig; + s32 val; + + int calib_x = 0, calib_y = 0; + int reg_x, reg_y; + int err_x, err_y; + + int error, error2; + int i; + + reg1_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG1); + if (reg1_orig < 0) + return reg1_orig; + + reg2_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG2); + if (reg2_orig < 0) + return reg2_orig; + + reg3_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG3); + if (reg3_orig < 0) + return reg3_orig; + + error = i2c_smbus_write_byte_data(client, INT_MASK, + COORD_UPDATE | SLEEP_IN | SLEEP_OUT | + PROGRAM_LOAD_DONE); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, TEST1, + DUALTOUCH_STABILIZE_ON); + if (error) + goto out; + + for (retry = 0; retry < CALIBRATION_RETRY_MAX; retry++) { + /* wait 2 sampling for update */ + mdelay(2 * SAMPLING_DELAY); + +#define READ_CALIB_BUF(reg) buf[((reg) - PRM1_X_H)] + + error = rohm_i2c_burst_read(client, PRM1_X_H, buf, sizeof(buf)); + if (error) + goto out; + + if (READ_CALIB_BUF(TOUCH) & TOUCH_DETECT) + continue; + + if (first_time) { + /* generate calibration parameter */ + calib_x = ((int)READ_CALIB_BUF(PRM1_X_H) << 2 | + READ_CALIB_BUF(PRM1_X_L)) - AXIS_OFFSET; + calib_y = ((int)READ_CALIB_BUF(PRM1_Y_H) << 2 | + READ_CALIB_BUF(PRM1_Y_L)) - AXIS_OFFSET; + + error = i2c_smbus_write_byte_data(client, TEST1, + DUALTOUCH_STABILIZE_ON | DUALTOUCH_REG_ON); + if (error) + goto out; + + first_time = false; + } else { + /* generate adjustment parameter */ + err_x = (int)READ_CALIB_BUF(PRM1_X_H) << 2 | + READ_CALIB_BUF(PRM1_X_L); + err_y = (int)READ_CALIB_BUF(PRM1_Y_H) << 2 | + READ_CALIB_BUF(PRM1_Y_L); + + /* X axis ajust */ + if (err_x <= 4) + calib_x -= AXIS_ADJUST; + else if (err_x >= 60) + calib_x += AXIS_ADJUST; + + /* Y axis ajust */ + if (err_y <= 4) + calib_y -= AXIS_ADJUST; + else if (err_y >= 60) + calib_y += AXIS_ADJUST; + } + + /* generate calibration setting value */ + reg_x = calib_x + ((calib_x & 0x200) << 1); + reg_y = calib_y + ((calib_y & 0x200) << 1); + + /* convert for register format */ + reg1 = reg_x >> 3; + reg2 = (reg_y & 0x7) << 4 | (reg_x & 0x7); + reg3 = reg_y >> 3; + + error = i2c_smbus_write_byte_data(client, + CALIBRATION_REG1, reg1); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, + CALIBRATION_REG2, reg2); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, + CALIBRATION_REG3, reg3); + if (error) + goto out; + + /* + * force calibration sequcence + */ + error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION, + FORCE_CALIBRATION_OFF); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION, + FORCE_CALIBRATION_ON); + if (error) + goto out; + + /* clear all interrupts */ + error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff); + if (error) + goto out; + + /* + * Wait for the status change of calibration, max 10 sampling + */ + calibration_done = false; + + for (i = 0; i < 10; i++) { + mdelay(SAMPLING_DELAY); + + val = i2c_smbus_read_byte_data(client, TOUCH_GESTURE); + if (!(val & CALIBRATION_MASK)) { + calibration_done = true; + break; + } else if (val < 0) { + error = val; + goto out; + } + } + + if (calibration_done) { + val = i2c_smbus_read_byte_data(client, INT_STATUS); + if (val == CALIBRATION_DONE) { + success = true; + break; + } else if (val < 0) { + error = val; + goto out; + } + } else { + dev_warn(dev, "calibration timeout\n"); + } + } + + if (!success) { + error = i2c_smbus_write_byte_data(client, CALIBRATION_REG1, + reg1_orig); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, CALIBRATION_REG2, + reg2_orig); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, CALIBRATION_REG3, + reg3_orig); + if (error) + goto out; + + /* calibration data enable */ + error = i2c_smbus_write_byte_data(client, TEST1, + DUALTOUCH_STABILIZE_ON | + DUALTOUCH_REG_ON); + if (error) + goto out; + + /* wait 10 sampling */ + mdelay(10 * SAMPLING_DELAY); + + error = -EBUSY; + } + +out: + error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL); + if (!error2) + /* Clear all interrupts */ + error2 = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff); + + return error ? error : error2; +} + +static const unsigned int untouch_threshold[3] = { 0, 1, 5 }; +static const unsigned int single_touch_threshold[3] = { 0, 0, 4 }; +static const unsigned int dual_touch_threshold[3] = { 10, 8, 0 }; + +static irqreturn_t rohm_ts_soft_irq(int irq, void *dev_id) +{ + struct rohm_ts_data *ts = dev_id; + struct i2c_client *client = ts->client; + struct input_dev *input_dev = ts->input; + struct device *dev = &client->dev; + + u8 buf[10]; /* for POS_X1_H(0x20)-TOUCH_GESTURE(0x29) */ + + struct input_mt_pos pos[MAX_CONTACTS]; + int slots[MAX_CONTACTS]; + u8 touch_flags; + unsigned int threshold; + int finger_count = -1; + int prev_finger_count = ts->finger_count; + int count; + int error; + int i; + + error = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL); + if (error) + return IRQ_HANDLED; + + /* Clear all interrupts */ + error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff); + if (error) + return IRQ_HANDLED; + +#define READ_POS_BUF(reg) buf[((reg) - POS_X1_H)] + + error = rohm_i2c_burst_read(client, POS_X1_H, buf, sizeof(buf)); + if (error) + return IRQ_HANDLED; + + touch_flags = READ_POS_BUF(TOUCH_GESTURE) & TOUCH_MASK; + if (touch_flags) { + /* generate coordinates */ + pos[0].x = ((s16)READ_POS_BUF(POS_X1_H) << 2) | + READ_POS_BUF(POS_X1_L); + pos[0].y = ((s16)READ_POS_BUF(POS_Y1_H) << 2) | + READ_POS_BUF(POS_Y1_L); + pos[1].x = ((s16)READ_POS_BUF(POS_X2_H) << 2) | + READ_POS_BUF(POS_X2_L); + pos[1].y = ((s16)READ_POS_BUF(POS_Y2_H) << 2) | + READ_POS_BUF(POS_Y2_L); + } + + switch (touch_flags) { + case 0: + threshold = untouch_threshold[prev_finger_count]; + if (++ts->contact_count[0] >= threshold) + finger_count = 0; + break; + + case SINGLE_TOUCH: + threshold = single_touch_threshold[prev_finger_count]; + if (++ts->contact_count[1] >= threshold) + finger_count = 1; + + if (finger_count == 1) { + if (pos[1].x != 0 && pos[1].y != 0) { + pos[0].x = pos[1].x; + pos[0].y = pos[1].y; + pos[1].x = 0; + pos[1].y = 0; + } + } + break; + + case DUAL_TOUCH: + threshold = dual_touch_threshold[prev_finger_count]; + if (++ts->contact_count[2] >= threshold) + finger_count = 2; + break; + + default: + dev_dbg(dev, + "Three or more touches are not supported\n"); + return IRQ_HANDLED; + } + + if (finger_count >= 0) { + if (prev_finger_count != finger_count) { + count = ts->contact_count[finger_count]; + memset(ts->contact_count, 0, sizeof(ts->contact_count)); + ts->contact_count[finger_count] = count; + } + + input_mt_assign_slots(input_dev, slots, pos, + finger_count, ROHM_TS_DISPLACEMENT_MAX); + + for (i = 0; i < finger_count; i++) { + input_mt_slot(input_dev, slots[i]); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, true); + input_report_abs(input_dev, + ABS_MT_POSITION_X, pos[i].x); + input_report_abs(input_dev, + ABS_MT_POSITION_Y, pos[i].y); + } + + input_mt_sync_frame(input_dev); + input_mt_report_pointer_emulation(input_dev, true); + input_sync(input_dev); + + ts->finger_count = finger_count; + } + + if (READ_POS_BUF(TOUCH_GESTURE) & CALIBRATION_REQUEST) { + error = rohm_ts_manual_calibration(ts); + if (error) + dev_warn(dev, "manual calibration failed: %d\n", + error); + } + + i2c_smbus_write_byte_data(client, INT_MASK, + CALIBRATION_DONE | SLEEP_OUT | SLEEP_IN | + PROGRAM_LOAD_DONE); + + return IRQ_HANDLED; +} + +static int rohm_ts_load_firmware(struct i2c_client *client, + const char *firmware_name) +{ + struct device *dev = &client->dev; + const struct firmware *fw; + s32 status; + unsigned int offset, len, xfer_len; + unsigned int retry = 0; + int error, error2; + + error = reject_firmware(&fw, firmware_name, dev); + if (error) { + dev_err(dev, "unable to retrieve firmware %s: %d\n", + firmware_name, error); + return error; + } + + error = i2c_smbus_write_byte_data(client, INT_MASK, + COORD_UPDATE | CALIBRATION_DONE | + SLEEP_IN | SLEEP_OUT); + if (error) + goto out; + + do { + if (retry) { + dev_warn(dev, "retrying firmware load\n"); + + /* settings for retry */ + error = i2c_smbus_write_byte_data(client, EX_WDAT, 0); + if (error) + goto out; + } + + error = i2c_smbus_write_byte_data(client, EX_ADDR_H, 0); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, EX_ADDR_L, 0); + if (error) + goto out; + + error = i2c_smbus_write_byte_data(client, COMMON_SETUP1, + COMMON_SETUP1_DEFAULT); + if (error) + goto out; + + /* firmware load to the device */ + offset = 0; + len = fw->size; + + while (len) { + xfer_len = min(FIRMWARE_BLOCK_SIZE, len); + + error = i2c_smbus_write_i2c_block_data(client, EX_WDAT, + xfer_len, &fw->data[offset]); + if (error) + goto out; + + len -= xfer_len; + offset += xfer_len; + } + + /* check firmware load result */ + status = i2c_smbus_read_byte_data(client, INT_STATUS); + if (status < 0) { + error = status; + goto out; + } + + /* clear all interrupts */ + error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff); + if (error) + goto out; + + if (status == PROGRAM_LOAD_DONE) + break; + + error = -EIO; + } while (++retry <= FIRMWARE_RETRY_MAX); + +out: + error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL); + + release_firmware(fw); + + return error ? error : error2; +} + +static ssize_t swap_xy_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rohm_ts_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", !!(ts->setup2 & SWAP_XY)); +} + +static ssize_t swap_xy_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rohm_ts_data *ts = i2c_get_clientdata(client); + unsigned int val; + int error; + + error = kstrtouint(buf, 0, &val); + if (error) + return error; + + error = mutex_lock_interruptible(&ts->input->mutex); + if (error) + return error; + + if (val) + ts->setup2 |= SWAP_XY; + else + ts->setup2 &= ~SWAP_XY; + + if (ts->initialized) + error = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2, + ts->setup2); + + mutex_unlock(&ts->input->mutex); + + return error ? error : count; +} + +static ssize_t inv_x_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rohm_ts_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", !!(ts->setup2 & INV_X)); +} + +static ssize_t inv_x_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rohm_ts_data *ts = i2c_get_clientdata(client); + unsigned int val; + int error; + + error = kstrtouint(buf, 0, &val); + if (error) + return error; + + error = mutex_lock_interruptible(&ts->input->mutex); + if (error) + return error; + + if (val) + ts->setup2 |= INV_X; + else + ts->setup2 &= ~INV_X; + + if (ts->initialized) + error = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2, + ts->setup2); + + mutex_unlock(&ts->input->mutex); + + return error ? error : count; +} + +static ssize_t inv_y_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rohm_ts_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", !!(ts->setup2 & INV_Y)); +} + +static ssize_t inv_y_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rohm_ts_data *ts = i2c_get_clientdata(client); + unsigned int val; + int error; + + error = kstrtouint(buf, 0, &val); + if (error) + return error; + + error = mutex_lock_interruptible(&ts->input->mutex); + if (error) + return error; + + if (val) + ts->setup2 |= INV_Y; + else + ts->setup2 &= ~INV_Y; + + if (ts->initialized) + error = i2c_smbus_write_byte_data(client, COMMON_SETUP2, + ts->setup2); + + mutex_unlock(&ts->input->mutex); + + return error ? error : count; +} + +static DEVICE_ATTR_RW(swap_xy); +static DEVICE_ATTR_RW(inv_x); +static DEVICE_ATTR_RW(inv_y); + +static struct attribute *rohm_ts_attrs[] = { + &dev_attr_swap_xy.attr, + &dev_attr_inv_x.attr, + &dev_attr_inv_y.attr, + NULL, +}; + +static const struct attribute_group rohm_ts_attr_group = { + .attrs = rohm_ts_attrs, +}; + +static int rohm_ts_device_init(struct i2c_client *client, u8 setup2) +{ + struct device *dev = &client->dev; + int error; + + disable_irq(client->irq); + + /* + * Wait 200usec for reset + */ + udelay(200); + + /* Release analog reset */ + error = i2c_smbus_write_byte_data(client, SYSTEM, + ANALOG_POWER_ON | CPU_POWER_OFF); + if (error) + return error; + + /* Waiting for the analog warm-up, max. 200usec */ + udelay(200); + + /* clear all interrupts */ + error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, EX_WDAT, 0); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, COMMON_SETUP1, 0); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, COMMON_SETUP2, setup2); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, COMMON_SETUP3, + SEL_TBL_DEFAULT | EN_MULTI); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, THRESHOLD_GESTURE, + THRESHOLD_GESTURE_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, INTERVAL_TIME, + INTERVAL_TIME_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, CPU_FREQ, CPU_FREQ_10MHZ); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, PRM_SWOFF_TIME, + PRM_SWOFF_TIME_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, ADC_CTRL, ADC_DIV_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, ADC_WAIT, ADC_WAIT_DEFAULT); + if (error) + return error; + + /* + * Panel setup, these values change with the panel. + */ + error = i2c_smbus_write_byte_data(client, STEP_X, STEP_X_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, STEP_Y, STEP_Y_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, OFFSET_X, OFFSET_X_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, OFFSET_Y, OFFSET_Y_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, THRESHOLD_TOUCH, + THRESHOLD_TOUCH_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, EVR_XY, EVR_XY_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, EVR_X, EVR_X_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, EVR_Y, EVR_Y_DEFAULT); + if (error) + return error; + + /* Fixed value settings */ + error = i2c_smbus_write_byte_data(client, CALIBRATION_ADJUST, + CALIBRATION_ADJUST_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, SWCONT, SWCONT_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, TEST1, + DUALTOUCH_STABILIZE_ON | + DUALTOUCH_REG_ON); + if (error) + return error; + + error = rohm_ts_load_firmware(client, BU21023_FIRMWARE_NAME); + if (error) { + dev_err(dev, "failed to load firmware: %d\n", error); + return error; + } + + /* + * Manual calibration results are not changed in same environment. + * If the force calibration is performed, + * the controller will not require calibration request interrupt + * when the typical values are set to the calibration registers. + */ + error = i2c_smbus_write_byte_data(client, CALIBRATION_REG1, + CALIBRATION_REG1_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, CALIBRATION_REG2, + CALIBRATION_REG2_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, CALIBRATION_REG3, + CALIBRATION_REG3_DEFAULT); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION, + FORCE_CALIBRATION_OFF); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION, + FORCE_CALIBRATION_ON); + if (error) + return error; + + /* Clear all interrupts */ + error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff); + if (error) + return error; + + /* Enable coordinates update interrupt */ + error = i2c_smbus_write_byte_data(client, INT_MASK, + CALIBRATION_DONE | SLEEP_OUT | + SLEEP_IN | PROGRAM_LOAD_DONE); + if (error) + return error; + + error = i2c_smbus_write_byte_data(client, ERR_MASK, + PROGRAM_LOAD_ERR | CPU_TIMEOUT | + ADC_TIMEOUT); + if (error) + return error; + + /* controller CPU power on */ + error = i2c_smbus_write_byte_data(client, SYSTEM, + ANALOG_POWER_ON | CPU_POWER_ON); + + enable_irq(client->irq); + + return error; +} + +static int rohm_ts_power_off(struct i2c_client *client) +{ + int error; + + error = i2c_smbus_write_byte_data(client, SYSTEM, + ANALOG_POWER_ON | CPU_POWER_OFF); + if (error) { + dev_err(&client->dev, + "failed to power off device CPU: %d\n", error); + return error; + } + + error = i2c_smbus_write_byte_data(client, SYSTEM, + ANALOG_POWER_OFF | CPU_POWER_OFF); + if (error) + dev_err(&client->dev, + "failed to power off the device: %d\n", error); + + return error; +} + +static int rohm_ts_open(struct input_dev *input_dev) +{ + struct rohm_ts_data *ts = input_get_drvdata(input_dev); + struct i2c_client *client = ts->client; + int error; + + if (!ts->initialized) { + error = rohm_ts_device_init(client, ts->setup2); + if (error) { + dev_err(&client->dev, + "device initialization failed: %d\n", error); + return error; + } + + ts->initialized = true; + } + + return 0; +} + +static void rohm_ts_close(struct input_dev *input_dev) +{ + struct rohm_ts_data *ts = input_get_drvdata(input_dev); + + rohm_ts_power_off(ts->client); + + ts->initialized = false; +} + +static void rohm_ts_remove_sysfs_group(void *_dev) +{ + struct device *dev = _dev; + + sysfs_remove_group(&dev->kobj, &rohm_ts_attr_group); +} + +static int rohm_bu21023_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct rohm_ts_data *ts; + struct input_dev *input; + int error; + + if (!client->irq) { + dev_err(dev, "IRQ is not assigned\n"); + return -EINVAL; + } + + if (!client->adapter->algo->master_xfer) { + dev_err(dev, "I2C level transfers not supported\n"); + return -EOPNOTSUPP; + } + + /* Turn off CPU just in case */ + error = rohm_ts_power_off(client); + if (error) + return error; + + ts = devm_kzalloc(dev, sizeof(struct rohm_ts_data), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->client = client; + ts->setup2 = MAF_1SAMPLE; + i2c_set_clientdata(client, ts); + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = BU21023_NAME; + input->id.bustype = BUS_I2C; + input->open = rohm_ts_open; + input->close = rohm_ts_close; + + ts->input = input; + input_set_drvdata(input, ts); + + input_set_abs_params(input, ABS_MT_POSITION_X, + ROHM_TS_ABS_X_MIN, ROHM_TS_ABS_X_MAX, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + ROHM_TS_ABS_Y_MIN, ROHM_TS_ABS_Y_MAX, 0, 0); + + error = input_mt_init_slots(input, MAX_CONTACTS, + INPUT_MT_DIRECT | INPUT_MT_TRACK | + INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(dev, "failed to multi touch slots initialization\n"); + return error; + } + + error = devm_request_threaded_irq(dev, client->irq, + NULL, rohm_ts_soft_irq, + IRQF_ONESHOT, client->name, ts); + if (error) { + dev_err(dev, "failed to request IRQ: %d\n", error); + return error; + } + + error = input_register_device(input); + if (error) { + dev_err(dev, "failed to register input device: %d\n", error); + return error; + } + + error = sysfs_create_group(&dev->kobj, &rohm_ts_attr_group); + if (error) { + dev_err(dev, "failed to create sysfs group: %d\n", error); + return error; + } + + error = devm_add_action(dev, rohm_ts_remove_sysfs_group, dev); + if (error) { + rohm_ts_remove_sysfs_group(dev); + dev_err(&client->dev, + "Failed to add sysfs cleanup action: %d\n", + error); + return error; + } + + return error; +} + +static const struct i2c_device_id rohm_bu21023_i2c_id[] = { + { BU21023_NAME, 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, rohm_bu21023_i2c_id); + +static struct i2c_driver rohm_bu21023_i2c_driver = { + .driver = { + .name = BU21023_NAME, + }, + .probe = rohm_bu21023_i2c_probe, + .id_table = rohm_bu21023_i2c_id, +}; +module_i2c_driver(rohm_bu21023_i2c_driver); + +MODULE_DESCRIPTION("ROHM BU21023/24 Touchscreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("ROHM Co., Ltd."); diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 3f117637e..d214f22ed 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -38,6 +38,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-sg.h> /* read 512 bytes from endpoint 0x86 -> get header + blobs */ @@ -163,7 +164,7 @@ struct sur40_state { }; struct sur40_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head list; }; @@ -420,7 +421,7 @@ static void sur40_process_video(struct sur40_state *sur40) dev_dbg(sur40->dev, "header acquired\n"); - sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0); + sgt = vb2_dma_sg_plane_desc(&new_buf->vb.vb2_buf, 0); result = usb_sg_init(&sgr, sur40->usbdev, usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, @@ -443,15 +444,15 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; /* mark as finished */ - v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); - new_buf->vb.v4l2_buf.sequence = sur40->sequence++; - new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; - vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + v4l2_get_timestamp(&new_buf->vb.timestamp); + new_buf->vb.sequence = sur40->sequence++; + new_buf->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); dev_dbg(sur40->dev, "buffer marked done\n"); return; err_poll: - vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } /* Initialize input device parameters. */ @@ -643,10 +644,11 @@ static void sur40_disconnect(struct usb_interface *interface) * minimum number: many DMA engines need a minimum of 2 buffers in the * queue and you need to have another available for userspace processing. */ -static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, +static int sur40_queue_setup(struct vb2_queue *q, const void *parg, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { + const struct v4l2_format *fmt = parg; struct sur40_state *sur40 = vb2_get_drv_priv(q); if (q->num_buffers + *nbuffers < 3) @@ -701,7 +703,7 @@ static void return_all_buffers(struct sur40_state *sur40, spin_lock(&sur40->qlock); list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { - vb2_buffer_done(&buf->vb, state); + vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } spin_unlock(&sur40->qlock); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 191a1b878..a21a07c3a 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev) status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); if (status & IRQENB_HW_PEN) { ts_dev->pen_down = true; - titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); - titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); irqclr |= IRQENB_HW_PEN; } diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 4ffd829d1..a340bfccd 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -50,14 +50,7 @@ struct tps6507x_ts { static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data) { - int err; - - err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data); - - if (err) - return err; - - return 0; + return tsc->mfd->read_dev(tsc->mfd, reg, 1, data); } static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data) diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c new file mode 100644 index 000000000..3c3dd7830 --- /dev/null +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -0,0 +1,216 @@ +/* + * Touchscreen driver for the TS-4800 board + * + * Copyright (c) 2015 - Savoir-faire Linux + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/bitops.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* polling interval in ms */ +#define POLL_INTERVAL 3 + +#define DEBOUNCE_COUNT 1 + +/* sensor values are 12-bit wide */ +#define MAX_12BIT ((1 << 12) - 1) + +#define PENDOWN_MASK 0x1 + +#define X_OFFSET 0x0 +#define Y_OFFSET 0x2 + +struct ts4800_ts { + struct input_polled_dev *poll_dev; + struct device *dev; + char phys[32]; + + void __iomem *base; + struct regmap *regmap; + unsigned int reg; + unsigned int bit; + + bool pendown; + int debounce; +}; + +static void ts4800_ts_open(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); + if (ret) + dev_warn(ts->dev, "Failed to enable touchscreen\n"); +} + +static void ts4800_ts_close(struct input_polled_dev *dev) +{ + struct ts4800_ts *ts = dev->private; + int ret; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0); + if (ret) + dev_warn(ts->dev, "Failed to disable touchscreen\n"); + +} + +static void ts4800_ts_poll(struct input_polled_dev *dev) +{ + struct input_dev *input_dev = dev->input; + struct ts4800_ts *ts = dev->private; + u16 last_x = readw(ts->base + X_OFFSET); + u16 last_y = readw(ts->base + Y_OFFSET); + bool pendown = last_x & PENDOWN_MASK; + + if (pendown) { + if (ts->debounce) { + ts->debounce--; + return; + } + + if (!ts->pendown) { + input_report_key(input_dev, BTN_TOUCH, 1); + ts->pendown = true; + } + + last_x = ((~last_x) >> 4) & MAX_12BIT; + last_y = ((~last_y) >> 4) & MAX_12BIT; + + input_report_abs(input_dev, ABS_X, last_x); + input_report_abs(input_dev, ABS_Y, last_y); + input_sync(input_dev); + } else if (ts->pendown) { + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + } +} + +static int ts4800_parse_dt(struct platform_device *pdev, + struct ts4800_ts *ts) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *syscon_np; + u32 reg, bit; + int error; + + syscon_np = of_parse_phandle(np, "syscon", 0); + if (!syscon_np) { + dev_err(dev, "no syscon property\n"); + return -ENODEV; + } + + error = of_property_read_u32_index(np, "syscon", 1, ®); + if (error < 0) { + dev_err(dev, "no offset in syscon\n"); + return error; + } + + ts->reg = reg; + + error = of_property_read_u32_index(np, "syscon", 2, &bit); + if (error < 0) { + dev_err(dev, "no bit in syscon\n"); + return error; + } + + ts->bit = BIT(bit); + + ts->regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(ts->regmap)) { + dev_err(dev, "cannot get parent's regmap\n"); + return PTR_ERR(ts->regmap); + } + + return 0; +} + +static int ts4800_ts_probe(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev; + struct ts4800_ts *ts; + struct resource *res; + int error; + + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + error = ts4800_parse_dt(pdev, ts); + if (error) + return error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ts->base)) + return PTR_ERR(ts->base); + + poll_dev = devm_input_allocate_polled_device(&pdev->dev); + if (!poll_dev) + return -ENOMEM; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); + ts->poll_dev = poll_dev; + ts->dev = &pdev->dev; + + poll_dev->private = ts; + poll_dev->poll_interval = POLL_INTERVAL; + poll_dev->open = ts4800_ts_open; + poll_dev->close = ts4800_ts_close; + poll_dev->poll = ts4800_ts_poll; + + poll_dev->input->name = "TS-4800 Touchscreen"; + poll_dev->input->phys = ts->phys; + + input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH); + input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(&pdev->dev, + "Unabled to register polled input device (%d)\n", + error); + return error; + } + + return 0; +} + +static const struct of_device_id ts4800_ts_of_match[] = { + { .compatible = "technologic,ts4800-ts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ts4800_ts_of_match); + +static struct platform_driver ts4800_ts_driver = { + .driver = { + .name = "ts4800-ts", + .of_match_table = ts4800_ts_of_match, + }, + .probe = ts4800_ts_probe, +}; +module_platform_driver(ts4800_ts_driver); + +MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); +MODULE_DESCRIPTION("TS-4800 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ts4800_ts"); diff --git a/drivers/input/touchscreen/tsc2004.c b/drivers/input/touchscreen/tsc2004.c new file mode 100644 index 000000000..7295c198a --- /dev/null +++ b/drivers/input/touchscreen/tsc2004.c @@ -0,0 +1,83 @@ +/* + * TSC2004 touchscreen driver + * + * Copyright (C) 2015 QWERTY Embedded Design + * Copyright (C) 2015 EMAC Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/of.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include "tsc200x-core.h" + +static int tsc2004_cmd(struct device *dev, u8 cmd) +{ + u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd; + s32 data; + struct i2c_client *i2c = to_i2c_client(dev); + + data = i2c_smbus_write_byte(i2c, tx); + if (data < 0) { + dev_err(dev, "%s: failed, command: %x i2c error: %d\n", + __func__, cmd, data); + return data; + } + + return 0; +} + +static int tsc2004_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) + +{ + return tsc200x_probe(&i2c->dev, i2c->irq, BUS_I2C, + devm_regmap_init_i2c(i2c, &tsc200x_regmap_config), + tsc2004_cmd); +} + +static int tsc2004_remove(struct i2c_client *i2c) +{ + return tsc200x_remove(&i2c->dev); +} + +static const struct i2c_device_id tsc2004_idtable[] = { + { "tsc2004", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tsc2004_idtable); + +#ifdef CONFIG_OF +static const struct of_device_id tsc2004_of_match[] = { + { .compatible = "ti,tsc2004" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tsc2004_of_match); +#endif + +static struct i2c_driver tsc2004_driver = { + .driver = { + .name = "tsc2004", + .of_match_table = of_match_ptr(tsc2004_of_match), + .pm = &tsc200x_pm_ops, + }, + .id_table = tsc2004_idtable, + .probe = tsc2004_probe, + .remove = tsc2004_remove, +}; +module_i2c_driver(tsc2004_driver); + +MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>"); +MODULE_DESCRIPTION("TSC2004 Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 0f65d02ee..b9f593dfd 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -2,9 +2,10 @@ * TSC2005 touchscreen driver * * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2015 QWERTY Embedded Design + * Copyright (C) 2015 EMAC Inc. * - * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com> - * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com> + * Based on original tsc2005.c by Lauri Leukkunen <lauri.leukkunen@nokia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,192 +16,32 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ -#include <linux/kernel.h> #include <linux/module.h> #include <linux/input.h> -#include <linux/input/touchscreen.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/pm.h> -#include <linux/of.h> #include <linux/spi/spi.h> -#include <linux/spi/tsc2005.h> -#include <linux/regulator/consumer.h> #include <linux/regmap.h> -#include <linux/gpio/consumer.h> - -/* - * The touchscreen interface operates as follows: - * - * 1) Pen is pressed against the touchscreen. - * 2) TSC2005 performs AD conversion. - * 3) After the conversion is done TSC2005 drives DAV line down. - * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled. - * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2 - * values. - * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up - * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms). - * 7) When the penup timer expires, there have not been touch or DAV interrupts - * during the last 40ms which means the pen has been lifted. - * - * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond - * after a configurable period (in ms) of activity. If esd_timeout is 0, the - * watchdog is disabled. - */ - -/* control byte 1 */ -#define TSC2005_CMD 0x80 -#define TSC2005_CMD_NORMAL 0x00 -#define TSC2005_CMD_STOP 0x01 -#define TSC2005_CMD_12BIT 0x04 - -/* control byte 0 */ -#define TSC2005_REG_READ 0x01 /* R/W access */ -#define TSC2005_REG_PND0 0x02 /* Power Not Down Control */ -#define TSC2005_REG_X (0x0 << 3) -#define TSC2005_REG_Y (0x1 << 3) -#define TSC2005_REG_Z1 (0x2 << 3) -#define TSC2005_REG_Z2 (0x3 << 3) -#define TSC2005_REG_AUX (0x4 << 3) -#define TSC2005_REG_TEMP1 (0x5 << 3) -#define TSC2005_REG_TEMP2 (0x6 << 3) -#define TSC2005_REG_STATUS (0x7 << 3) -#define TSC2005_REG_AUX_HIGH (0x8 << 3) -#define TSC2005_REG_AUX_LOW (0x9 << 3) -#define TSC2005_REG_TEMP_HIGH (0xA << 3) -#define TSC2005_REG_TEMP_LOW (0xB << 3) -#define TSC2005_REG_CFR0 (0xC << 3) -#define TSC2005_REG_CFR1 (0xD << 3) -#define TSC2005_REG_CFR2 (0xE << 3) -#define TSC2005_REG_CONV_FUNC (0xF << 3) - -/* configuration register 0 */ -#define TSC2005_CFR0_PRECHARGE_276US 0x0040 -#define TSC2005_CFR0_STABTIME_1MS 0x0300 -#define TSC2005_CFR0_CLOCK_1MHZ 0x1000 -#define TSC2005_CFR0_RESOLUTION12 0x2000 -#define TSC2005_CFR0_PENMODE 0x8000 -#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \ - TSC2005_CFR0_CLOCK_1MHZ | \ - TSC2005_CFR0_RESOLUTION12 | \ - TSC2005_CFR0_PRECHARGE_276US | \ - TSC2005_CFR0_PENMODE) - -/* bits common to both read and write of configuration register 0 */ -#define TSC2005_CFR0_RW_MASK 0x3fff - -/* configuration register 1 */ -#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003 -#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS - -/* configuration register 2 */ -#define TSC2005_CFR2_MAVE_Z 0x0004 -#define TSC2005_CFR2_MAVE_Y 0x0008 -#define TSC2005_CFR2_MAVE_X 0x0010 -#define TSC2005_CFR2_AVG_7 0x0800 -#define TSC2005_CFR2_MEDIUM_15 0x3000 -#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \ - TSC2005_CFR2_MAVE_Y | \ - TSC2005_CFR2_MAVE_Z | \ - TSC2005_CFR2_MEDIUM_15 | \ - TSC2005_CFR2_AVG_7) - -#define MAX_12BIT 0xfff -#define TSC2005_DEF_X_FUZZ 4 -#define TSC2005_DEF_Y_FUZZ 8 -#define TSC2005_DEF_P_FUZZ 2 -#define TSC2005_DEF_RESISTOR 280 - -#define TSC2005_SPI_MAX_SPEED_HZ 10000000 -#define TSC2005_PENUP_TIME_MS 40 - -static const struct regmap_range tsc2005_writable_ranges[] = { - regmap_reg_range(TSC2005_REG_AUX_HIGH, TSC2005_REG_CFR2), -}; - -static const struct regmap_access_table tsc2005_writable_table = { - .yes_ranges = tsc2005_writable_ranges, - .n_yes_ranges = ARRAY_SIZE(tsc2005_writable_ranges), -}; - -static struct regmap_config tsc2005_regmap_config = { - .reg_bits = 8, - .val_bits = 16, - .reg_stride = 0x08, - .max_register = 0x78, - .read_flag_mask = TSC2005_REG_READ, - .write_flag_mask = TSC2005_REG_PND0, - .wr_table = &tsc2005_writable_table, - .use_single_rw = true, -}; - -struct tsc2005_data { - u16 x; - u16 y; - u16 z1; - u16 z2; -} __packed; -#define TSC2005_DATA_REGS 4 - -struct tsc2005 { - struct spi_device *spi; - struct regmap *regmap; - - struct input_dev *idev; - char phys[32]; - - struct mutex mutex; - - /* raw copy of previous x,y,z */ - int in_x; - int in_y; - int in_z1; - int in_z2; - - spinlock_t lock; - struct timer_list penup_timer; +#include "tsc200x-core.h" - unsigned int esd_timeout; - struct delayed_work esd_work; - unsigned long last_valid_interrupt; - - unsigned int x_plate_ohm; - - bool opened; - bool suspended; - - bool pen_down; - - struct regulator *vio; - - struct gpio_desc *reset_gpio; - void (*set_reset)(bool enable); -}; - -static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) +static int tsc2005_cmd(struct device *dev, u8 cmd) { - u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; + u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd; struct spi_transfer xfer = { - .tx_buf = &tx, - .len = 1, - .bits_per_word = 8, + .tx_buf = &tx, + .len = 1, + .bits_per_word = 8, }; struct spi_message msg; + struct spi_device *spi = to_spi_device(dev); int error; spi_message_init(&msg); spi_message_add_tail(&xfer, &msg); - error = spi_sync(ts->spi, &msg); + error = spi_sync(spi, &msg); if (error) { - dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", + dev_err(dev, "%s: failed, command: %x, spi error: %d\n", __func__, cmd, error); return error; } @@ -208,382 +49,10 @@ static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) return 0; } -static void tsc2005_update_pen_state(struct tsc2005 *ts, - int x, int y, int pressure) -{ - if (pressure) { - input_report_abs(ts->idev, ABS_X, x); - input_report_abs(ts->idev, ABS_Y, y); - input_report_abs(ts->idev, ABS_PRESSURE, pressure); - if (!ts->pen_down) { - input_report_key(ts->idev, BTN_TOUCH, !!pressure); - ts->pen_down = true; - } - } else { - input_report_abs(ts->idev, ABS_PRESSURE, 0); - if (ts->pen_down) { - input_report_key(ts->idev, BTN_TOUCH, 0); - ts->pen_down = false; - } - } - input_sync(ts->idev); - dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, - pressure); -} - -static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) -{ - struct tsc2005 *ts = _ts; - unsigned long flags; - unsigned int pressure; - struct tsc2005_data tsdata; - int error; - - /* read the coordinates */ - error = regmap_bulk_read(ts->regmap, TSC2005_REG_X, &tsdata, - TSC2005_DATA_REGS); - if (unlikely(error)) - goto out; - - /* validate position */ - if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT)) - goto out; - - /* Skip reading if the pressure components are out of range */ - if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT)) - goto out; - if (unlikely(tsdata.z1 >= tsdata.z2)) - goto out; - - /* - * Skip point if this is a pen down with the exact same values as - * the value before pen-up - that implies SPI fed us stale data - */ - if (!ts->pen_down && - ts->in_x == tsdata.x && ts->in_y == tsdata.y && - ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) { - goto out; - } - - /* - * At this point we are happy we have a valid and useful reading. - * Remember it for later comparisons. We may now begin downsampling. - */ - ts->in_x = tsdata.x; - ts->in_y = tsdata.y; - ts->in_z1 = tsdata.z1; - ts->in_z2 = tsdata.z2; - - /* Compute touch pressure resistance using equation #1 */ - pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1; - pressure = pressure * ts->x_plate_ohm / 4096; - if (unlikely(pressure > MAX_12BIT)) - goto out; - - spin_lock_irqsave(&ts->lock, flags); - - tsc2005_update_pen_state(ts, tsdata.x, tsdata.y, pressure); - mod_timer(&ts->penup_timer, - jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS)); - - spin_unlock_irqrestore(&ts->lock, flags); - - ts->last_valid_interrupt = jiffies; -out: - return IRQ_HANDLED; -} - -static void tsc2005_penup_timer(unsigned long data) -{ - struct tsc2005 *ts = (struct tsc2005 *)data; - unsigned long flags; - - spin_lock_irqsave(&ts->lock, flags); - tsc2005_update_pen_state(ts, 0, 0, 0); - spin_unlock_irqrestore(&ts->lock, flags); -} - -static void tsc2005_start_scan(struct tsc2005 *ts) -{ - regmap_write(ts->regmap, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE); - regmap_write(ts->regmap, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE); - regmap_write(ts->regmap, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE); - tsc2005_cmd(ts, TSC2005_CMD_NORMAL); -} - -static void tsc2005_stop_scan(struct tsc2005 *ts) -{ - tsc2005_cmd(ts, TSC2005_CMD_STOP); -} - -static void tsc2005_set_reset(struct tsc2005 *ts, bool enable) -{ - if (ts->reset_gpio) - gpiod_set_value_cansleep(ts->reset_gpio, enable); - else if (ts->set_reset) - ts->set_reset(enable); -} - -/* must be called with ts->mutex held */ -static void __tsc2005_disable(struct tsc2005 *ts) -{ - tsc2005_stop_scan(ts); - - disable_irq(ts->spi->irq); - del_timer_sync(&ts->penup_timer); - - cancel_delayed_work_sync(&ts->esd_work); - - enable_irq(ts->spi->irq); -} - -/* must be called with ts->mutex held */ -static void __tsc2005_enable(struct tsc2005 *ts) -{ - tsc2005_start_scan(ts); - - if (ts->esd_timeout && (ts->set_reset || ts->reset_gpio)) { - ts->last_valid_interrupt = jiffies; - schedule_delayed_work(&ts->esd_work, - round_jiffies_relative( - msecs_to_jiffies(ts->esd_timeout))); - } - -} - -static ssize_t tsc2005_selftest_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct tsc2005 *ts = dev_get_drvdata(dev); - unsigned int temp_high; - unsigned int temp_high_orig; - unsigned int temp_high_test; - bool success = true; - int error; - - mutex_lock(&ts->mutex); - - /* - * Test TSC2005 communications via temp high register. - */ - __tsc2005_disable(ts); - - error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high_orig); - if (error) { - dev_warn(dev, "selftest failed: read error %d\n", error); - success = false; - goto out; - } - - temp_high_test = (temp_high_orig - 1) & MAX_12BIT; - - error = regmap_write(ts->regmap, TSC2005_REG_TEMP_HIGH, temp_high_test); - if (error) { - dev_warn(dev, "selftest failed: write error %d\n", error); - success = false; - goto out; - } - - error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high); - if (error) { - dev_warn(dev, "selftest failed: read error %d after write\n", - error); - success = false; - goto out; - } - - if (temp_high != temp_high_test) { - dev_warn(dev, "selftest failed: %d != %d\n", - temp_high, temp_high_test); - success = false; - } - - /* hardware reset */ - tsc2005_set_reset(ts, false); - usleep_range(100, 500); /* only 10us required */ - tsc2005_set_reset(ts, true); - - if (!success) - goto out; - - /* test that the reset really happened */ - error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high); - if (error) { - dev_warn(dev, "selftest failed: read error %d after reset\n", - error); - success = false; - goto out; - } - - if (temp_high != temp_high_orig) { - dev_warn(dev, "selftest failed after reset: %d != %d\n", - temp_high, temp_high_orig); - success = false; - } - -out: - __tsc2005_enable(ts); - mutex_unlock(&ts->mutex); - - return sprintf(buf, "%d\n", success); -} - -static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL); - -static struct attribute *tsc2005_attrs[] = { - &dev_attr_selftest.attr, - NULL -}; - -static umode_t tsc2005_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct tsc2005 *ts = dev_get_drvdata(dev); - umode_t mode = attr->mode; - - if (attr == &dev_attr_selftest.attr) { - if (!ts->set_reset && !ts->reset_gpio) - mode = 0; - } - - return mode; -} - -static const struct attribute_group tsc2005_attr_group = { - .is_visible = tsc2005_attr_is_visible, - .attrs = tsc2005_attrs, -}; - -static void tsc2005_esd_work(struct work_struct *work) -{ - struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work); - int error; - unsigned int r; - - if (!mutex_trylock(&ts->mutex)) { - /* - * If the mutex is taken, it means that disable or enable is in - * progress. In that case just reschedule the work. If the work - * is not needed, it will be canceled by disable. - */ - goto reschedule; - } - - if (time_is_after_jiffies(ts->last_valid_interrupt + - msecs_to_jiffies(ts->esd_timeout))) - goto out; - - /* We should be able to read register without disabling interrupts. */ - error = regmap_read(ts->regmap, TSC2005_REG_CFR0, &r); - if (!error && - !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) { - goto out; - } - - /* - * If we could not read our known value from configuration register 0 - * then we should reset the controller as if from power-up and start - * scanning again. - */ - dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n"); - - disable_irq(ts->spi->irq); - del_timer_sync(&ts->penup_timer); - - tsc2005_update_pen_state(ts, 0, 0, 0); - - tsc2005_set_reset(ts, false); - usleep_range(100, 500); /* only 10us required */ - tsc2005_set_reset(ts, true); - - enable_irq(ts->spi->irq); - tsc2005_start_scan(ts); - -out: - mutex_unlock(&ts->mutex); -reschedule: - /* re-arm the watchdog */ - schedule_delayed_work(&ts->esd_work, - round_jiffies_relative( - msecs_to_jiffies(ts->esd_timeout))); -} - -static int tsc2005_open(struct input_dev *input) -{ - struct tsc2005 *ts = input_get_drvdata(input); - - mutex_lock(&ts->mutex); - - if (!ts->suspended) - __tsc2005_enable(ts); - - ts->opened = true; - - mutex_unlock(&ts->mutex); - - return 0; -} - -static void tsc2005_close(struct input_dev *input) -{ - struct tsc2005 *ts = input_get_drvdata(input); - - mutex_lock(&ts->mutex); - - if (!ts->suspended) - __tsc2005_disable(ts); - - ts->opened = false; - - mutex_unlock(&ts->mutex); -} - static int tsc2005_probe(struct spi_device *spi) { - const struct tsc2005_platform_data *pdata = dev_get_platdata(&spi->dev); - struct device_node *np = spi->dev.of_node; - - struct tsc2005 *ts; - struct input_dev *input_dev; - unsigned int max_x = MAX_12BIT; - unsigned int max_y = MAX_12BIT; - unsigned int max_p = MAX_12BIT; - unsigned int fudge_x = TSC2005_DEF_X_FUZZ; - unsigned int fudge_y = TSC2005_DEF_Y_FUZZ; - unsigned int fudge_p = TSC2005_DEF_P_FUZZ; - unsigned int x_plate_ohm = TSC2005_DEF_RESISTOR; - unsigned int esd_timeout; int error; - if (!np && !pdata) { - dev_err(&spi->dev, "no platform data\n"); - return -ENODEV; - } - - if (spi->irq <= 0) { - dev_err(&spi->dev, "no irq\n"); - return -ENODEV; - } - - if (pdata) { - fudge_x = pdata->ts_x_fudge; - fudge_y = pdata->ts_y_fudge; - fudge_p = pdata->ts_pressure_fudge; - max_x = pdata->ts_x_max; - max_y = pdata->ts_y_max; - max_p = pdata->ts_pressure_max; - x_plate_ohm = pdata->ts_x_plate_ohm; - esd_timeout = pdata->esd_timeout_ms; - } else { - x_plate_ohm = TSC2005_DEF_RESISTOR; - of_property_read_u32(np, "ti,x-plate-ohms", &x_plate_ohm); - esd_timeout = 0; - of_property_read_u32(np, "ti,esd-recovery-timeout-ms", - &esd_timeout); - } - spi->mode = SPI_MODE_0; spi->bits_per_word = 8; if (!spi->max_speed_hz) @@ -593,175 +62,27 @@ static int tsc2005_probe(struct spi_device *spi) if (error) return error; - ts = devm_kzalloc(&spi->dev, sizeof(*ts), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - input_dev = devm_input_allocate_device(&spi->dev); - if (!input_dev) - return -ENOMEM; - - ts->spi = spi; - ts->idev = input_dev; - - ts->regmap = devm_regmap_init_spi(spi, &tsc2005_regmap_config); - if (IS_ERR(ts->regmap)) - return PTR_ERR(ts->regmap); - - ts->x_plate_ohm = x_plate_ohm; - ts->esd_timeout = esd_timeout; - - ts->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", - GPIOD_OUT_HIGH); - if (IS_ERR(ts->reset_gpio)) { - error = PTR_ERR(ts->reset_gpio); - dev_err(&spi->dev, "error acquiring reset gpio: %d\n", error); - return error; - } - - ts->vio = devm_regulator_get_optional(&spi->dev, "vio"); - if (IS_ERR(ts->vio)) { - error = PTR_ERR(ts->vio); - dev_err(&spi->dev, "vio regulator missing (%d)", error); - return error; - } - - if (!ts->reset_gpio && pdata) - ts->set_reset = pdata->set_reset; - - mutex_init(&ts->mutex); - - spin_lock_init(&ts->lock); - setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts); - - INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work); - - snprintf(ts->phys, sizeof(ts->phys), - "%s/input-ts", dev_name(&spi->dev)); - - input_dev->name = "TSC2005 touchscreen"; - input_dev->phys = ts->phys; - input_dev->id.bustype = BUS_SPI; - input_dev->dev.parent = &spi->dev; - input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0); - input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); - - if (np) - touchscreen_parse_properties(input_dev, false); - - input_dev->open = tsc2005_open; - input_dev->close = tsc2005_close; - - input_set_drvdata(input_dev, ts); - - /* Ensure the touchscreen is off */ - tsc2005_stop_scan(ts); - - error = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, - tsc2005_irq_thread, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "tsc2005", ts); - if (error) { - dev_err(&spi->dev, "Failed to request irq, err: %d\n", error); - return error; - } - - /* enable regulator for DT */ - if (ts->vio) { - error = regulator_enable(ts->vio); - if (error) - return error; - } - - dev_set_drvdata(&spi->dev, ts); - error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group); - if (error) { - dev_err(&spi->dev, - "Failed to create sysfs attributes, err: %d\n", error); - goto disable_regulator; - } - - error = input_register_device(ts->idev); - if (error) { - dev_err(&spi->dev, - "Failed to register input device, err: %d\n", error); - goto err_remove_sysfs; - } - - irq_set_irq_wake(spi->irq, 1); - return 0; - -err_remove_sysfs: - sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); -disable_regulator: - if (ts->vio) - regulator_disable(ts->vio); - return error; + return tsc200x_probe(&spi->dev, spi->irq, BUS_SPI, + devm_regmap_init_spi(spi, &tsc200x_regmap_config), + tsc2005_cmd); } static int tsc2005_remove(struct spi_device *spi) { - struct tsc2005 *ts = dev_get_drvdata(&spi->dev); - - sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); - - if (ts->vio) - regulator_disable(ts->vio); - - return 0; -} - -static int __maybe_unused tsc2005_suspend(struct device *dev) -{ - struct tsc2005 *ts = dev_get_drvdata(dev); - - mutex_lock(&ts->mutex); - - if (!ts->suspended && ts->opened) - __tsc2005_disable(ts); - - ts->suspended = true; - - mutex_unlock(&ts->mutex); - - return 0; -} - -static int __maybe_unused tsc2005_resume(struct device *dev) -{ - struct tsc2005 *ts = dev_get_drvdata(dev); - - mutex_lock(&ts->mutex); - - if (ts->suspended && ts->opened) - __tsc2005_enable(ts); - - ts->suspended = false; - - mutex_unlock(&ts->mutex); - - return 0; + return tsc200x_remove(&spi->dev); } -static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume); - static struct spi_driver tsc2005_driver = { .driver = { .name = "tsc2005", - .owner = THIS_MODULE, - .pm = &tsc2005_pm_ops, + .pm = &tsc200x_pm_ops, }, .probe = tsc2005_probe, .remove = tsc2005_remove, }; - module_spi_driver(tsc2005_driver); -MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); +MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>"); MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:tsc2005"); diff --git a/drivers/input/touchscreen/tsc200x-core.c b/drivers/input/touchscreen/tsc200x-core.c new file mode 100644 index 000000000..15240c1ee --- /dev/null +++ b/drivers/input/touchscreen/tsc200x-core.c @@ -0,0 +1,665 @@ +/* + * TSC2004/TSC2005 touchscreen driver core + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2015 QWERTY Embedded Design + * Copyright (C) 2015 EMAC Inc. + * + * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com> + * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/input/touchscreen.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/of.h> +#include <linux/spi/tsc2005.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/gpio/consumer.h> +#include "tsc200x-core.h" + +/* + * The touchscreen interface operates as follows: + * + * 1) Pen is pressed against the touchscreen. + * 2) TSC200X performs AD conversion. + * 3) After the conversion is done TSC200X drives DAV line down. + * 4) GPIO IRQ is received and tsc200x_irq_thread() is scheduled. + * 5) tsc200x_irq_thread() queues up a transfer to fetch the x, y, z1, z2 + * values. + * 6) tsc200x_irq_thread() reports coordinates to input layer and sets up + * tsc200x_penup_timer() to be called after TSC200X_PENUP_TIME_MS (40ms). + * 7) When the penup timer expires, there have not been touch or DAV interrupts + * during the last 40ms which means the pen has been lifted. + * + * ESD recovery via a hardware reset is done if the TSC200X doesn't respond + * after a configurable period (in ms) of activity. If esd_timeout is 0, the + * watchdog is disabled. + */ + +static const struct regmap_range tsc200x_writable_ranges[] = { + regmap_reg_range(TSC200X_REG_AUX_HIGH, TSC200X_REG_CFR2), +}; + +static const struct regmap_access_table tsc200x_writable_table = { + .yes_ranges = tsc200x_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(tsc200x_writable_ranges), +}; + +const struct regmap_config tsc200x_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .reg_stride = 0x08, + .max_register = 0x78, + .read_flag_mask = TSC200X_REG_READ, + .write_flag_mask = TSC200X_REG_PND0, + .wr_table = &tsc200x_writable_table, + .use_single_rw = true, +}; +EXPORT_SYMBOL_GPL(tsc200x_regmap_config); + +struct tsc200x_data { + u16 x; + u16 y; + u16 z1; + u16 z2; +} __packed; +#define TSC200X_DATA_REGS 4 + +struct tsc200x { + struct device *dev; + struct regmap *regmap; + __u16 bustype; + + struct input_dev *idev; + char phys[32]; + + struct mutex mutex; + + /* raw copy of previous x,y,z */ + int in_x; + int in_y; + int in_z1; + int in_z2; + + spinlock_t lock; + struct timer_list penup_timer; + + unsigned int esd_timeout; + struct delayed_work esd_work; + unsigned long last_valid_interrupt; + + unsigned int x_plate_ohm; + + bool opened; + bool suspended; + + bool pen_down; + + struct regulator *vio; + + struct gpio_desc *reset_gpio; + void (*set_reset)(bool enable); + int (*tsc200x_cmd)(struct device *dev, u8 cmd); + int irq; +}; + +static void tsc200x_update_pen_state(struct tsc200x *ts, + int x, int y, int pressure) +{ + if (pressure) { + input_report_abs(ts->idev, ABS_X, x); + input_report_abs(ts->idev, ABS_Y, y); + input_report_abs(ts->idev, ABS_PRESSURE, pressure); + if (!ts->pen_down) { + input_report_key(ts->idev, BTN_TOUCH, !!pressure); + ts->pen_down = true; + } + } else { + input_report_abs(ts->idev, ABS_PRESSURE, 0); + if (ts->pen_down) { + input_report_key(ts->idev, BTN_TOUCH, 0); + ts->pen_down = false; + } + } + input_sync(ts->idev); + dev_dbg(ts->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, + pressure); +} + +static irqreturn_t tsc200x_irq_thread(int irq, void *_ts) +{ + struct tsc200x *ts = _ts; + unsigned long flags; + unsigned int pressure; + struct tsc200x_data tsdata; + int error; + + /* read the coordinates */ + error = regmap_bulk_read(ts->regmap, TSC200X_REG_X, &tsdata, + TSC200X_DATA_REGS); + if (unlikely(error)) + goto out; + + /* validate position */ + if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT)) + goto out; + + /* Skip reading if the pressure components are out of range */ + if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT)) + goto out; + if (unlikely(tsdata.z1 >= tsdata.z2)) + goto out; + + /* + * Skip point if this is a pen down with the exact same values as + * the value before pen-up - that implies SPI fed us stale data + */ + if (!ts->pen_down && + ts->in_x == tsdata.x && ts->in_y == tsdata.y && + ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) { + goto out; + } + + /* + * At this point we are happy we have a valid and useful reading. + * Remember it for later comparisons. We may now begin downsampling. + */ + ts->in_x = tsdata.x; + ts->in_y = tsdata.y; + ts->in_z1 = tsdata.z1; + ts->in_z2 = tsdata.z2; + + /* Compute touch pressure resistance using equation #1 */ + pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1; + pressure = pressure * ts->x_plate_ohm / 4096; + if (unlikely(pressure > MAX_12BIT)) + goto out; + + spin_lock_irqsave(&ts->lock, flags); + + tsc200x_update_pen_state(ts, tsdata.x, tsdata.y, pressure); + mod_timer(&ts->penup_timer, + jiffies + msecs_to_jiffies(TSC200X_PENUP_TIME_MS)); + + spin_unlock_irqrestore(&ts->lock, flags); + + ts->last_valid_interrupt = jiffies; +out: + return IRQ_HANDLED; +} + +static void tsc200x_penup_timer(unsigned long data) +{ + struct tsc200x *ts = (struct tsc200x *)data; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + tsc200x_update_pen_state(ts, 0, 0, 0); + spin_unlock_irqrestore(&ts->lock, flags); +} + +static void tsc200x_start_scan(struct tsc200x *ts) +{ + regmap_write(ts->regmap, TSC200X_REG_CFR0, TSC200X_CFR0_INITVALUE); + regmap_write(ts->regmap, TSC200X_REG_CFR1, TSC200X_CFR1_INITVALUE); + regmap_write(ts->regmap, TSC200X_REG_CFR2, TSC200X_CFR2_INITVALUE); + ts->tsc200x_cmd(ts->dev, TSC200X_CMD_NORMAL); +} + +static void tsc200x_stop_scan(struct tsc200x *ts) +{ + ts->tsc200x_cmd(ts->dev, TSC200X_CMD_STOP); +} + +static void tsc200x_set_reset(struct tsc200x *ts, bool enable) +{ + if (ts->reset_gpio) + gpiod_set_value_cansleep(ts->reset_gpio, enable); + else if (ts->set_reset) + ts->set_reset(enable); +} + +/* must be called with ts->mutex held */ +static void __tsc200x_disable(struct tsc200x *ts) +{ + tsc200x_stop_scan(ts); + + disable_irq(ts->irq); + del_timer_sync(&ts->penup_timer); + + cancel_delayed_work_sync(&ts->esd_work); + + enable_irq(ts->irq); +} + +/* must be called with ts->mutex held */ +static void __tsc200x_enable(struct tsc200x *ts) +{ + tsc200x_start_scan(ts); + + if (ts->esd_timeout && (ts->set_reset || ts->reset_gpio)) { + ts->last_valid_interrupt = jiffies; + schedule_delayed_work(&ts->esd_work, + round_jiffies_relative( + msecs_to_jiffies(ts->esd_timeout))); + } +} + +static ssize_t tsc200x_selftest_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tsc200x *ts = dev_get_drvdata(dev); + unsigned int temp_high; + unsigned int temp_high_orig; + unsigned int temp_high_test; + bool success = true; + int error; + + mutex_lock(&ts->mutex); + + /* + * Test TSC200X communications via temp high register. + */ + __tsc200x_disable(ts); + + error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high_orig); + if (error) { + dev_warn(dev, "selftest failed: read error %d\n", error); + success = false; + goto out; + } + + temp_high_test = (temp_high_orig - 1) & MAX_12BIT; + + error = regmap_write(ts->regmap, TSC200X_REG_TEMP_HIGH, temp_high_test); + if (error) { + dev_warn(dev, "selftest failed: write error %d\n", error); + success = false; + goto out; + } + + error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after write\n", + error); + success = false; + goto out; + } + + if (temp_high != temp_high_test) { + dev_warn(dev, "selftest failed: %d != %d\n", + temp_high, temp_high_test); + success = false; + } + + /* hardware reset */ + tsc200x_set_reset(ts, false); + usleep_range(100, 500); /* only 10us required */ + tsc200x_set_reset(ts, true); + + if (!success) + goto out; + + /* test that the reset really happened */ + error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after reset\n", + error); + success = false; + goto out; + } + + if (temp_high != temp_high_orig) { + dev_warn(dev, "selftest failed after reset: %d != %d\n", + temp_high, temp_high_orig); + success = false; + } + +out: + __tsc200x_enable(ts); + mutex_unlock(&ts->mutex); + + return sprintf(buf, "%d\n", success); +} + +static DEVICE_ATTR(selftest, S_IRUGO, tsc200x_selftest_show, NULL); + +static struct attribute *tsc200x_attrs[] = { + &dev_attr_selftest.attr, + NULL +}; + +static umode_t tsc200x_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tsc200x *ts = dev_get_drvdata(dev); + umode_t mode = attr->mode; + + if (attr == &dev_attr_selftest.attr) { + if (!ts->set_reset && !ts->reset_gpio) + mode = 0; + } + + return mode; +} + +static const struct attribute_group tsc200x_attr_group = { + .is_visible = tsc200x_attr_is_visible, + .attrs = tsc200x_attrs, +}; + +static void tsc200x_esd_work(struct work_struct *work) +{ + struct tsc200x *ts = container_of(work, struct tsc200x, esd_work.work); + int error; + unsigned int r; + + if (!mutex_trylock(&ts->mutex)) { + /* + * If the mutex is taken, it means that disable or enable is in + * progress. In that case just reschedule the work. If the work + * is not needed, it will be canceled by disable. + */ + goto reschedule; + } + + if (time_is_after_jiffies(ts->last_valid_interrupt + + msecs_to_jiffies(ts->esd_timeout))) + goto out; + + /* We should be able to read register without disabling interrupts. */ + error = regmap_read(ts->regmap, TSC200X_REG_CFR0, &r); + if (!error && + !((r ^ TSC200X_CFR0_INITVALUE) & TSC200X_CFR0_RW_MASK)) { + goto out; + } + + /* + * If we could not read our known value from configuration register 0 + * then we should reset the controller as if from power-up and start + * scanning again. + */ + dev_info(ts->dev, "TSC200X not responding - resetting\n"); + + disable_irq(ts->irq); + del_timer_sync(&ts->penup_timer); + + tsc200x_update_pen_state(ts, 0, 0, 0); + + tsc200x_set_reset(ts, false); + usleep_range(100, 500); /* only 10us required */ + tsc200x_set_reset(ts, true); + + enable_irq(ts->irq); + tsc200x_start_scan(ts); + +out: + mutex_unlock(&ts->mutex); +reschedule: + /* re-arm the watchdog */ + schedule_delayed_work(&ts->esd_work, + round_jiffies_relative( + msecs_to_jiffies(ts->esd_timeout))); +} + +static int tsc200x_open(struct input_dev *input) +{ + struct tsc200x *ts = input_get_drvdata(input); + + mutex_lock(&ts->mutex); + + if (!ts->suspended) + __tsc200x_enable(ts); + + ts->opened = true; + + mutex_unlock(&ts->mutex); + + return 0; +} + +static void tsc200x_close(struct input_dev *input) +{ + struct tsc200x *ts = input_get_drvdata(input); + + mutex_lock(&ts->mutex); + + if (!ts->suspended) + __tsc200x_disable(ts); + + ts->opened = false; + + mutex_unlock(&ts->mutex); +} + +int tsc200x_probe(struct device *dev, int irq, __u16 bustype, + struct regmap *regmap, + int (*tsc200x_cmd)(struct device *dev, u8 cmd)) +{ + const struct tsc2005_platform_data *pdata = dev_get_platdata(dev); + struct device_node *np = dev->of_node; + + struct tsc200x *ts; + struct input_dev *input_dev; + unsigned int max_x = MAX_12BIT; + unsigned int max_y = MAX_12BIT; + unsigned int max_p = MAX_12BIT; + unsigned int fudge_x = TSC200X_DEF_X_FUZZ; + unsigned int fudge_y = TSC200X_DEF_Y_FUZZ; + unsigned int fudge_p = TSC200X_DEF_P_FUZZ; + unsigned int x_plate_ohm = TSC200X_DEF_RESISTOR; + unsigned int esd_timeout; + int error; + + if (!np && !pdata) { + dev_err(dev, "no platform data\n"); + return -ENODEV; + } + + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -ENODEV; + } + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + if (!tsc200x_cmd) { + dev_err(dev, "no cmd function\n"); + return -ENODEV; + } + + if (pdata) { + fudge_x = pdata->ts_x_fudge; + fudge_y = pdata->ts_y_fudge; + fudge_p = pdata->ts_pressure_fudge; + max_x = pdata->ts_x_max; + max_y = pdata->ts_y_max; + max_p = pdata->ts_pressure_max; + x_plate_ohm = pdata->ts_x_plate_ohm; + esd_timeout = pdata->esd_timeout_ms; + } else { + x_plate_ohm = TSC200X_DEF_RESISTOR; + of_property_read_u32(np, "ti,x-plate-ohms", &x_plate_ohm); + esd_timeout = 0; + of_property_read_u32(np, "ti,esd-recovery-timeout-ms", + &esd_timeout); + } + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return -ENOMEM; + + ts->irq = irq; + ts->dev = dev; + ts->idev = input_dev; + ts->regmap = regmap; + ts->tsc200x_cmd = tsc200x_cmd; + ts->x_plate_ohm = x_plate_ohm; + ts->esd_timeout = esd_timeout; + + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + dev_err(dev, "error acquiring reset gpio: %d\n", error); + return error; + } + + ts->vio = devm_regulator_get_optional(dev, "vio"); + if (IS_ERR(ts->vio)) { + error = PTR_ERR(ts->vio); + dev_err(dev, "vio regulator missing (%d)", error); + return error; + } + + if (!ts->reset_gpio && pdata) + ts->set_reset = pdata->set_reset; + + mutex_init(&ts->mutex); + + spin_lock_init(&ts->lock); + setup_timer(&ts->penup_timer, tsc200x_penup_timer, (unsigned long)ts); + + INIT_DELAYED_WORK(&ts->esd_work, tsc200x_esd_work); + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input-ts", dev_name(dev)); + + input_dev->name = "TSC200X touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = bustype; + input_dev->dev.parent = dev; + input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0); + input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); + + if (np) + touchscreen_parse_properties(input_dev, false); + + input_dev->open = tsc200x_open; + input_dev->close = tsc200x_close; + + input_set_drvdata(input_dev, ts); + + /* Ensure the touchscreen is off */ + tsc200x_stop_scan(ts); + + error = devm_request_threaded_irq(dev, irq, NULL, + tsc200x_irq_thread, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "tsc200x", ts); + if (error) { + dev_err(dev, "Failed to request irq, err: %d\n", error); + return error; + } + + /* enable regulator for DT */ + if (ts->vio) { + error = regulator_enable(ts->vio); + if (error) + return error; + } + + dev_set_drvdata(dev, ts); + error = sysfs_create_group(&dev->kobj, &tsc200x_attr_group); + if (error) { + dev_err(dev, + "Failed to create sysfs attributes, err: %d\n", error); + goto disable_regulator; + } + + error = input_register_device(ts->idev); + if (error) { + dev_err(dev, + "Failed to register input device, err: %d\n", error); + goto err_remove_sysfs; + } + + irq_set_irq_wake(irq, 1); + return 0; + +err_remove_sysfs: + sysfs_remove_group(&dev->kobj, &tsc200x_attr_group); +disable_regulator: + if (ts->vio) + regulator_disable(ts->vio); + return error; +} +EXPORT_SYMBOL_GPL(tsc200x_probe); + +int tsc200x_remove(struct device *dev) +{ + struct tsc200x *ts = dev_get_drvdata(dev); + + sysfs_remove_group(&dev->kobj, &tsc200x_attr_group); + + if (ts->vio) + regulator_disable(ts->vio); + + return 0; +} +EXPORT_SYMBOL_GPL(tsc200x_remove); + +static int __maybe_unused tsc200x_suspend(struct device *dev) +{ + struct tsc200x *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->mutex); + + if (!ts->suspended && ts->opened) + __tsc200x_disable(ts); + + ts->suspended = true; + + mutex_unlock(&ts->mutex); + + return 0; +} + +static int __maybe_unused tsc200x_resume(struct device *dev) +{ + struct tsc200x *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->mutex); + + if (ts->suspended && ts->opened) + __tsc200x_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->mutex); + + return 0; +} + +SIMPLE_DEV_PM_OPS(tsc200x_pm_ops, tsc200x_suspend, tsc200x_resume); +EXPORT_SYMBOL_GPL(tsc200x_pm_ops); + +MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); +MODULE_DESCRIPTION("TSC200x Touchscreen Driver Core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc200x-core.h b/drivers/input/touchscreen/tsc200x-core.h new file mode 100644 index 000000000..7a482d102 --- /dev/null +++ b/drivers/input/touchscreen/tsc200x-core.h @@ -0,0 +1,78 @@ +#ifndef _TSC200X_CORE_H +#define _TSC200X_CORE_H + +/* control byte 1 */ +#define TSC200X_CMD 0x80 +#define TSC200X_CMD_NORMAL 0x00 +#define TSC200X_CMD_STOP 0x01 +#define TSC200X_CMD_12BIT 0x04 + +/* control byte 0 */ +#define TSC200X_REG_READ 0x01 /* R/W access */ +#define TSC200X_REG_PND0 0x02 /* Power Not Down Control */ +#define TSC200X_REG_X (0x0 << 3) +#define TSC200X_REG_Y (0x1 << 3) +#define TSC200X_REG_Z1 (0x2 << 3) +#define TSC200X_REG_Z2 (0x3 << 3) +#define TSC200X_REG_AUX (0x4 << 3) +#define TSC200X_REG_TEMP1 (0x5 << 3) +#define TSC200X_REG_TEMP2 (0x6 << 3) +#define TSC200X_REG_STATUS (0x7 << 3) +#define TSC200X_REG_AUX_HIGH (0x8 << 3) +#define TSC200X_REG_AUX_LOW (0x9 << 3) +#define TSC200X_REG_TEMP_HIGH (0xA << 3) +#define TSC200X_REG_TEMP_LOW (0xB << 3) +#define TSC200X_REG_CFR0 (0xC << 3) +#define TSC200X_REG_CFR1 (0xD << 3) +#define TSC200X_REG_CFR2 (0xE << 3) +#define TSC200X_REG_CONV_FUNC (0xF << 3) + +/* configuration register 0 */ +#define TSC200X_CFR0_PRECHARGE_276US 0x0040 +#define TSC200X_CFR0_STABTIME_1MS 0x0300 +#define TSC200X_CFR0_CLOCK_1MHZ 0x1000 +#define TSC200X_CFR0_RESOLUTION12 0x2000 +#define TSC200X_CFR0_PENMODE 0x8000 +#define TSC200X_CFR0_INITVALUE (TSC200X_CFR0_STABTIME_1MS | \ + TSC200X_CFR0_CLOCK_1MHZ | \ + TSC200X_CFR0_RESOLUTION12 | \ + TSC200X_CFR0_PRECHARGE_276US | \ + TSC200X_CFR0_PENMODE) + +/* bits common to both read and write of configuration register 0 */ +#define TSC200X_CFR0_RW_MASK 0x3fff + +/* configuration register 1 */ +#define TSC200X_CFR1_BATCHDELAY_4MS 0x0003 +#define TSC200X_CFR1_INITVALUE TSC200X_CFR1_BATCHDELAY_4MS + +/* configuration register 2 */ +#define TSC200X_CFR2_MAVE_Z 0x0004 +#define TSC200X_CFR2_MAVE_Y 0x0008 +#define TSC200X_CFR2_MAVE_X 0x0010 +#define TSC200X_CFR2_AVG_7 0x0800 +#define TSC200X_CFR2_MEDIUM_15 0x3000 +#define TSC200X_CFR2_INITVALUE (TSC200X_CFR2_MAVE_X | \ + TSC200X_CFR2_MAVE_Y | \ + TSC200X_CFR2_MAVE_Z | \ + TSC200X_CFR2_MEDIUM_15 | \ + TSC200X_CFR2_AVG_7) + +#define MAX_12BIT 0xfff +#define TSC200X_DEF_X_FUZZ 4 +#define TSC200X_DEF_Y_FUZZ 8 +#define TSC200X_DEF_P_FUZZ 2 +#define TSC200X_DEF_RESISTOR 280 + +#define TSC2005_SPI_MAX_SPEED_HZ 10000000 +#define TSC200X_PENUP_TIME_MS 40 + +extern const struct regmap_config tsc200x_regmap_config; +extern const struct dev_pm_ops tsc200x_pm_ops; + +int tsc200x_probe(struct device *dev, int irq, __u16 bustype, + struct regmap *regmap, + int (*tsc200x_cmd)(struct device *dev, u8 cmd)); +int tsc200x_remove(struct device *dev); + +#endif diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 2792ca397..bab3c6acf 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -80,7 +80,8 @@ struct w8001_touch_query { */ struct w8001 { - struct input_dev *dev; + struct input_dev *pen_dev; + struct input_dev *touch_dev; struct serio *serio; struct completion cmd_done; int id; @@ -95,7 +96,10 @@ struct w8001 { u16 max_touch_y; u16 max_pen_x; u16 max_pen_y; - char name[64]; + char pen_name[64]; + char touch_name[64]; + int open_count; + struct mutex mutex; }; static void parse_pen_data(u8 *data, struct w8001_coord *coord) @@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001, static void parse_multi_touch(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned char *data = w8001->data; unsigned int x, y; int i; @@ -151,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001) bool touch = data[0] & (1 << i); input_mt_slot(dev, i); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); if (touch) { x = (data[6 * i + 1] << 7) | data[6 * i + 2]; y = (data[6 * i + 3] << 7) | data[6 * i + 4]; @@ -207,7 +210,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query) static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->pen_dev; /* * We have 1 bit for proximity (rdy) and 3 bits for tip, side, @@ -233,11 +236,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) break; case BTN_TOOL_FINGER: - input_report_key(dev, BTN_TOUCH, 0); - input_report_key(dev, BTN_TOOL_FINGER, 0); - input_sync(dev); - /* fall through */ - case KEY_RESERVED: w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; break; @@ -261,7 +259,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) { - struct input_dev *dev = w8001->dev; + struct input_dev *dev = w8001->touch_dev; unsigned int x = coord->x; unsigned int y = coord->y; @@ -271,7 +269,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); input_report_key(dev, BTN_TOUCH, coord->tsw); - input_report_key(dev, BTN_TOOL_FINGER, coord->tsw); input_sync(dev); @@ -369,22 +366,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command, static int w8001_open(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); + int err; - return w8001_command(w8001, W8001_CMD_START, false); + err = mutex_lock_interruptible(&w8001->mutex); + if (err) + return err; + + if (w8001->open_count++ == 0) { + err = w8001_command(w8001, W8001_CMD_START, false); + if (err) + w8001->open_count--; + } + + mutex_unlock(&w8001->mutex); + return err; } static void w8001_close(struct input_dev *dev) { struct w8001 *w8001 = input_get_drvdata(dev); - w8001_command(w8001, W8001_CMD_STOP, false); + mutex_lock(&w8001->mutex); + + if (--w8001->open_count == 0) + w8001_command(w8001, W8001_CMD_STOP, false); + + mutex_unlock(&w8001->mutex); } -static int w8001_setup(struct w8001 *w8001) +static int w8001_detect(struct w8001 *w8001) { - struct input_dev *dev = w8001->dev; - struct w8001_coord coord; - struct w8001_touch_query touch; int error; error = w8001_command(w8001, W8001_CMD_STOP, false); @@ -393,105 +404,145 @@ static int w8001_setup(struct w8001 *w8001) msleep(250); /* wait 250ms before querying the device */ - dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); + return 0; +} - __set_bit(INPUT_PROP_DIRECT, dev->propbit); +static int w8001_setup_pen(struct w8001 *w8001, char *basename, + size_t basename_sz) +{ + struct input_dev *dev = w8001->pen_dev; + struct w8001_coord coord; + int error; /* penabled? */ error = w8001_command(w8001, W8001_CMD_QUERY, true); - if (!error) { - __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_PEN, dev->keybit); - __set_bit(BTN_TOOL_RUBBER, dev->keybit); - __set_bit(BTN_STYLUS, dev->keybit); - __set_bit(BTN_STYLUS2, dev->keybit); - - parse_pen_data(w8001->response, &coord); - w8001->max_pen_x = coord.x; - w8001->max_pen_y = coord.y; - - input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); - input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); - input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); - input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); - input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); - if (coord.tilt_x && coord.tilt_y) { - input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); - input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); - } - w8001->id = 0x90; - strlcat(w8001->name, " Penabled", sizeof(w8001->name)); + if (error) + return error; + + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_PEN, dev->keybit); + __set_bit(BTN_TOOL_RUBBER, dev->keybit); + __set_bit(BTN_STYLUS, dev->keybit); + __set_bit(BTN_STYLUS2, dev->keybit); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); + + parse_pen_data(w8001->response, &coord); + w8001->max_pen_x = coord.x; + w8001->max_pen_y = coord.y; + + input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); + input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); + input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); + input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); + if (coord.tilt_x && coord.tilt_y) { + input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); + input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); } + w8001->id = 0x90; + strlcat(basename, " Penabled", basename_sz); + + return 0; +} + +static int w8001_setup_touch(struct w8001 *w8001, char *basename, + size_t basename_sz) +{ + struct input_dev *dev = w8001->touch_dev; + struct w8001_touch_query touch; + int error; + + /* Touch enabled? */ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); - + if (error) + return error; /* * Some non-touch devices may reply to the touch query. But their * second byte is empty, which indicates touch is not supported. */ - if (!error && w8001->response[1]) { - __set_bit(BTN_TOUCH, dev->keybit); - __set_bit(BTN_TOOL_FINGER, dev->keybit); - - parse_touchquery(w8001->response, &touch); - w8001->max_touch_x = touch.x; - w8001->max_touch_y = touch.y; - - if (w8001->max_pen_x && w8001->max_pen_y) { - /* if pen is supported scale to pen maximum */ - touch.x = w8001->max_pen_x; - touch.y = w8001->max_pen_y; - touch.panel_res = W8001_PEN_RESOLUTION; - } + if (!w8001->response[1]) + return -ENXIO; - input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); - input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); - input_abs_set_res(dev, ABS_X, touch.panel_res); - input_abs_set_res(dev, ABS_Y, touch.panel_res); - - switch (touch.sensor_id) { - case 0: - case 2: - w8001->pktlen = W8001_PKTLEN_TOUCH93; - w8001->id = 0x93; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); - break; + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_ABS, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); - case 1: - case 3: - case 4: - w8001->pktlen = W8001_PKTLEN_TOUCH9A; - strlcat(w8001->name, " 1FG", sizeof(w8001->name)); - w8001->id = 0x9a; - break; + parse_touchquery(w8001->response, &touch); + w8001->max_touch_x = touch.x; + w8001->max_touch_y = touch.y; - case 5: - w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - - input_mt_init_slots(dev, 2, 0); - input_set_abs_params(dev, ABS_MT_POSITION_X, - 0, touch.x, 0, 0); - input_set_abs_params(dev, ABS_MT_POSITION_Y, - 0, touch.y, 0, 0); - input_set_abs_params(dev, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); - - strlcat(w8001->name, " 2FG", sizeof(w8001->name)); - if (w8001->max_pen_x && w8001->max_pen_y) - w8001->id = 0xE3; - else - w8001->id = 0xE2; - break; - } + if (w8001->max_pen_x && w8001->max_pen_y) { + /* if pen is supported scale to pen maximum */ + touch.x = w8001->max_pen_x; + touch.y = w8001->max_pen_y; + touch.panel_res = W8001_PEN_RESOLUTION; + } + + input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); + input_abs_set_res(dev, ABS_X, touch.panel_res); + input_abs_set_res(dev, ABS_Y, touch.panel_res); + + switch (touch.sensor_id) { + case 0: + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + w8001->id = 0x93; + strlcat(basename, " 1FG", basename_sz); + break; + + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; + strlcat(basename, " 1FG", basename_sz); + w8001->id = 0x9a; + break; + + case 5: + w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + input_mt_init_slots(dev, 2, 0); + input_set_abs_params(dev, ABS_MT_POSITION_X, + 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, + 0, touch.y, 0, 0); + + strlcat(basename, " 2FG", basename_sz); + if (w8001->max_pen_x && w8001->max_pen_y) + w8001->id = 0xE3; + else + w8001->id = 0xE2; + break; } - strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + strlcat(basename, " Touchscreen", basename_sz); return 0; } +static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001, + struct serio *serio) +{ + dev->phys = w8001->phys; + dev->id.bustype = BUS_RS232; + dev->id.product = w8001->id; + dev->id.vendor = 0x056a; + dev->id.version = 0x0100; + dev->open = w8001_open; + dev->close = w8001_close; + + dev->dev.parent = &serio->dev; + + input_set_drvdata(dev, w8001); +} + /* * w8001_disconnect() is the opposite of w8001_connect() */ @@ -502,7 +553,10 @@ static void w8001_disconnect(struct serio *serio) serio_close(serio); - input_unregister_device(w8001->dev); + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); + if (w8001->touch_dev) + input_unregister_device(w8001->touch_dev); kfree(w8001); serio_set_drvdata(serio, NULL); @@ -517,18 +571,23 @@ static void w8001_disconnect(struct serio *serio) static int w8001_connect(struct serio *serio, struct serio_driver *drv) { struct w8001 *w8001; - struct input_dev *input_dev; - int err; + struct input_dev *input_dev_pen; + struct input_dev *input_dev_touch; + char basename[64]; + int err, err_pen, err_touch; w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!w8001 || !input_dev) { + input_dev_pen = input_allocate_device(); + input_dev_touch = input_allocate_device(); + if (!w8001 || !input_dev_pen || !input_dev_touch) { err = -ENOMEM; goto fail1; } w8001->serio = serio; - w8001->dev = input_dev; + w8001->pen_dev = input_dev_pen; + w8001->touch_dev = input_dev_touch; + mutex_init(&w8001->mutex); init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); @@ -537,35 +596,67 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail2; - err = w8001_setup(w8001); + err = w8001_detect(w8001); if (err) goto fail3; - input_dev->name = w8001->name; - input_dev->phys = w8001->phys; - input_dev->id.product = w8001->id; - input_dev->id.bustype = BUS_RS232; - input_dev->id.vendor = 0x056a; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &serio->dev; + /* For backwards-compatibility we compose the basename based on + * capabilities and then just append the tool type + */ + strlcpy(basename, "Wacom Serial", sizeof(basename)); + + err_pen = w8001_setup_pen(w8001, basename, sizeof(basename)); + err_touch = w8001_setup_touch(w8001, basename, sizeof(basename)); + if (err_pen && err_touch) { + err = -ENXIO; + goto fail3; + } + + if (!err_pen) { + strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name)); + strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name)); + input_dev_pen->name = w8001->pen_name; - input_dev->open = w8001_open; - input_dev->close = w8001_close; + w8001_set_devdata(input_dev_pen, w8001, serio); - input_set_drvdata(input_dev, w8001); + err = input_register_device(w8001->pen_dev); + if (err) + goto fail3; + } else { + input_free_device(input_dev_pen); + input_dev_pen = NULL; + w8001->pen_dev = NULL; + } - err = input_register_device(w8001->dev); - if (err) - goto fail3; + if (!err_touch) { + strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name)); + strlcat(w8001->touch_name, " Finger", + sizeof(w8001->touch_name)); + input_dev_touch->name = w8001->touch_name; + + w8001_set_devdata(input_dev_touch, w8001, serio); + + err = input_register_device(w8001->touch_dev); + if (err) + goto fail4; + } else { + input_free_device(input_dev_touch); + input_dev_touch = NULL; + w8001->touch_dev = NULL; + } return 0; +fail4: + if (w8001->pen_dev) + input_unregister_device(w8001->pen_dev); fail3: serio_close(serio); fail2: serio_set_drvdata(serio, NULL); fail1: - input_free_device(input_dev); + input_free_device(input_dev_pen); + input_free_device(input_dev_touch); kfree(w8001); return err; } diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 781d0f830..9bbadaaf6 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -599,13 +599,8 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id) static int zforce_input_open(struct input_dev *dev) { struct zforce_ts *ts = input_get_drvdata(dev); - int ret; - ret = zforce_start(ts); - if (ret) - return ret; - - return 0; + return zforce_start(ts); } static void zforce_input_close(struct input_dev *dev) |