Adds support for the i2c based tsc2004. Support was added to the tsc2005 driver due to the similarity of the devices. Signed-off-by: Michael Welling <mwelling@xxxxxxxx> --- .../bindings/input/touchscreen/tsc2004.txt | 38 ++++ drivers/input/touchscreen/Kconfig | 5 +- drivers/input/touchscreen/tsc2005.c | 206 ++++++++++++++++----- 3 files changed, 204 insertions(+), 45 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/tsc2004.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/tsc2004.txt b/Documentation/devicetree/bindings/input/touchscreen/tsc2004.txt new file mode 100644 index 0000000..14a37fb --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/tsc2004.txt @@ -0,0 +1,38 @@ +* Texas Instruments tsc2004 touchscreen controller + +Required properties: + - compatible : "ti,tsc2004" + - interrupts : IRQ specifier + - vio-supply : Regulator specifier + +Optional properties: + - reset-gpios : GPIO specifier + - ti,x-plate-ohms : integer, resistance of the touchscreen's X plates + in ohm (defaults to 280) + - ti,esd-recovery-timeout-ms : integer, if the touchscreen does not respond after + the configured time (in milli seconds), the driver + will reset it. This is disabled by default. + - properties defined in touchscreen.txt + +Example: + +&i2c3 { + tsc2004@48 { + compatible = "ti,tsc2004"; + reg = <0x48>; + vio-supply = <&vio>; + + reset-gpios = <&gpio4 8 GPIO_ACTIVE_HIGH>; + interrupts-extended = <&gpio1 27 IRQ_TYPE_EDGE_RISING>; + + touchscreen-fuzz-x = <4>; + touchscreen-fuzz-y = <7>; + touchscreen-fuzz-pressure = <2>; + touchscreen-size-x = <4096>; + touchscreen-size-y = <4096>; + touchscreen-max-pressure = <2048>; + + ti,x-plate-ohms = <280>; + ti,esd-recovery-timeout-ms = <8000>; + }; +} diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 80cc698..7f311d7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -940,9 +940,10 @@ config TOUCHSCREEN_TSC_SERIO module will be called tsc40. config TOUCHSCREEN_TSC2005 - tristate "TSC2005 based touchscreens" - depends on SPI_MASTER + tristate "TSC2004/TSC2005 based touchscreens" + depends on SPI_MASTER || I2C select REGMAP_SPI + select REGMAP_I2C help Say Y here if you have a TSC2005 based touchscreen. diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 0f65d02..08decd4 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -30,6 +30,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/of.h> +#include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/spi/tsc2005.h> #include <linux/regulator/consumer.h> @@ -151,6 +152,8 @@ struct tsc2005_data { struct tsc2005 { struct spi_device *spi; + struct i2c_client *i2c; + struct device *dev; struct regmap *regmap; struct input_dev *idev; @@ -182,9 +185,11 @@ struct tsc2005 { struct gpio_desc *reset_gpio; void (*set_reset)(bool enable); + + int irq; }; -static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) +static int tsc2005_cmd_spi(struct tsc2005 *ts, u8 cmd) { u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; struct spi_transfer xfer = { @@ -200,7 +205,7 @@ static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) error = spi_sync(ts->spi, &msg); if (error) { - dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", + dev_err(ts->dev, "%s: failed, command: %x, spi error: %d\n", __func__, cmd, error); return error; } @@ -208,6 +213,32 @@ static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) return 0; } +static int tsc2005_cmd_i2c(struct tsc2005 *ts, u8 cmd) +{ + u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; + s32 data; + + data = i2c_smbus_write_byte(ts->i2c, tx); + if (data < 0) { + dev_err(&ts->dev, "%s: failed, command: %x i2c error: %d\n", + __func__, cmd, data); + return data; + } + + return 0; +} + +static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) +{ + if (ts->spi) + return tsc2005_cmd_spi(ts, cmd); + + if (ts->i2c) + return tsc2005_cmd_i2c(ts, cmd); + + return -ENODEV; +} + static void tsc2005_update_pen_state(struct tsc2005 *ts, int x, int y, int pressure) { @@ -227,7 +258,7 @@ static void tsc2005_update_pen_state(struct tsc2005 *ts, } } input_sync(ts->idev); - dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, + dev_dbg(ts->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, pressure); } @@ -329,12 +360,12 @@ static void __tsc2005_disable(struct tsc2005 *ts) { tsc2005_stop_scan(ts); - disable_irq(ts->spi->irq); + disable_irq(ts->irq); del_timer_sync(&ts->penup_timer); cancel_delayed_work_sync(&ts->esd_work); - enable_irq(ts->spi->irq); + enable_irq(ts->irq); } /* must be called with ts->mutex held */ @@ -487,9 +518,9 @@ static void tsc2005_esd_work(struct work_struct *work) * 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"); + dev_info(ts->dev, "TSC2005 not responding - resetting\n"); - disable_irq(ts->spi->irq); + disable_irq(ts->irq); del_timer_sync(&ts->penup_timer); tsc2005_update_pen_state(ts, 0, 0, 0); @@ -498,7 +529,7 @@ static void tsc2005_esd_work(struct work_struct *work) usleep_range(100, 500); /* only 10us required */ tsc2005_set_reset(ts, true); - enable_irq(ts->spi->irq); + enable_irq(ts->irq); tsc2005_start_scan(ts); out: @@ -540,10 +571,10 @@ static void tsc2005_close(struct input_dev *input) mutex_unlock(&ts->mutex); } -static int tsc2005_probe(struct spi_device *spi) +static int tsc200x_probe_common(struct device *dev, int irq, __u16 bustype) { - const struct tsc2005_platform_data *pdata = dev_get_platdata(&spi->dev); - struct device_node *np = spi->dev.of_node; + const struct tsc2005_platform_data *pdata = dev_get_platdata(dev); + struct device_node *np = dev->of_node; struct tsc2005 *ts; struct input_dev *input_dev; @@ -558,12 +589,12 @@ static int tsc2005_probe(struct spi_device *spi) int error; if (!np && !pdata) { - dev_err(&spi->dev, "no platform data\n"); + dev_err(dev, "no platform data\n"); return -ENODEV; } - if (spi->irq <= 0) { - dev_err(&spi->dev, "no irq\n"); + if (irq <= 0) { + dev_err(dev, "no irq\n"); return -ENODEV; } @@ -584,45 +615,46 @@ static int tsc2005_probe(struct spi_device *spi) &esd_timeout); } - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; - if (!spi->max_speed_hz) - spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ; - - error = spi_setup(spi); - if (error) - return error; - - ts = devm_kzalloc(&spi->dev, sizeof(*ts), GFP_KERNEL); + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; - input_dev = devm_input_allocate_device(&spi->dev); + input_dev = devm_input_allocate_device(dev); if (!input_dev) return -ENOMEM; - ts->spi = spi; + ts->irq = irq; + ts->dev = dev; ts->idev = input_dev; - ts->regmap = devm_regmap_init_spi(spi, &tsc2005_regmap_config); + if (bustype == BUS_SPI) { + ts->spi = to_spi_device(dev); + ts->regmap = devm_regmap_init_spi(ts->spi, + &tsc2005_regmap_config); + } else if (bustype == BUS_I2C) { + ts->i2c = to_i2c_client(dev); + ts->regmap = devm_regmap_init_i2c(ts->i2c, + &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", + 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(&spi->dev, "error acquiring reset gpio: %d\n", error); + dev_err(dev, "error acquiring reset gpio: %d\n", error); return error; } - ts->vio = devm_regulator_get_optional(&spi->dev, "vio"); + ts->vio = devm_regulator_get_optional(dev, "vio"); if (IS_ERR(ts->vio)) { error = PTR_ERR(ts->vio); - dev_err(&spi->dev, "vio regulator missing (%d)", error); + dev_err(dev, "vio regulator missing (%d)", error); return error; } @@ -637,12 +669,12 @@ static int tsc2005_probe(struct spi_device *spi) INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work); snprintf(ts->phys, sizeof(ts->phys), - "%s/input-ts", dev_name(&spi->dev)); + "%s/input-ts", dev_name(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->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); @@ -661,12 +693,12 @@ static int tsc2005_probe(struct spi_device *spi) /* Ensure the touchscreen is off */ tsc2005_stop_scan(ts); - error = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + error = devm_request_threaded_irq(dev, 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); + dev_err(dev, "Failed to request irq, err: %d\n", error); return error; } @@ -677,32 +709,49 @@ static int tsc2005_probe(struct spi_device *spi) return error; } - dev_set_drvdata(&spi->dev, ts); - error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group); + dev_set_drvdata(dev, ts); + error = sysfs_create_group(&dev->kobj, &tsc2005_attr_group); if (error) { - dev_err(&spi->dev, + 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(&spi->dev, + dev_err(dev, "Failed to register input device, err: %d\n", error); goto err_remove_sysfs; } - irq_set_irq_wake(spi->irq, 1); + irq_set_irq_wake(irq, 1); return 0; err_remove_sysfs: - sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); + sysfs_remove_group(&dev->kobj, &tsc2005_attr_group); disable_regulator: if (ts->vio) regulator_disable(ts->vio); return error; } + +static int tsc2005_probe(struct spi_device *spi) +{ + int error; + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + if (!spi->max_speed_hz) + spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ; + + error = spi_setup(spi); + if (error) + return error; + + return tsc200x_probe_common(&spi->dev, spi->irq, BUS_SPI); +} + static int tsc2005_remove(struct spi_device *spi) { struct tsc2005 *ts = dev_get_drvdata(&spi->dev); @@ -759,7 +808,78 @@ static struct spi_driver tsc2005_driver = { .remove = tsc2005_remove, }; -module_spi_driver(tsc2005_driver); +static int tsc2004_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) + +{ + return tsc200x_probe_common(&i2c->dev, i2c->irq, BUS_I2C); +} + +static int tsc2004_remove(struct i2c_client *i2c) +{ + struct tsc2005 *ts = dev_get_drvdata(&i2c->dev); + + sysfs_remove_group(&i2c->dev.kobj, &tsc2005_attr_group); + + if (ts->vio) + regulator_disable(ts->vio); + + return 0; +} + +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 = &tsc2005_pm_ops, + }, + .id_table = tsc2004_idtable, + .probe = tsc2004_probe, + .remove = tsc2004_remove, +}; + +static int __init tsc2005_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&tsc2004_driver); + if (ret != 0) + pr_err("Failed to register tsc2004 I2C driver: %d\n", ret); +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&tsc2005_driver); + if (ret != 0) + pr_err("Failed to register tsc2005 SPI driver: %d\n", ret); +#endif + return ret; +} +module_init(tsc2005_modinit); + +static void __exit tsc2005_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&tsc2004_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&tsc2005_driver); +#endif +} +module_exit(tsc2005_exit); MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@xxxxxxxxx>"); MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html