Message ID | 1445978203-31264-1-git-send-email-mwelling@ieee.org (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
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@ieee.org> > --- > .../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@nokia.com>"); > 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Michael, [auto build test ERROR on input/next -- if it's inappropriate base, please suggest rules for selecting the more suitable base] url: https://github.com/0day-ci/linux/commits/Michael-Welling/Input-tsc2005-Add-support-for-tsc2004/20151028-043959 config: x86_64-randconfig-s3-10280543 (attached as .config) reproduce: # save the attached .config to linux build tree make ARCH=x86_64 All errors (new ones prefixed by >>): drivers/built-in.o: In function `regmap_smbus_byte_reg_read': >> regmap-i2c.c:(.text+0x9980c): undefined reference to `i2c_smbus_read_byte_data' drivers/built-in.o: In function `regmap_smbus_byte_reg_write': >> regmap-i2c.c:(.text+0x9983b): undefined reference to `i2c_smbus_write_byte_data' drivers/built-in.o: In function `regmap_smbus_word_reg_read': >> regmap-i2c.c:(.text+0x99861): undefined reference to `i2c_smbus_read_word_data' drivers/built-in.o: In function `regmap_smbus_word_read_swapped': regmap-i2c.c:(.text+0x99891): undefined reference to `i2c_smbus_read_word_data' drivers/built-in.o: In function `regmap_smbus_word_write_swapped': >> regmap-i2c.c:(.text+0x998c5): undefined reference to `i2c_smbus_write_word_data' drivers/built-in.o: In function `regmap_smbus_word_reg_write': regmap-i2c.c:(.text+0x998ea): undefined reference to `i2c_smbus_write_word_data' drivers/built-in.o: In function `regmap_i2c_smbus_i2c_read': >> regmap-i2c.c:(.text+0x9991e): undefined reference to `i2c_smbus_read_i2c_block_data' drivers/built-in.o: In function `regmap_i2c_smbus_i2c_write': >> regmap-i2c.c:(.text+0x9996d): undefined reference to `i2c_smbus_write_i2c_block_data' drivers/built-in.o: In function `regmap_i2c_read': >> regmap-i2c.c:(.text+0x999c1): undefined reference to `i2c_transfer' drivers/built-in.o: In function `regmap_i2c_gather_write': regmap-i2c.c:(.text+0x99a66): undefined reference to `i2c_transfer' drivers/built-in.o: In function `regmap_i2c_write': >> regmap-i2c.c:(.text+0x99a9d): undefined reference to `i2c_master_send' drivers/built-in.o: In function `tsc2005_cmd': >> tsc2005.c:(.text+0x12ab28): undefined reference to `i2c_smbus_write_byte' drivers/built-in.o: In function `tsc2005_modinit': >> tsc2005.c:(.init.text+0x85f3): undefined reference to `i2c_register_driver' drivers/built-in.o: In function `tsc2005_exit': >> tsc2005.c:(.exit.text+0xbec): undefined reference to `i2c_del_driver' --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
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@nokia.com>"); MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
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@ieee.org> --- .../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