diff mbox

Input: tsc2005 - Add support for tsc2004

Message ID 1445978203-31264-1-git-send-email-mwelling@ieee.org (mailing list archive)
State Superseded
Headers show

Commit Message

Michael Welling Oct. 27, 2015, 8:36 p.m. UTC
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

Comments

Michael Welling Oct. 27, 2015, 8:49 p.m. UTC | #1
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
kernel test robot Oct. 27, 2015, 11:34 p.m. UTC | #2
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 mbox

Patch

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");