On Tue, Oct 27, 2015 at 03:36:43PM -0500, Michael Welling wrote: > Adds support for the i2c based tsc2004. Support was added to the tsc2005 driver > due to the similarity of the devices. > It should be noted that the new TSC2004 I2C support was tested but I do not have a device with the TSC2005 to verify. Anyone with a N900 or other device with the TSC2005 willing to test this out and see if I unintentionally broke the driver? > 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