diff options
Diffstat (limited to 'drivers/input/touchscreen')
80 files changed, 16555 insertions, 4538 deletions
diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index b3aebc2166b..0d4a9fad4a7 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -10,11 +10,13 @@   */  #include <linux/kernel.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/i2c.h>  #include <linux/input.h>  #include <linux/mfd/88pm860x.h>  #include <linux/slab.h> +#include <linux/device.h>  #define MEAS_LEN		(8)  #define ACCURATE_BIT		(12) @@ -113,14 +115,69 @@ static void pm860x_touch_close(struct input_dev *dev)  	pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);  } -static int __devinit pm860x_touch_probe(struct platform_device *pdev) +#ifdef CONFIG_OF +static int pm860x_touch_dt_init(struct platform_device *pdev, +					  struct pm860x_chip *chip, +					  int *res_x) +{ +	struct device_node *np = pdev->dev.parent->of_node; +	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ +				 : chip->companion; +	int data, n, ret; +	if (!np) +		return -ENODEV; +	np = of_find_node_by_name(np, "touch"); +	if (!np) { +		dev_err(&pdev->dev, "Can't find touch node\n"); +		return -EINVAL; +	} +	/* set GPADC MISC1 register */ +	data = 0; +	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-prebias", &n)) +		data |= (n << 1) & PM8607_GPADC_PREBIAS_MASK; +	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-slot-cycle", &n)) +		data |= (n << 3) & PM8607_GPADC_SLOT_CYCLE_MASK; +	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-off-scale", &n)) +		data |= (n << 5) & PM8607_GPADC_OFF_SCALE_MASK; +	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-sw-cal", &n)) +		data |= (n << 7) & PM8607_GPADC_SW_CAL_MASK; +	if (data) { +		ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); +		if (ret < 0) +			return -EINVAL; +	} +	/* set tsi prebias time */ +	if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) { +		ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); +		if (ret < 0) +			return -EINVAL; +	} +	/* set prebias & prechg time of pen detect */ +	data = 0; +	if (!of_property_read_u32(np, "marvell,88pm860x-pen-prebias", &n)) +		data |= n & PM8607_PD_PREBIAS_MASK; +	if (!of_property_read_u32(np, "marvell,88pm860x-pen-prechg", &n)) +		data |= n & PM8607_PD_PRECHG_MASK; +	if (data) { +		ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); +		if (ret < 0) +			return -EINVAL; +	} +	of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x); +	return 0; +} +#else +#define pm860x_touch_dt_init(x, y, z)	(-1) +#endif + +static int pm860x_touch_probe(struct platform_device *pdev)  {  	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); -	struct pm860x_platform_data *pm860x_pdata =		\ -				pdev->dev.parent->platform_data; -	struct pm860x_touch_pdata *pdata = NULL; +	struct pm860x_touch_pdata *pdata = dev_get_platdata(&pdev->dev);  	struct pm860x_touch *touch; -	int irq, ret; +	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ +				 : chip->companion; +	int irq, ret, res_x = 0, data = 0;  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) { @@ -128,27 +185,67 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev)  		return -EINVAL;  	} -	if (!pm860x_pdata) { -		dev_err(&pdev->dev, "platform data is missing\n"); -		return -EINVAL; -	} - -	pdata = pm860x_pdata->touch; -	if (!pdata) { -		dev_err(&pdev->dev, "touchscreen data is missing\n"); -		return -EINVAL; +	if (pm860x_touch_dt_init(pdev, chip, &res_x)) { +		if (pdata) { +			/* set GPADC MISC1 register */ +			data = 0; +			data |= (pdata->gpadc_prebias << 1) +				& PM8607_GPADC_PREBIAS_MASK; +			data |= (pdata->slot_cycle << 3) +				& PM8607_GPADC_SLOT_CYCLE_MASK; +			data |= (pdata->off_scale << 5) +				& PM8607_GPADC_OFF_SCALE_MASK; +			data |= (pdata->sw_cal << 7) +				& PM8607_GPADC_SW_CAL_MASK; +			if (data) { +				ret = pm860x_reg_write(i2c, +					PM8607_GPADC_MISC1, data); +				if (ret < 0) +					return -EINVAL; +			} +			/* set tsi prebias time */ +			if (pdata->tsi_prebias) { +				data = pdata->tsi_prebias; +				ret = pm860x_reg_write(i2c, +					PM8607_TSI_PREBIAS, data); +				if (ret < 0) +					return -EINVAL; +			} +			/* set prebias & prechg time of pen detect */ +			data = 0; +			data |= pdata->pen_prebias +				& PM8607_PD_PREBIAS_MASK; +			data |= (pdata->pen_prechg << 5) +				& PM8607_PD_PRECHG_MASK; +			if (data) { +				ret = pm860x_reg_write(i2c, +					PM8607_PD_PREBIAS, data); +				if (ret < 0) +					return -EINVAL; +			} +			res_x = pdata->res_x; +		} else { +			dev_err(&pdev->dev, "failed to get platform data\n"); +			return -EINVAL; +		}  	} - -	touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL); -	if (touch == NULL) +	/* enable GPADC */ +	ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, PM8607_GPADC_EN, +			      PM8607_GPADC_EN); +	if (ret) +		return ret; + +	touch = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_touch), +			     GFP_KERNEL); +	if (!touch)  		return -ENOMEM; -	dev_set_drvdata(&pdev->dev, touch); -	touch->idev = input_allocate_device(); -	if (touch->idev == NULL) { +	platform_set_drvdata(pdev, touch); + +	touch->idev = devm_input_allocate_device(&pdev->dev); +	if (!touch->idev) {  		dev_err(&pdev->dev, "Failed to allocate input device!\n"); -		ret = -ENOMEM; -		goto out; +		return -ENOMEM;  	}  	touch->idev->name = "88pm860x-touch"; @@ -158,15 +255,16 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev)  	touch->idev->open = pm860x_touch_open;  	touch->idev->close = pm860x_touch_close;  	touch->chip = chip; -	touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; -	touch->irq = irq + chip->irq_base; -	touch->res_x = pdata->res_x; +	touch->i2c = i2c; +	touch->irq = irq; +	touch->res_x = res_x;  	input_set_drvdata(touch->idev, touch); -	ret = request_threaded_irq(touch->irq, NULL, pm860x_touch_handler, -				   IRQF_ONESHOT, "touch", touch); +	ret = devm_request_threaded_irq(&pdev->dev, touch->irq, NULL, +					pm860x_touch_handler, IRQF_ONESHOT, +					"touch", touch);  	if (ret < 0) -		goto out_irq; +		return ret;  	__set_bit(EV_ABS, touch->idev->evbit);  	__set_bit(ABS_X, touch->idev->absbit); @@ -184,29 +282,11 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev)  	ret = input_register_device(touch->idev);  	if (ret < 0) {  		dev_err(chip->dev, "Failed to register touch!\n"); -		goto out_rg; +		return ret;  	}  	platform_set_drvdata(pdev, touch);  	return 0; -out_rg: -	free_irq(touch->irq, touch); -out_irq: -	input_free_device(touch->idev); -out: -	kfree(touch); -	return ret; -} - -static int __devexit pm860x_touch_remove(struct platform_device *pdev) -{ -	struct pm860x_touch *touch = platform_get_drvdata(pdev); - -	input_unregister_device(touch->idev); -	free_irq(touch->irq, touch); -	platform_set_drvdata(pdev, NULL); -	kfree(touch); -	return 0;  }  static struct platform_driver pm860x_touch_driver = { @@ -215,20 +295,8 @@ static struct platform_driver pm860x_touch_driver = {  		.owner	= THIS_MODULE,  	},  	.probe	= pm860x_touch_probe, -	.remove	= __devexit_p(pm860x_touch_remove),  }; - -static int __init pm860x_touch_init(void) -{ -	return platform_driver_register(&pm860x_touch_driver); -} -module_init(pm860x_touch_init); - -static void __exit pm860x_touch_exit(void) -{ -	platform_driver_unregister(&pm860x_touch_driver); -} -module_exit(pm860x_touch_exit); +module_platform_driver(pm860x_touch_driver);  MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");  MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 06ea8da95c6..a23a94bb4bc 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -11,6 +11,10 @@ menuconfig INPUT_TOUCHSCREEN  if INPUT_TOUCHSCREEN +config OF_TOUCHSCREEN +	def_tristate INPUT +	depends on INPUT && OF +  config TOUCHSCREEN_88PM860X  	tristate "Marvell 88PM860x touchscreen"  	depends on MFD_88PM860X @@ -86,17 +90,31 @@ config TOUCHSCREEN_AD7879_SPI  	  To compile this driver as a module, choose M here: the  	  module will be called ad7879-spi. -config TOUCHSCREEN_BITSY -	tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" -	depends on SA1100_BITSY -	select SERIO +config TOUCHSCREEN_ATMEL_MXT +	tristate "Atmel mXT I2C Touchscreen" +	depends on I2C +	select FW_LOADER +	help +	  Say Y here if you have Atmel mXT series I2C touchscreen, +	  such as AT42QT602240/ATMXT224, connected to your system. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called atmel_mxt_ts. + +config TOUCHSCREEN_AUO_PIXCIR +	tristate "AUO in-cell touchscreen using Pixcir ICs" +	depends on I2C +	depends on GPIOLIB  	help -	  Say Y here if you have the h3600 (Bitsy) touchscreen. +	  Say Y here if you have a AUO display with in-cell touchscreen +	  using Pixcir ICs.  	  If unsure, say N.  	  To compile this driver as a module, choose M here: the -	  module will be called h3600_ts_input. +	  module will be called auo-pixcir-ts.  config TOUCHSCREEN_BU21013  	tristate "BU21013 based touch panel controllers" @@ -114,7 +132,6 @@ config TOUCHSCREEN_CY8CTMG110  	tristate "cy8ctmg110 touchscreen"  	depends on I2C  	depends on GPIOLIB -  	help  	  Say Y here if you have a cy8ctmg110 capacitive touchscreen on  	  an AAVA device. @@ -124,6 +141,67 @@ config TOUCHSCREEN_CY8CTMG110  	  To compile this driver as a module, choose M here: the  	  module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CYTTSP_CORE +	tristate "Cypress TTSP touchscreen" +	help +	  Say Y here if you have a touchscreen using controller from +	  the Cypress TrueTouch(tm) Standard Product family connected +	  to your system. You will also need to select appropriate +	  bus connection below. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called cyttsp_core. + +config TOUCHSCREEN_CYTTSP_I2C +	tristate "support I2C bus connection" +	depends on TOUCHSCREEN_CYTTSP_CORE && I2C +	help +	  Say Y here if the touchscreen is connected via I2C bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called cyttsp_i2c. + +config TOUCHSCREEN_CYTTSP_SPI +	tristate "support SPI bus connection" +	depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER +	help +	  Say Y here if the touchscreen is connected via SPI bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called cyttsp_spi. + +config TOUCHSCREEN_CYTTSP4_CORE +	tristate "Cypress TrueTouch Gen4 Touchscreen Driver" +	help +	  Core driver for Cypress TrueTouch(tm) Standard Product +	  Generation4 touchscreen controllers. + +	  Say Y here if you have a Cypress Gen4 touchscreen. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here. + +config TOUCHSCREEN_CYTTSP4_I2C +	tristate "support I2C bus connection" +	depends on TOUCHSCREEN_CYTTSP4_CORE && I2C +	help +	  Say Y here if the touchscreen is connected via I2C bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called cyttsp4_i2c. + +config TOUCHSCREEN_CYTTSP4_SPI +	tristate "support SPI bus connection" +	depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER +	help +	  Say Y here if the touchscreen is connected via SPI bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called cyttsp4_spi. +  config TOUCHSCREEN_DA9034  	tristate "Touchscreen support for Dialog Semiconductor DA9034"  	depends on PMIC_DA903X @@ -132,6 +210,23 @@ config TOUCHSCREEN_DA9034  	  Say Y here to enable the support for the touchscreen found  	  on Dialog Semiconductor DA9034 PMIC. +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called da9034-ts. + +config TOUCHSCREEN_DA9052 +	tristate "Dialog DA9052/DA9053 TSI" +	depends on PMIC_DA9052 +	help +	  Say Y here to support the touchscreen found on Dialog Semiconductor +	  DA9052-BC and DA9053-AA/Bx PMICs. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called da9052_tsi. +  config TOUCHSCREEN_DYNAPRO  	tristate "Dynapro serial touchscreen"  	select SERIO @@ -165,6 +260,16 @@ config TOUCHSCREEN_EETI  	  To compile this driver as a module, choose M here: the  	  module will be called eeti_ts. +config TOUCHSCREEN_EGALAX +	tristate "EETI eGalax multi-touch panel support" +	depends on I2C && OF +	help +	  Say Y here to enable support for I2C connected EETI +	  eGalax multi-touch panels. + +	  To compile this driver as a module, choose M here: the +	  module will be called egalax_ts. +  config TOUCHSCREEN_FUJITSU  	tristate "Fujitsu serial touchscreen"  	select SERIO @@ -178,9 +283,24 @@ config TOUCHSCREEN_FUJITSU  	  To compile this driver as a module, choose M here: the  	  module will be called fujitsu-ts. +config TOUCHSCREEN_ILI210X +	tristate "Ilitek ILI210X based touchscreen" +	depends on I2C +	help +	  Say Y here if you have a ILI210X based touchscreen +	  controller. This driver supports models ILI2102, +	  ILI2102s, ILI2103, ILI2103s and ILI2105. +	  Such kind of chipsets can be found in Amazon Kindle Fire +	  touchscreens. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called ili210x. +  config TOUCHSCREEN_S3C2410  	tristate "Samsung S3C2410/generic touchscreen input driver" -	depends on ARCH_S3C2410 || SAMSUNG_DEV_TS +	depends on ARCH_S3C24XX || SAMSUNG_DEV_TS  	select S3C_ADC  	help  	  Say Y here if you have the s3c2410 touchscreen. @@ -226,6 +346,18 @@ config TOUCHSCREEN_WACOM_W8001  	  To compile this driver as a module, choose M here: the  	  module will be called wacom_w8001. +config TOUCHSCREEN_WACOM_I2C +	tristate "Wacom Tablet support (I2C)" +	depends on I2C +	help +	  Say Y here if you want to use the I2C version of the Wacom +	  Pen Tablet. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the module +	  will be called wacom_i2c. +  config TOUCHSCREEN_LPC32XX  	tristate "LPC32XX touchscreen controller"  	depends on ARCH_LPC32XX @@ -236,6 +368,18 @@ config TOUCHSCREEN_LPC32XX  	  To compile this driver as a module, choose M here: the  	  module will be called lpc32xx_ts. +config TOUCHSCREEN_MAX11801 +	tristate "MAX11801 based touchscreens" +	depends on I2C +	help +	  Say Y here if you have a MAX11801 based touchscreen +	  controller. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called max11801_ts. +  config TOUCHSCREEN_MCS5000  	tristate "MELFAS MCS-5000 touchscreen"  	depends on I2C @@ -248,6 +392,18 @@ config TOUCHSCREEN_MCS5000  	  To compile this driver as a module, choose M here: the  	  module will be called mcs5000_ts. +config TOUCHSCREEN_MMS114 +	tristate "MELFAS MMS114 touchscreen" +	depends on I2C +	help +	  Say Y here if you have the MELFAS MMS114 touchscreen controller +	  chip in your system. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called mms114. +  config TOUCHSCREEN_MTOUCH  	tristate "MicroTouch serial touchscreens"  	select SERIO @@ -339,17 +495,18 @@ config TOUCHSCREEN_PENMOUNT  	  To compile this driver as a module, choose M here: the  	  module will be called penmount. -config TOUCHSCREEN_QT602240 -	tristate "QT602240 I2C Touchscreen" +config TOUCHSCREEN_EDT_FT5X06 +	tristate "EDT FocalTech FT5x06 I2C Touchscreen support"  	depends on I2C  	help -	  Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen -	  connected to your system. +	  Say Y here if you have an EDT "Polytouch" touchscreen based +	  on the FocalTech FT5x06 family of controllers connected to +	  your system.  	  If unsure, say N.  	  To compile this driver as a module, choose M here: the -	  module will be called qt602240_ts. +	  module will be called edt-ft5x06.  config TOUCHSCREEN_MIGOR  	tristate "Renesas MIGO-R touchscreen" @@ -362,15 +519,6 @@ config TOUCHSCREEN_MIGOR  	  To compile this driver as a module, choose M here: the  	  module will be called migor_ts. -config TOUCHSCREEN_TNETV107X -	tristate "TI TNETV107X touchscreen support" -	depends on ARCH_DAVINCI_TNETV107X -	help -	  Say Y here if you want to use the TNETV107X touchscreen. - -	  To compile this driver as a module, choose M here: the -	  module will be called tnetv107x-ts. -  config TOUCHSCREEN_TOUCHRIGHT  	tristate "Touchright serial touchscreen"  	select SERIO @@ -395,17 +543,17 @@ config TOUCHSCREEN_TOUCHWIN  	  To compile this driver as a module, choose M here: the  	  module will be called touchwin. -config TOUCHSCREEN_ATMEL_TSADCC -	tristate "Atmel Touchscreen Interface" -	depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 +config TOUCHSCREEN_TI_AM335X_TSC +	tristate "TI Touchscreen Interface" +	depends on MFD_TI_AM335X_TSCADC  	help -	  Say Y here if you have a 4-wire touchscreen connected to the -          ADC Controller on your Atmel SoC (such as the AT91SAM9RL). +	  Say Y here if you have 4/5/8 wire touchscreen controller +	  to be connected to the ADC controller on your TI AM335x SoC.  	  If unsure, say N.  	  To compile this driver as a module, choose M here: the -	  module will be called atmel_tsadcc. +	  module will be called ti_am335x_tsc.  config TOUCHSCREEN_UCB1400  	tristate "Philips UCB1400 touchscreen" @@ -423,6 +571,28 @@ config TOUCHSCREEN_UCB1400  	  To compile this driver as a module, choose M here: the  	  module will be called ucb1400_ts. +config TOUCHSCREEN_PIXCIR +	tristate "PIXCIR I2C touchscreens" +	depends on I2C +	help +	  Say Y here if you have a pixcir i2c touchscreen +	  controller. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called pixcir_i2c_ts. + +config TOUCHSCREEN_WM831X +	tristate "Support for WM831x touchscreen controllers" +	depends on MFD_WM831X +	help +	  This enables support for the touchscreen controller on the WM831x +	  series of PMICs. + +	  To compile this driver as a module, choose M here: the +	  module will be called wm831x-ts. +  config TOUCHSCREEN_WM97XX  	tristate "Support for WM97xx AC97 touchscreen controllers"  	depends on AC97_BUS @@ -463,7 +633,7 @@ config TOUCHSCREEN_WM9713  config TOUCHSCREEN_WM97XX_ATMEL  	tristate "WM97xx Atmel accelerated touch" -	depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91) +	depends on TOUCHSCREEN_WM97XX && AVR32  	help  	  Say Y here for support for streaming mode with WM97xx touchscreens  	  on Atmel AT91 or AVR32 systems with an AC97C module. @@ -519,6 +689,9 @@ config TOUCHSCREEN_USB_COMPOSITE  	  - GoTop Super_Q2/GogoPen/PenPower tablets  	  - JASTEC USB Touch Controller/DigiTech DTR-02U  	  - Zytronic controllers +	  - Elo TouchSystems 2700 IntelliTouch +	  - EasyTouch USB Touch Controller from Data Modul +	  - e2i (Mimo monitors)  	  Have a look at <http://linux.chapter7.ch/touchkit/> for  	  a usage description and the required user-space stuff. @@ -528,7 +701,7 @@ config TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_MC13783  	tristate "Freescale MC13783 touchscreen input driver" -	depends on MFD_MC13783 +	depends on MFD_MC13XXX  	help  	  Say Y here if you have an Freescale MC13783 PMIC on your  	  board and want to use its touchscreen @@ -540,83 +713,96 @@ config TOUCHSCREEN_MC13783  config TOUCHSCREEN_USB_EGALAX  	default y -	bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED +	bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_PANJIT  	default y -	bool "PanJit device support" if EMBEDDED +	bool "PanJit device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_3M  	default y -	bool "3M/Microtouch EX II series device support" if EMBEDDED +	bool "3M/Microtouch EX II series device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_ITM  	default y -	bool "ITM device support" if EMBEDDED +	bool "ITM device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_ETURBO  	default y -	bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED +	bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_GUNZE  	default y -	bool "Gunze AHL61 device support" if EMBEDDED +	bool "Gunze AHL61 device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_DMC_TSC10  	default y -	bool "DMC TSC-10/25 device support" if EMBEDDED +	bool "DMC TSC-10/25 device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_IRTOUCH  	default y -	bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED +	bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_IDEALTEK  	default y -	bool "IdealTEK URTC1000 device support" if EMBEDDED +	bool "IdealTEK URTC1000 device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_GENERAL_TOUCH  	default y -	bool "GeneralTouch Touchscreen device support" if EMBEDDED +	bool "GeneralTouch Touchscreen device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_GOTOP  	default y -	bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED +	bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_JASTEC  	default y -	bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED +	bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT +	depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ELO +	default y +	bool "Elo TouchSystems 2700 IntelliTouch controller device support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_E2I  	default y -	bool "e2i Touchscreen controller (e.g. from Mimo 740)" +	bool "e2i Touchscreen controller (e.g. from Mimo 740)" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_ZYTRONIC  	default y -	bool "Zytronic controller" if EMBEDDED +	bool "Zytronic controller" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_ETT_TC45USB  	default y -	bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED +	bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT  	depends on TOUCHSCREEN_USB_COMPOSITE  config TOUCHSCREEN_USB_NEXIO  	default y -	bool "NEXIO/iNexio device support" if EMBEDDED +	bool "NEXIO/iNexio device support" if EXPERT +	depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_EASYTOUCH +	default y +	bool "EasyTouch USB Touch controller device support" if EMBEDDED  	depends on TOUCHSCREEN_USB_COMPOSITE +	help +	  Say Y here if you have an EasyTouch USB Touch controller. +	  If unsure, say N.  config TOUCHSCREEN_TOUCHIT213  	tristate "Sahara TouchIT-213 touchscreen" @@ -629,6 +815,29 @@ config TOUCHSCREEN_TOUCHIT213  	  To compile this driver as a module, choose M here: the  	  module will be called touchit213. +config TOUCHSCREEN_TSC_SERIO +	tristate "TSC-10/25/40 serial touchscreen support" +	select SERIO +	help +	  Say Y here if you have a TSC-10, 25 or 40 serial touchscreen connected +	  to your system. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called tsc40. + +config TOUCHSCREEN_TSC2005 +        tristate "TSC2005 based touchscreens" +        depends on SPI_MASTER +        help +          Say Y here if you have a TSC2005 based touchscreen. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called tsc2005. +  config TOUCHSCREEN_TSC2007  	tristate "TSC2007 based touchscreens"  	depends on I2C @@ -642,7 +851,7 @@ config TOUCHSCREEN_TSC2007  config TOUCHSCREEN_W90X900  	tristate "W90P910 touchscreen driver" -	depends on HAVE_CLK +	depends on ARCH_W90X900  	help  	  Say Y here if you have a W90P910 based touchscreen. @@ -659,17 +868,17 @@ config TOUCHSCREEN_PCAP  	  To compile this driver as a module, choose M here: the  	  module will be called pcap_ts. -config TOUCHSCREEN_TPS6507X -	tristate "TPS6507x based touchscreens" +config TOUCHSCREEN_ST1232 +	tristate "Sitronix ST1232 touchscreen controllers"  	depends on I2C  	help -	  Say Y here if you have a TPS6507x based touchscreen -	  controller. +	  Say Y here if you want to support Sitronix ST1232 +	  touchscreen controller.  	  If unsure, say N.  	  To compile this driver as a module, choose M here: the -	  module will be called tps6507x_ts. +	  module will be called st1232_ts.  config TOUCHSCREEN_STMPE  	tristate "STMicroelectronics STMPE touchscreens" @@ -681,4 +890,52 @@ config TOUCHSCREEN_STMPE  	  To compile this driver as a module, choose M here: the  	  module will be called stmpe-ts. +config TOUCHSCREEN_SUN4I +	tristate "Allwinner sun4i resistive touchscreen controller support" +	depends on ARCH_SUNXI || COMPILE_TEST +	depends on HWMON +	help +	  This selects support for the resistive touchscreen controller +	  found on Allwinner sunxi SoCs. + +	  To compile this driver as a module, choose M here: the +	  module will be called sun4i-ts. + +config TOUCHSCREEN_SUR40 +	tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" +	depends on USB +	select INPUT_POLLDEV +	help +	  Say Y here if you want support for the Samsung SUR40 touchscreen +	  (also known as Microsoft Surface 2.0 or Microsoft PixelSense). + +	  To compile this driver as a module, choose M here: the +	  module will be called sur40. + +config TOUCHSCREEN_TPS6507X +	tristate "TPS6507x based touchscreens" +	depends on I2C +	select INPUT_POLLDEV +	help +	  Say Y here if you have a TPS6507x based touchscreen +	  controller. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called tps6507x_ts. + +config TOUCHSCREEN_ZFORCE +	tristate "Neonode zForce infrared touchscreens" +	depends on I2C +	depends on GPIOLIB +	help +	  Say Y here if you have a touchscreen using the zforce +	  infraread technology from Neonode. + +	  If unsure, say N. + +	  To compile this driver as a module, choose M here: the +	  module will be called zforce_ts. +  endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 7cc1b4f4b67..126479d8c29 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -6,29 +6,42 @@  wm97xx-ts-y := wm97xx-core.o +obj-$(CONFIG_OF_TOUCHSCREEN)		+= of_touchscreen.o  obj-$(CONFIG_TOUCHSCREEN_88PM860X)	+= 88pm860x-ts.o  obj-$(CONFIG_TOUCHSCREEN_AD7877)	+= ad7877.o  obj-$(CONFIG_TOUCHSCREEN_AD7879)	+= ad7879.o  obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C)	+= ad7879-i2c.o  obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o  obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o -obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o -obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o -obj-$(CONFIG_TOUCHSCREEN_BU21013)       += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)	+= atmel_mxt_ts.o +obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)	+= auo-pixcir-ts.o +obj-$(CONFIG_TOUCHSCREEN_BU21013)	+= bu21013_ts.o  obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)	+= cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE)	+= cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C)	+= cyttsp_i2c.o cyttsp_i2c_common.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI)	+= cyttsp_spi.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE)	+= cyttsp4_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C)	+= cyttsp4_i2c.o cyttsp_i2c_common.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI)	+= cyttsp4_spi.o  obj-$(CONFIG_TOUCHSCREEN_DA9034)	+= da9034-ts.o +obj-$(CONFIG_TOUCHSCREEN_DA9052)	+= da9052_tsi.o  obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)	+= dynapro.o +obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06)	+= edt-ft5x06.o  obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)	+= hampshire.o  obj-$(CONFIG_TOUCHSCREEN_GUNZE)		+= gunze.o  obj-$(CONFIG_TOUCHSCREEN_EETI)		+= eeti_ts.o  obj-$(CONFIG_TOUCHSCREEN_ELO)		+= elo.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o  obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o  obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o  obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o +obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o +obj-$(CONFIG_TOUCHSCREEN_MMS114)	+= mms114.o  obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o  obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o  obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o @@ -37,16 +50,23 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN)	+= htcpen.o  obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o  obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o  obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o -obj-$(CONFIG_TOUCHSCREEN_QT602240)	+= qt602240_ts.o +obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o  obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o +obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o  obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o -obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o +obj-$(CONFIG_TOUCHSCREEN_SUN4I)		+= sun4i-ts.o +obj-$(CONFIG_TOUCHSCREEN_SUR40)		+= sur40.o +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_TSC_SERIO)	+= tsc40.o +obj-$(CONFIG_TOUCHSCREEN_TSC2005)	+= tsc2005.o  obj-$(CONFIG_TOUCHSCREEN_TSC2007)	+= tsc2007.o  obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.o  obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)	+= wacom_w8001.o +obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C)	+= wacom_i2c.o +obj-$(CONFIG_TOUCHSCREEN_WM831X)	+= wm831x-ts.o  obj-$(CONFIG_TOUCHSCREEN_WM97XX)	+= wm97xx-ts.o  wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705)	+= wm9705.o  wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712)	+= wm9712.o @@ -56,3 +76,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o  obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o  obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o  obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index a1952fcc083..523865daa1d 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -37,13 +37,14 @@  #include <linux/device.h> -#include <linux/init.h>  #include <linux/delay.h>  #include <linux/input.h>  #include <linux/interrupt.h> +#include <linux/pm.h>  #include <linux/slab.h>  #include <linux/spi/spi.h>  #include <linux/spi/ad7877.h> +#include <linux/module.h>  #include <asm/irq.h>  #define	TS_PEN_UP_TIMEOUT	msecs_to_jiffies(100) @@ -209,11 +210,6 @@ static bool gpio3;  module_param(gpio3, bool, 0);  MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3"); -/* - * ad7877_read/write are only used for initial setup and for sysfs controls. - * The main traffic is done using spi_async() in the interrupt handler. - */ -  static int ad7877_read(struct spi_device *spi, u16 reg)  {  	struct ser_req *req; @@ -271,7 +267,7 @@ static int ad7877_write(struct spi_device *spi, u16 reg, u16 val)  static int ad7877_read_adc(struct spi_device *spi, unsigned command)  { -	struct ad7877 *ts = dev_get_drvdata(&spi->dev); +	struct ad7877 *ts = spi_get_drvdata(spi);  	struct ser_req *req;  	int status;  	int sample; @@ -486,10 +482,10 @@ static ssize_t ad7877_disable_store(struct device *dev,  				     const char *buf, size_t count)  {  	struct ad7877 *ts = dev_get_drvdata(dev); -	unsigned long val; +	unsigned int val;  	int error; -	error = strict_strtoul(buf, 10, &val); +	error = kstrtouint(buf, 10, &val);  	if (error)  		return error; @@ -516,10 +512,10 @@ static ssize_t ad7877_dac_store(struct device *dev,  				     const char *buf, size_t count)  {  	struct ad7877 *ts = dev_get_drvdata(dev); -	unsigned long val; +	unsigned int val;  	int error; -	error = strict_strtoul(buf, 10, &val); +	error = kstrtouint(buf, 10, &val);  	if (error)  		return error; @@ -546,10 +542,10 @@ static ssize_t ad7877_gpio3_store(struct device *dev,  				     const char *buf, size_t count)  {  	struct ad7877 *ts = dev_get_drvdata(dev); -	unsigned long val; +	unsigned int val;  	int error; -	error = strict_strtoul(buf, 10, &val); +	error = kstrtouint(buf, 10, &val);  	if (error)  		return error; @@ -577,10 +573,10 @@ static ssize_t ad7877_gpio4_store(struct device *dev,  				     const char *buf, size_t count)  {  	struct ad7877 *ts = dev_get_drvdata(dev); -	unsigned long val; +	unsigned int val;  	int error; -	error = strict_strtoul(buf, 10, &val); +	error = kstrtouint(buf, 10, &val);  	if (error)  		return error; @@ -610,10 +606,10 @@ static struct attribute *ad7877_attributes[] = {  	NULL  }; -static mode_t ad7877_attr_is_visible(struct kobject *kobj, +static umode_t ad7877_attr_is_visible(struct kobject *kobj,  				     struct attribute *attr, int n)  { -	mode_t mode = attr->mode; +	umode_t mode = attr->mode;  	if (attr == &dev_attr_aux3.attr) {  		if (gpio3) @@ -680,11 +676,11 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)  	}  } -static int __devinit ad7877_probe(struct spi_device *spi) +static int ad7877_probe(struct spi_device *spi)  {  	struct ad7877			*ts;  	struct input_dev		*input_dev; -	struct ad7877_platform_data	*pdata = spi->dev.platform_data; +	struct ad7877_platform_data	*pdata = dev_get_platdata(&spi->dev);  	int				err;  	u16				verify; @@ -718,7 +714,7 @@ static int __devinit ad7877_probe(struct spi_device *spi)  		goto err_free_mem;  	} -	dev_set_drvdata(&spi->dev, ts); +	spi_set_drvdata(spi, ts);  	ts->spi = spi;  	ts->input = input_dev; @@ -804,13 +800,12 @@ err_free_irq:  err_free_mem:  	input_free_device(input_dev);  	kfree(ts); -	dev_set_drvdata(&spi->dev, NULL);  	return err;  } -static int __devexit ad7877_remove(struct spi_device *spi) +static int ad7877_remove(struct spi_device *spi)  { -	struct ad7877 *ts = dev_get_drvdata(&spi->dev); +	struct ad7877 *ts = spi_get_drvdata(spi);  	sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); @@ -821,57 +816,43 @@ static int __devexit ad7877_remove(struct spi_device *spi)  	kfree(ts);  	dev_dbg(&spi->dev, "unregistered touchscreen\n"); -	dev_set_drvdata(&spi->dev, NULL);  	return 0;  } -#ifdef CONFIG_PM -static int ad7877_suspend(struct spi_device *spi, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int ad7877_suspend(struct device *dev)  { -	struct ad7877 *ts = dev_get_drvdata(&spi->dev); +	struct ad7877 *ts = dev_get_drvdata(dev);  	ad7877_disable(ts);  	return 0;  } -static int ad7877_resume(struct spi_device *spi) +static int ad7877_resume(struct device *dev)  { -	struct ad7877 *ts = dev_get_drvdata(&spi->dev); +	struct ad7877 *ts = dev_get_drvdata(dev);  	ad7877_enable(ts);  	return 0;  } -#else -#define ad7877_suspend NULL -#define ad7877_resume  NULL  #endif +static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume); +  static struct spi_driver ad7877_driver = {  	.driver = {  		.name	= "ad7877", -		.bus	= &spi_bus_type,  		.owner	= THIS_MODULE, +		.pm	= &ad7877_pm,  	},  	.probe		= ad7877_probe, -	.remove		= __devexit_p(ad7877_remove), -	.suspend	= ad7877_suspend, -	.resume		= ad7877_resume, +	.remove		= ad7877_remove,  }; -static int __init ad7877_init(void) -{ -	return spi_register_driver(&ad7877_driver); -} -module_init(ad7877_init); - -static void __exit ad7877_exit(void) -{ -	spi_unregister_driver(&ad7877_driver); -} -module_exit(ad7877_exit); +module_spi_driver(ad7877_driver);  MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");  MODULE_DESCRIPTION("AD7877 touchscreen Driver"); diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index d82a38ee9a3..dcf39077154 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -10,34 +10,12 @@  #include <linux/i2c.h>  #include <linux/module.h>  #include <linux/types.h> +#include <linux/pm.h>  #include "ad7879.h"  #define AD7879_DEVID		0x79	/* AD7879-1/AD7889-1 */ -#ifdef CONFIG_PM -static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message) -{ -	struct ad7879 *ts = i2c_get_clientdata(client); - -	ad7879_suspend(ts); - -	return 0; -} - -static int ad7879_i2c_resume(struct i2c_client *client) -{ -	struct ad7879 *ts = i2c_get_clientdata(client); - -	ad7879_resume(ts); - -	return 0; -} -#else -# define ad7879_i2c_suspend NULL -# define ad7879_i2c_resume  NULL -#endif -  /* All registers are word-sized.   * AD7879 uses a high-byte first convention.   */ @@ -45,7 +23,7 @@ static int ad7879_i2c_read(struct device *dev, u8 reg)  {  	struct i2c_client *client = to_i2c_client(dev); -	return swab16(i2c_smbus_read_word_data(client, reg)); +	return i2c_smbus_read_word_swapped(client, reg);  }  static int ad7879_i2c_multi_read(struct device *dev, @@ -66,7 +44,7 @@ static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val)  {  	struct i2c_client *client = to_i2c_client(dev); -	return i2c_smbus_write_word_data(client, reg, swab16(val)); +	return i2c_smbus_write_word_swapped(client, reg, val);  }  static const struct ad7879_bus_ops ad7879_i2c_bus_ops = { @@ -76,7 +54,7 @@ static const struct ad7879_bus_ops ad7879_i2c_bus_ops = {  	.write		= ad7879_i2c_write,  }; -static int __devinit ad7879_i2c_probe(struct i2c_client *client, +static int ad7879_i2c_probe(struct i2c_client *client,  				      const struct i2c_device_id *id)  {  	struct ad7879 *ts; @@ -97,7 +75,7 @@ static int __devinit ad7879_i2c_probe(struct i2c_client *client,  	return 0;  } -static int __devexit ad7879_i2c_remove(struct i2c_client *client) +static int ad7879_i2c_remove(struct i2c_client *client)  {  	struct ad7879 *ts = i2c_get_clientdata(client); @@ -117,27 +95,15 @@ static struct i2c_driver ad7879_i2c_driver = {  	.driver = {  		.name	= "ad7879",  		.owner	= THIS_MODULE, +		.pm	= &ad7879_pm_ops,  	},  	.probe		= ad7879_i2c_probe, -	.remove		= __devexit_p(ad7879_i2c_remove), -	.suspend	= ad7879_i2c_suspend, -	.resume		= ad7879_i2c_resume, +	.remove		= ad7879_i2c_remove,  	.id_table	= ad7879_id,  }; -static int __init ad7879_i2c_init(void) -{ -	return i2c_add_driver(&ad7879_i2c_driver); -} -module_init(ad7879_i2c_init); - -static void __exit ad7879_i2c_exit(void) -{ -	i2c_del_driver(&ad7879_i2c_driver); -} -module_exit(ad7879_i2c_exit); +module_i2c_driver(ad7879_i2c_driver);  MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");  MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");  MODULE_LICENSE("GPL"); -MODULE_ALIAS("i2c:ad7879"); diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 59c6e68c432..1a7b1143536 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -7,7 +7,9 @@   */  #include <linux/input.h>	/* BUS_SPI */ +#include <linux/pm.h>  #include <linux/spi/spi.h> +#include <linux/module.h>  #include "ad7879.h" @@ -20,29 +22,6 @@  #define AD7879_WRITECMD(reg) (AD7879_CMD(reg))  #define AD7879_READCMD(reg)  (AD7879_CMD(reg) | AD7879_CMD_READ) -#ifdef CONFIG_PM -static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) -{ -	struct ad7879 *ts = spi_get_drvdata(spi); - -	ad7879_suspend(ts); - -	return 0; -} - -static int ad7879_spi_resume(struct spi_device *spi) -{ -	struct ad7879 *ts = spi_get_drvdata(spi); - -	ad7879_resume(ts); - -	return 0; -} -#else -# define ad7879_spi_suspend NULL -# define ad7879_spi_resume  NULL -#endif -  /*   * ad7879_read/write are only used for initial setup and for sysfs controls.   * The main traffic is done in ad7879_collect(). @@ -131,7 +110,7 @@ static const struct ad7879_bus_ops ad7879_spi_bus_ops = {  	.write		= ad7879_spi_write,  }; -static int __devinit ad7879_spi_probe(struct spi_device *spi) +static int ad7879_spi_probe(struct spi_device *spi)  {  	struct ad7879 *ts;  	int err; @@ -158,12 +137,11 @@ static int __devinit ad7879_spi_probe(struct spi_device *spi)  	return 0;  } -static int __devexit ad7879_spi_remove(struct spi_device *spi) +static int ad7879_spi_remove(struct spi_device *spi)  {  	struct ad7879 *ts = spi_get_drvdata(spi);  	ad7879_remove(ts); -	spi_set_drvdata(spi, NULL);  	return 0;  } @@ -171,26 +149,14 @@ static int __devexit ad7879_spi_remove(struct spi_device *spi)  static struct spi_driver ad7879_spi_driver = {  	.driver = {  		.name	= "ad7879", -		.bus	= &spi_bus_type,  		.owner	= THIS_MODULE, +		.pm	= &ad7879_pm_ops,  	},  	.probe		= ad7879_spi_probe, -	.remove		= __devexit_p(ad7879_spi_remove), -	.suspend	= ad7879_spi_suspend, -	.resume		= ad7879_spi_resume, +	.remove		= ad7879_spi_remove,  }; -static int __init ad7879_spi_init(void) -{ -	return spi_register_driver(&ad7879_spi_driver); -} -module_init(ad7879_spi_init); - -static void __exit ad7879_spi_exit(void) -{ -	spi_unregister_driver(&ad7879_spi_driver); -} -module_exit(ad7879_spi_exit); +module_spi_driver(ad7879_spi_driver);  MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");  MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index bc3b5187f3a..fce590677b7 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -22,7 +22,6 @@   */  #include <linux/device.h> -#include <linux/init.h>  #include <linux/delay.h>  #include <linux/input.h>  #include <linux/interrupt.h> @@ -33,6 +32,7 @@  #include <linux/gpio.h>  #include <linux/spi/ad7879.h> +#include <linux/module.h>  #include "ad7879.h"  #define AD7879_REG_ZEROS		0 @@ -117,6 +117,7 @@ struct ad7879 {  	unsigned int		irq;  	bool			disabled;	/* P: input->mutex */  	bool			suspended;	/* P: input->mutex */ +	bool			swap_xy;  	u16			conversion_data[AD7879_NR_SENSE];  	char			phys[32];  	u8			first_conversion_delay; @@ -160,6 +161,9 @@ static int ad7879_report(struct ad7879 *ts)  	z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT;  	z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; +	if (ts->swap_xy) +		swap(x, y); +  	/*  	 * The samples processed here are already preprocessed by the AD7879.  	 * The preprocessing function consists of a median and an averaging @@ -249,12 +253,14 @@ static void __ad7879_enable(struct ad7879 *ts)  static void __ad7879_disable(struct ad7879 *ts)  { +	u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) | +		AD7879_PM(AD7879_PM_SHUTDOWN);  	disable_irq(ts->irq);  	if (del_timer_sync(&ts->timer))  		ad7879_ts_event_release(ts); -	ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN)); +	ad7879_write(ts, AD7879_REG_CTRL2, reg);  } @@ -278,8 +284,11 @@ static void ad7879_close(struct input_dev* input)  		__ad7879_disable(ts);  } -void ad7879_suspend(struct ad7879 *ts) +#ifdef CONFIG_PM_SLEEP +static int ad7879_suspend(struct device *dev)  { +	struct ad7879 *ts = dev_get_drvdata(dev); +  	mutex_lock(&ts->input->mutex);  	if (!ts->suspended && !ts->disabled && ts->input->users) @@ -288,11 +297,14 @@ void ad7879_suspend(struct ad7879 *ts)  	ts->suspended = true;  	mutex_unlock(&ts->input->mutex); + +	return 0;  } -EXPORT_SYMBOL(ad7879_suspend); -void ad7879_resume(struct ad7879 *ts) +static int ad7879_resume(struct device *dev)  { +	struct ad7879 *ts = dev_get_drvdata(dev); +  	mutex_lock(&ts->input->mutex);  	if (ts->suspended && !ts->disabled && ts->input->users) @@ -301,8 +313,13 @@ void ad7879_resume(struct ad7879 *ts)  	ts->suspended = false;  	mutex_unlock(&ts->input->mutex); + +	return 0;  } -EXPORT_SYMBOL(ad7879_resume); +#endif + +SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume); +EXPORT_SYMBOL(ad7879_pm_ops);  static void ad7879_toggle(struct ad7879 *ts, bool disable)  { @@ -337,10 +354,10 @@ static ssize_t ad7879_disable_store(struct device *dev,  				     const char *buf, size_t count)  {  	struct ad7879 *ts = dev_get_drvdata(dev); -	unsigned long val; +	unsigned int val;  	int error; -	error = strict_strtoul(buf, 10, &val); +	error = kstrtouint(buf, 10, &val);  	if (error)  		return error; @@ -452,7 +469,7 @@ static int ad7879_gpio_add(struct ad7879 *ts,  static void ad7879_gpio_remove(struct ad7879 *ts)  { -	const struct ad7879_platform_data *pdata = ts->dev->platform_data; +	const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);  	int ret;  	if (pdata->gpio_export) { @@ -477,7 +494,7 @@ static inline void ad7879_gpio_remove(struct ad7879 *ts)  struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,  			    const struct ad7879_bus_ops *bops)  { -	struct ad7879_platform_data *pdata = dev->platform_data; +	struct ad7879_platform_data *pdata = dev_get_platdata(dev);  	struct ad7879 *ts;  	struct input_dev *input_dev;  	int err; @@ -506,6 +523,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,  	ts->dev = dev;  	ts->input = input_dev;  	ts->irq = irq; +	ts->swap_xy = pdata->swap_xy;  	setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); @@ -583,7 +601,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,  			AD7879_TMR(ts->pen_down_acc_interval);  	err = request_threaded_irq(ts->irq, NULL, ad7879_irq, -				   IRQF_TRIGGER_FALLING, +				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,  				   dev_name(dev), ts);  	if (err) {  		dev_err(dev, "irq %d busy?\n", ts->irq); diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h index 6b45a27236c..6fd13c48d37 100644 --- a/drivers/input/touchscreen/ad7879.h +++ b/drivers/input/touchscreen/ad7879.h @@ -21,8 +21,8 @@ struct ad7879_bus_ops {  	int (*write)(struct device *dev, u8 reg, u16 val);  }; -void ad7879_suspend(struct ad7879 *); -void ad7879_resume(struct ad7879 *); +extern const struct dev_pm_ops ad7879_pm_ops; +  struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,  			    const struct ad7879_bus_ops *bops);  void ad7879_remove(struct ad7879 *); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 14ea54b78e4..da201b8e37d 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -19,17 +19,21 @@   */  #include <linux/types.h>  #include <linux/hwmon.h> -#include <linux/init.h>  #include <linux/err.h>  #include <linux/sched.h>  #include <linux/delay.h>  #include <linux/input.h>  #include <linux/interrupt.h>  #include <linux/slab.h> +#include <linux/pm.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h>  #include <linux/gpio.h>  #include <linux/spi/spi.h>  #include <linux/spi/ads7846.h>  #include <linux/regulator/consumer.h> +#include <linux/module.h>  #include <asm/irq.h>  /* @@ -96,8 +100,7 @@ struct ads7846 {  	struct spi_device	*spi;  	struct regulator	*reg; -#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) -	struct attribute_group	*attr_group; +#if IS_ENABLED(CONFIG_HWMON)  	struct device		*hwmon;  #endif @@ -108,6 +111,7 @@ struct ads7846 {  	u16			pressure_max;  	bool			swap_xy; +	bool			use_internal;  	struct ads7846_packet	*packet; @@ -233,7 +237,12 @@ static void __ads7846_disable(struct ads7846 *ts)  /* Must be called with ts->lock held */  static void __ads7846_enable(struct ads7846 *ts)  { -	regulator_enable(ts->reg); +	int error; + +	error = regulator_enable(ts->reg); +	if (error != 0) +		dev_err(&ts->spi->dev, "Failed to enable supply: %d\n", error); +  	ads7846_restart(ts);  } @@ -280,17 +289,24 @@ struct ser_req {  	u8			command;  	u8			ref_off;  	u16			scratch; -	__be16			sample;  	struct spi_message	msg;  	struct spi_transfer	xfer[6]; +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	__be16 sample ____cacheline_aligned;  };  struct ads7845_ser_req {  	u8			command[3]; -	u8			pwrdown[3]; -	u8			sample[3];  	struct spi_message	msg;  	struct spi_transfer	xfer[2]; +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	u8 sample[3] ____cacheline_aligned;  };  static int ads7846_read12_ser(struct device *dev, unsigned command) @@ -299,7 +315,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)  	struct ads7846 *ts = dev_get_drvdata(dev);  	struct ser_req *req;  	int status; -	int use_internal;  	req = kzalloc(sizeof *req, GFP_KERNEL);  	if (!req) @@ -307,11 +322,8 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)  	spi_message_init(&req->msg); -	/* FIXME boards with ads7846 might use external vref instead ... */ -	use_internal = (ts->model == 7846); -  	/* maybe turn on internal vREF, and let it settle */ -	if (use_internal) { +	if (ts->use_internal) {  		req->ref_on = REF_ON;  		req->xfer[0].tx_buf = &req->ref_on;  		req->xfer[0].len = 1; @@ -323,8 +335,14 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)  		/* for 1uF, settle for 800 usec; no cap, 100 usec.  */  		req->xfer[1].delay_usecs = ts->vref_delay_usecs;  		spi_message_add_tail(&req->xfer[1], &req->msg); + +		/* Enable reference voltage */ +		command |= ADS_PD10_REF_ON;  	} +	/* Enable ADC in every case */ +	command |= ADS_PD10_ADC_ON; +  	/* take sample */  	req->command = (u8) command;  	req->xfer[2].tx_buf = &req->command; @@ -401,14 +419,14 @@ static int ads7845_read12_ser(struct device *dev, unsigned command)  	return status;  } -#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) +#if IS_ENABLED(CONFIG_HWMON)  #define SHOW(name, var, adjust) static ssize_t \  name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \  { \  	struct ads7846 *ts = dev_get_drvdata(dev); \ -	ssize_t v = ads7846_read12_ser(dev, \ -			READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \ +	ssize_t v = ads7846_read12_ser(&ts->spi->dev, \ +			READ_12BIT_SER(var)); \  	if (v < 0) \  		return v; \  	return sprintf(buf, "%u\n", adjust(ts, v)); \ @@ -459,48 +477,43 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)  SHOW(in0_input, vaux, vaux_adjust)  SHOW(in1_input, vbatt, vbatt_adjust) -static struct attribute *ads7846_attributes[] = { -	&dev_attr_temp0.attr, -	&dev_attr_temp1.attr, -	&dev_attr_in0_input.attr, -	&dev_attr_in1_input.attr, -	NULL, -}; - -static struct attribute_group ads7846_attr_group = { -	.attrs = ads7846_attributes, -}; +static umode_t ads7846_is_visible(struct kobject *kobj, struct attribute *attr, +				  int index) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct ads7846 *ts = dev_get_drvdata(dev); -static struct attribute *ads7843_attributes[] = { -	&dev_attr_in0_input.attr, -	&dev_attr_in1_input.attr, -	NULL, -}; +	if (ts->model == 7843 && index < 2)	/* in0, in1 */ +		return 0; +	if (ts->model == 7845 && index != 2)	/* in0 */ +		return 0; -static struct attribute_group ads7843_attr_group = { -	.attrs = ads7843_attributes, -}; +	return attr->mode; +} -static struct attribute *ads7845_attributes[] = { -	&dev_attr_in0_input.attr, +static struct attribute *ads7846_attributes[] = { +	&dev_attr_temp0.attr,		/* 0 */ +	&dev_attr_temp1.attr,		/* 1 */ +	&dev_attr_in0_input.attr,	/* 2 */ +	&dev_attr_in1_input.attr,	/* 3 */  	NULL,  }; -static struct attribute_group ads7845_attr_group = { -	.attrs = ads7845_attributes, +static struct attribute_group ads7846_attr_group = { +	.attrs = ads7846_attributes, +	.is_visible = ads7846_is_visible,  }; +__ATTRIBUTE_GROUPS(ads7846_attr);  static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)  { -	struct device *hwmon; -	int err; -  	/* hwmon sensors need a reference voltage */  	switch (ts->model) {  	case 7846:  		if (!ts->vref_mv) {  			dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");  			ts->vref_mv = 2500; +			ts->use_internal = true;  		}  		break;  	case 7845: @@ -514,43 +527,19 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)  		break;  	} -	/* different chips have different sensor groups */ -	switch (ts->model) { -	case 7846: -		ts->attr_group = &ads7846_attr_group; -		break; -	case 7845: -		ts->attr_group = &ads7845_attr_group; -		break; -	case 7843: -		ts->attr_group = &ads7843_attr_group; -		break; -	default: -		dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model); -		return 0; -	} - -	err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); -	if (err) -		return err; +	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); -	hwmon = hwmon_device_register(&spi->dev); -	if (IS_ERR(hwmon)) { -		sysfs_remove_group(&spi->dev.kobj, ts->attr_group); -		return PTR_ERR(hwmon); -	} - -	ts->hwmon = hwmon;  	return 0;  }  static void ads784x_hwmon_unregister(struct spi_device *spi,  				     struct ads7846 *ts)  { -	if (ts->hwmon) { -		sysfs_remove_group(&spi->dev.kobj, ts->attr_group); +	if (ts->hwmon)  		hwmon_device_unregister(ts->hwmon); -	}  }  #else @@ -589,10 +578,12 @@ static ssize_t ads7846_disable_store(struct device *dev,  				     const char *buf, size_t count)  {  	struct ads7846 *ts = dev_get_drvdata(dev); -	unsigned long i; +	unsigned int i; +	int err; -	if (strict_strtoul(buf, 10, &i)) -		return -EINVAL; +	err = kstrtouint(buf, 10, &i); +	if (err) +		return err;  	if (i)  		ads7846_disable(ts); @@ -715,7 +706,7 @@ static void ads7846_read_state(struct ads7846 *ts)  		m = &ts->msg[msg_idx];  		error = spi_sync(ts->spi, m);  		if (error) { -			dev_err(&ts->spi->dev, "spi_async --> %d\n", error); +			dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);  			packet->tc.ignore = true;  			return;  		} @@ -892,9 +883,10 @@ static irqreturn_t ads7846_irq(int irq, void *handle)  	return IRQ_HANDLED;  } -static int ads7846_suspend(struct spi_device *spi, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int ads7846_suspend(struct device *dev)  { -	struct ads7846 *ts = dev_get_drvdata(&spi->dev); +	struct ads7846 *ts = dev_get_drvdata(dev);  	mutex_lock(&ts->lock); @@ -914,9 +906,9 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message)  	return 0;  } -static int ads7846_resume(struct spi_device *spi) +static int ads7846_resume(struct device *dev)  { -	struct ads7846 *ts = dev_get_drvdata(&spi->dev); +	struct ads7846 *ts = dev_get_drvdata(dev);  	mutex_lock(&ts->lock); @@ -935,34 +927,44 @@ static int ads7846_resume(struct spi_device *spi)  	return 0;  } +#endif -static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) +static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume); + +static int ads7846_setup_pendown(struct spi_device *spi, +				 struct ads7846 *ts, +				 const struct ads7846_platform_data *pdata)  { -	struct ads7846_platform_data *pdata = spi->dev.platform_data;  	int err; -	/* REVISIT when the irq can be triggered active-low, or if for some +	/* +	 * REVISIT when the irq can be triggered active-low, or if for some  	 * reason the touchscreen isn't hooked up, we don't need to access  	 * the pendown state.  	 */ -	if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) { -		dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); -		return -EINVAL; -	}  	if (pdata->get_pendown_state) {  		ts->get_pendown_state = pdata->get_pendown_state; -		return 0; -	} +	} else if (gpio_is_valid(pdata->gpio_pendown)) { + +		err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN, +				       "ads7846_pendown"); +		if (err) { +			dev_err(&spi->dev, +				"failed to request/setup pendown GPIO%d: %d\n", +				pdata->gpio_pendown, err); +			return err; +		} -	err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); -	if (err) { -		dev_err(&spi->dev, "failed to request pendown GPIO%d\n", -			pdata->gpio_pendown); -		return err; -	} +		ts->gpio_pendown = pdata->gpio_pendown; -	ts->gpio_pendown = pdata->gpio_pendown; +		if (pdata->gpio_pendown_debounce) +			gpio_set_debounce(pdata->gpio_pendown, +					  pdata->gpio_pendown_debounce); +	} else { +		dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); +		return -EINVAL; +	}  	return 0;  } @@ -971,8 +973,8 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784   * Set up the transfers to read touchscreen state; this assumes we   * use formula #2 for pressure, not #3.   */ -static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, -				const struct ads7846_platform_data *pdata) +static void ads7846_setup_spi_msg(struct ads7846 *ts, +				  const struct ads7846_platform_data *pdata)  {  	struct spi_message *m = &ts->msg[0];  	struct spi_transfer *x = ts->xfer; @@ -1170,33 +1172,107 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts,  	spi_message_add_tail(x, m);  } -static int __devinit ads7846_probe(struct spi_device *spi) +#ifdef CONFIG_OF +static const struct of_device_id ads7846_dt_ids[] = { +	{ .compatible = "ti,tsc2046",	.data = (void *) 7846 }, +	{ .compatible = "ti,ads7843",	.data = (void *) 7843 }, +	{ .compatible = "ti,ads7845",	.data = (void *) 7845 }, +	{ .compatible = "ti,ads7846",	.data = (void *) 7846 }, +	{ .compatible = "ti,ads7873",	.data = (void *) 7873 }, +	{ } +}; +MODULE_DEVICE_TABLE(of, ads7846_dt_ids); + +static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) +{ +	struct ads7846_platform_data *pdata; +	struct device_node *node = dev->of_node; +	const struct of_device_id *match; + +	if (!node) { +		dev_err(dev, "Device does not have associated DT data\n"); +		return ERR_PTR(-EINVAL); +	} + +	match = of_match_device(ads7846_dt_ids, dev); +	if (!match) { +		dev_err(dev, "Unknown device model\n"); +		return ERR_PTR(-EINVAL); +	} + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return ERR_PTR(-ENOMEM); + +	pdata->model = (unsigned long)match->data; + +	of_property_read_u16(node, "ti,vref-delay-usecs", +			     &pdata->vref_delay_usecs); +	of_property_read_u16(node, "ti,vref-mv", &pdata->vref_mv); +	pdata->keep_vref_on = of_property_read_bool(node, "ti,keep-vref-on"); + +	pdata->swap_xy = of_property_read_bool(node, "ti,swap-xy"); + +	of_property_read_u16(node, "ti,settle-delay-usec", +			     &pdata->settle_delay_usecs); +	of_property_read_u16(node, "ti,penirq-recheck-delay-usecs", +			     &pdata->penirq_recheck_delay_usecs); + +	of_property_read_u16(node, "ti,x-plate-ohms", &pdata->x_plate_ohms); +	of_property_read_u16(node, "ti,y-plate-ohms", &pdata->y_plate_ohms); + +	of_property_read_u16(node, "ti,x-min", &pdata->x_min); +	of_property_read_u16(node, "ti,y-min", &pdata->y_min); +	of_property_read_u16(node, "ti,x-max", &pdata->x_max); +	of_property_read_u16(node, "ti,y-max", &pdata->y_max); + +	of_property_read_u16(node, "ti,pressure-min", &pdata->pressure_min); +	of_property_read_u16(node, "ti,pressure-max", &pdata->pressure_max); + +	of_property_read_u16(node, "ti,debounce-max", &pdata->debounce_max); +	of_property_read_u16(node, "ti,debounce-tol", &pdata->debounce_tol); +	of_property_read_u16(node, "ti,debounce-rep", &pdata->debounce_rep); + +	of_property_read_u32(node, "ti,pendown-gpio-debounce", +			     &pdata->gpio_pendown_debounce); + +	pdata->wakeup = of_property_read_bool(node, "linux,wakeup"); + +	pdata->gpio_pendown = of_get_named_gpio(dev->of_node, "pendown-gpio", 0); + +	return pdata; +} +#else +static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) +{ +	dev_err(dev, "no platform data defined\n"); +	return ERR_PTR(-EINVAL); +} +#endif + +static int ads7846_probe(struct spi_device *spi)  { +	const struct ads7846_platform_data *pdata;  	struct ads7846 *ts;  	struct ads7846_packet *packet;  	struct input_dev *input_dev; -	struct ads7846_platform_data *pdata = spi->dev.platform_data;  	unsigned long irq_flags;  	int err;  	if (!spi->irq) {  		dev_dbg(&spi->dev, "no IRQ?\n"); -		return -ENODEV; -	} - -	if (!pdata) { -		dev_dbg(&spi->dev, "no platform data?\n"); -		return -ENODEV; +		return -EINVAL;  	}  	/* don't exceed max specified sample rate */  	if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { -		dev_dbg(&spi->dev, "f(sample) %d KHz?\n", +		dev_err(&spi->dev, "f(sample) %d KHz?\n",  				(spi->max_speed_hz/SAMPLE_BITS)/1000);  		return -EINVAL;  	} -	/* We'd set TX word size 8 bits and RX word size to 13 bits ... except +	/* +	 * We'd set TX word size 8 bits and RX word size to 13 bits ... except  	 * that even if the hardware can do that, the SPI controller driver  	 * may not.  So we stick to very-portable 8 bit words, both RX and TX.  	 */ @@ -1214,22 +1290,30 @@ static int __devinit ads7846_probe(struct spi_device *spi)  		goto err_free_mem;  	} -	dev_set_drvdata(&spi->dev, ts); +	spi_set_drvdata(spi, ts);  	ts->packet = packet;  	ts->spi = spi;  	ts->input = input_dev; -	ts->vref_mv = pdata->vref_mv; -	ts->swap_xy = pdata->swap_xy;  	mutex_init(&ts->lock);  	init_waitqueue_head(&ts->wait); +	pdata = dev_get_platdata(&spi->dev); +	if (!pdata) { +		pdata = ads7846_probe_dt(&spi->dev); +		if (IS_ERR(pdata)) +			return PTR_ERR(pdata); +	} +  	ts->model = pdata->model ? : 7846;  	ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;  	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;  	ts->pressure_max = pdata->pressure_max ? : ~0; +	ts->vref_mv = pdata->vref_mv; +	ts->swap_xy = pdata->swap_xy; +  	if (pdata->filter != NULL) {  		if (pdata->filter_init != NULL) {  			err = pdata->filter_init(pdata, &ts->filter_data); @@ -1250,7 +1334,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  		ts->filter = ads7846_no_filter;  	} -	err = ads7846_setup_pendown(spi, ts); +	err = ads7846_setup_pendown(spi, ts, pdata);  	if (err)  		goto err_cleanup_filter; @@ -1327,8 +1411,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	if (ts->model == 7845)  		ads7845_read12_ser(&spi->dev, PWRDOWN);  	else -		(void) ads7846_read12_ser(&spi->dev, -				READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); +		(void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));  	err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);  	if (err) @@ -1340,6 +1423,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	device_init_wakeup(&spi->dev, pdata->wakeup); +	/* +	 * If device does not carry platform data we must have allocated it +	 * when parsing DT data. +	 */ +	if (!dev_get_platdata(&spi->dev)) +		devm_kfree(&spi->dev, (void *)pdata); +  	return 0;   err_remove_attr_group: @@ -1353,7 +1443,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)   err_put_regulator:  	regulator_put(ts->reg);   err_free_gpio: -	if (ts->gpio_pendown != -1) +	if (!ts->get_pendown_state)  		gpio_free(ts->gpio_pendown);   err_cleanup_filter:  	if (ts->filter_cleanup) @@ -1365,9 +1455,9 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	return err;  } -static int __devexit ads7846_remove(struct spi_device *spi) +static int ads7846_remove(struct spi_device *spi)  { -	struct ads7846 *ts = dev_get_drvdata(&spi->dev); +	struct ads7846 *ts = spi_get_drvdata(spi);  	device_init_wakeup(&spi->dev, false); @@ -1383,8 +1473,13 @@ static int __devexit ads7846_remove(struct spi_device *spi)  	regulator_disable(ts->reg);  	regulator_put(ts->reg); -	if (ts->gpio_pendown != -1) +	if (!ts->get_pendown_state) { +		/* +		 * If we are not using specialized pendown method we must +		 * have been relying on gpio we set up ourselves. +		 */  		gpio_free(ts->gpio_pendown); +	}  	if (ts->filter_cleanup)  		ts->filter_cleanup(ts->filter_data); @@ -1400,26 +1495,15 @@ static int __devexit ads7846_remove(struct spi_device *spi)  static struct spi_driver ads7846_driver = {  	.driver = {  		.name	= "ads7846", -		.bus	= &spi_bus_type,  		.owner	= THIS_MODULE, +		.pm	= &ads7846_pm, +		.of_match_table = of_match_ptr(ads7846_dt_ids),  	},  	.probe		= ads7846_probe, -	.remove		= __devexit_p(ads7846_remove), -	.suspend	= ads7846_suspend, -	.resume		= ads7846_resume, +	.remove		= ads7846_remove,  }; -static int __init ads7846_init(void) -{ -	return spi_register_driver(&ads7846_driver); -} -module_init(ads7846_init); - -static void __exit ads7846_exit(void) -{ -	spi_unregister_driver(&ads7846_driver); -} -module_exit(ads7846_exit); +module_spi_driver(ads7846_driver);  MODULE_DESCRIPTION("ADS7846 TouchScreen Driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index fa8e56bd909..279c0e42b8a 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -164,7 +164,7 @@ static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id)  		data = ac97c_readl(atmel_wm97xx, CBRHR);  		value = data & 0x0fff; -		source = data & WM97XX_ADCSRC_MASK; +		source = data & WM97XX_ADCSEL_MASK;  		pen_down = (data & WM97XX_PEN_DOWN) >> 8;  		if (source == WM97XX_ADCSEL_X) @@ -372,7 +372,6 @@ static int __init atmel_wm97xx_probe(struct platform_device *pdev)  err_irq:  	free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);  err: -	platform_set_drvdata(pdev, NULL);  	kfree(atmel_wm97xx);  	return ret;  } @@ -386,15 +385,15 @@ static int __exit atmel_wm97xx_remove(struct platform_device *pdev)  	free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);  	del_timer_sync(&atmel_wm97xx->pen_timer);  	wm97xx_unregister_mach_ops(wm); -	platform_set_drvdata(pdev, NULL);  	kfree(atmel_wm97xx);  	return 0;  } -#ifdef CONFIG_PM -static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg) +#ifdef CONFIG_PM_SLEEP +static int atmel_wm97xx_suspend(struct device *dev)  { +	struct platform_device *pdev = to_platform_device(dev);  	struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);  	ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); @@ -404,8 +403,9 @@ static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg)  	return 0;  } -static int atmel_wm97xx_resume(struct platform_device *pdev) +static int atmel_wm97xx_resume(struct device *dev)  { +	struct platform_device *pdev = to_platform_device(dev);  	struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);  	struct wm97xx *wm = atmel_wm97xx->wm; @@ -416,32 +416,22 @@ static int atmel_wm97xx_resume(struct platform_device *pdev)  	return 0;  } -#else -#define atmel_wm97xx_suspend	NULL -#define atmel_wm97xx_resume	NULL  #endif +static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops, +			 atmel_wm97xx_suspend, atmel_wm97xx_resume); +  static struct platform_driver atmel_wm97xx_driver = {  	.remove		= __exit_p(atmel_wm97xx_remove),  	.driver		= { -		.name = "wm97xx-touch", +		.name	= "wm97xx-touch", +		.owner	= THIS_MODULE, +		.pm	= &atmel_wm97xx_pm_ops,  	}, -	.suspend	= atmel_wm97xx_suspend, -	.resume		= atmel_wm97xx_resume,  }; -static int __init atmel_wm97xx_init(void) -{ -	return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe); -} -module_init(atmel_wm97xx_init); - -static void __exit atmel_wm97xx_exit(void) -{ -	platform_driver_unregister(&atmel_wm97xx_driver); -} -module_exit(atmel_wm97xx_exit); +module_platform_driver_probe(atmel_wm97xx_driver, atmel_wm97xx_probe); -MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>"); +MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");  MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");  MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c new file mode 100644 index 00000000000..6e0b4a2120d --- /dev/null +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -0,0 +1,1619 @@ +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2012 Google, Inc. + * + * Author: Joonyoung Shim <jy0922.shim@samsung.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. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c/atmel_mxt_ts.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +/* Version */ +#define MXT_VER_20		20 +#define MXT_VER_21		21 +#define MXT_VER_22		22 + +/* Firmware */ +#define MXT_FW_NAME		"maxtouch.fw" + +/* Registers */ +#define MXT_INFO		0x00 +#define MXT_FAMILY_ID		0x00 +#define MXT_VARIANT_ID		0x01 +#define MXT_VERSION		0x02 +#define MXT_BUILD		0x03 +#define MXT_MATRIX_X_SIZE	0x04 +#define MXT_MATRIX_Y_SIZE	0x05 +#define MXT_OBJECT_NUM		0x06 +#define MXT_OBJECT_START	0x07 + +#define MXT_OBJECT_SIZE		6 + +/* Object types */ +#define MXT_DEBUG_DIAGNOSTIC_T37	37 +#define MXT_GEN_MESSAGE_T5		5 +#define MXT_GEN_COMMAND_T6		6 +#define MXT_GEN_POWER_T7		7 +#define MXT_GEN_ACQUIRE_T8		8 +#define MXT_GEN_DATASOURCE_T53		53 +#define MXT_TOUCH_MULTI_T9		9 +#define MXT_TOUCH_KEYARRAY_T15		15 +#define MXT_TOUCH_PROXIMITY_T23		23 +#define MXT_TOUCH_PROXKEY_T52		52 +#define MXT_PROCI_GRIPFACE_T20		20 +#define MXT_PROCG_NOISE_T22		22 +#define MXT_PROCI_ONETOUCH_T24		24 +#define MXT_PROCI_TWOTOUCH_T27		27 +#define MXT_PROCI_GRIP_T40		40 +#define MXT_PROCI_PALM_T41		41 +#define MXT_PROCI_TOUCHSUPPRESSION_T42	42 +#define MXT_PROCI_STYLUS_T47		47 +#define MXT_PROCG_NOISESUPPRESSION_T48	48 +#define MXT_SPT_COMMSCONFIG_T18		18 +#define MXT_SPT_GPIOPWM_T19		19 +#define MXT_SPT_SELFTEST_T25		25 +#define MXT_SPT_CTECONFIG_T28		28 +#define MXT_SPT_USERDATA_T38		38 +#define MXT_SPT_DIGITIZER_T43		43 +#define MXT_SPT_MESSAGECOUNT_T44	44 +#define MXT_SPT_CTECONFIG_T46		46 + +/* MXT_GEN_COMMAND_T6 field */ +#define MXT_COMMAND_RESET	0 +#define MXT_COMMAND_BACKUPNV	1 +#define MXT_COMMAND_CALIBRATE	2 +#define MXT_COMMAND_REPORTALL	3 +#define MXT_COMMAND_DIAGNOSTIC	5 + +/* Define for T6 status byte */ +#define MXT_T6_STATUS_RESET	(1 << 7) + +/* MXT_GEN_POWER_T7 field */ +#define MXT_POWER_IDLEACQINT	0 +#define MXT_POWER_ACTVACQINT	1 +#define MXT_POWER_ACTV2IDLETO	2 + +/* MXT_GEN_ACQUIRE_T8 field */ +#define MXT_ACQUIRE_CHRGTIME	0 +#define MXT_ACQUIRE_TCHDRIFT	2 +#define MXT_ACQUIRE_DRIFTST	3 +#define MXT_ACQUIRE_TCHAUTOCAL	4 +#define MXT_ACQUIRE_SYNC	5 +#define MXT_ACQUIRE_ATCHCALST	6 +#define MXT_ACQUIRE_ATCHCALSTHR	7 + +/* MXT_TOUCH_MULTI_T9 field */ +#define MXT_TOUCH_CTRL		0 +#define MXT_T9_ORIENT		9 +#define MXT_T9_RANGE		18 + +/* MXT_TOUCH_MULTI_T9 status */ +#define MXT_T9_UNGRIP		(1 << 0) +#define MXT_T9_SUPPRESS		(1 << 1) +#define MXT_T9_AMP		(1 << 2) +#define MXT_T9_VECTOR		(1 << 3) +#define MXT_T9_MOVE		(1 << 4) +#define MXT_T9_RELEASE		(1 << 5) +#define MXT_T9_PRESS		(1 << 6) +#define MXT_T9_DETECT		(1 << 7) + +struct t9_range { +	u16 x; +	u16 y; +} __packed; + +/* MXT_TOUCH_MULTI_T9 orient */ +#define MXT_T9_ORIENT_SWITCH	(1 << 0) + +/* MXT_PROCI_GRIPFACE_T20 field */ +#define MXT_GRIPFACE_CTRL	0 +#define MXT_GRIPFACE_XLOGRIP	1 +#define MXT_GRIPFACE_XHIGRIP	2 +#define MXT_GRIPFACE_YLOGRIP	3 +#define MXT_GRIPFACE_YHIGRIP	4 +#define MXT_GRIPFACE_MAXTCHS	5 +#define MXT_GRIPFACE_SZTHR1	7 +#define MXT_GRIPFACE_SZTHR2	8 +#define MXT_GRIPFACE_SHPTHR1	9 +#define MXT_GRIPFACE_SHPTHR2	10 +#define MXT_GRIPFACE_SUPEXTTO	11 + +/* MXT_PROCI_NOISE field */ +#define MXT_NOISE_CTRL		0 +#define MXT_NOISE_OUTFLEN	1 +#define MXT_NOISE_GCAFUL_LSB	3 +#define MXT_NOISE_GCAFUL_MSB	4 +#define MXT_NOISE_GCAFLL_LSB	5 +#define MXT_NOISE_GCAFLL_MSB	6 +#define MXT_NOISE_ACTVGCAFVALID	7 +#define MXT_NOISE_NOISETHR	8 +#define MXT_NOISE_FREQHOPSCALE	10 +#define MXT_NOISE_FREQ0		11 +#define MXT_NOISE_FREQ1		12 +#define MXT_NOISE_FREQ2		13 +#define MXT_NOISE_FREQ3		14 +#define MXT_NOISE_FREQ4		15 +#define MXT_NOISE_IDLEGCAFVALID	16 + +/* MXT_SPT_COMMSCONFIG_T18 */ +#define MXT_COMMS_CTRL		0 +#define MXT_COMMS_CMD		1 + +/* MXT_SPT_CTECONFIG_T28 field */ +#define MXT_CTE_CTRL		0 +#define MXT_CTE_CMD		1 +#define MXT_CTE_MODE		2 +#define MXT_CTE_IDLEGCAFDEPTH	3 +#define MXT_CTE_ACTVGCAFDEPTH	4 +#define MXT_CTE_VOLTAGE		5 + +#define MXT_VOLTAGE_DEFAULT	2700000 +#define MXT_VOLTAGE_STEP	10000 + +/* Define for MXT_GEN_COMMAND_T6 */ +#define MXT_BOOT_VALUE		0xa5 +#define MXT_RESET_VALUE		0x01 +#define MXT_BACKUP_VALUE	0x55 + +/* Delay times */ +#define MXT_BACKUP_TIME		50	/* msec */ +#define MXT_RESET_TIME		200	/* msec */ +#define MXT_RESET_TIMEOUT	3000	/* msec */ +#define MXT_CRC_TIMEOUT		1000	/* msec */ +#define MXT_FW_RESET_TIME	3000	/* msec */ +#define MXT_FW_CHG_TIMEOUT	300	/* msec */ + +/* Command to unlock bootloader */ +#define MXT_UNLOCK_CMD_MSB	0xaa +#define MXT_UNLOCK_CMD_LSB	0xdc + +/* Bootloader mode status */ +#define MXT_WAITING_BOOTLOAD_CMD	0xc0	/* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA	0x80	/* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK	0x02 +#define MXT_FRAME_CRC_FAIL	0x03 +#define MXT_FRAME_CRC_PASS	0x04 +#define MXT_APP_CRC_FAIL	0x40	/* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK	0x3f +#define MXT_BOOT_EXTENDED_ID	(1 << 5) +#define MXT_BOOT_ID_MASK	0x1f + +/* Touchscreen absolute values */ +#define MXT_MAX_AREA		0xff + +#define MXT_PIXELS_PER_MM	20 + +struct mxt_info { +	u8 family_id; +	u8 variant_id; +	u8 version; +	u8 build; +	u8 matrix_xsize; +	u8 matrix_ysize; +	u8 object_num; +}; + +struct mxt_object { +	u8 type; +	u16 start_address; +	u8 size_minus_one; +	u8 instances_minus_one; +	u8 num_report_ids; +} __packed; + +struct mxt_message { +	u8 reportid; +	u8 message[7]; +}; + +/* Each client has this additional data */ +struct mxt_data { +	struct i2c_client *client; +	struct input_dev *input_dev; +	char phys[64];		/* device physical location */ +	const struct mxt_platform_data *pdata; +	struct mxt_object *object_table; +	struct mxt_info info; +	unsigned int irq; +	unsigned int max_x; +	unsigned int max_y; +	bool in_bootloader; +	u32 config_crc; +	u8 bootloader_addr; + +	/* Cached parameters from object table */ +	u8 T6_reportid; +	u16 T6_address; +	u8 T9_reportid_min; +	u8 T9_reportid_max; +	u8 T19_reportid; + +	/* for fw update in bootloader */ +	struct completion bl_completion; + +	/* for reset handling */ +	struct completion reset_completion; + +	/* for config update handling */ +	struct completion crc_completion; +}; + +static size_t mxt_obj_size(const struct mxt_object *obj) +{ +	return obj->size_minus_one + 1; +} + +static size_t mxt_obj_instances(const struct mxt_object *obj) +{ +	return obj->instances_minus_one + 1; +} + +static bool mxt_object_readable(unsigned int type) +{ +	switch (type) { +	case MXT_GEN_COMMAND_T6: +	case MXT_GEN_POWER_T7: +	case MXT_GEN_ACQUIRE_T8: +	case MXT_GEN_DATASOURCE_T53: +	case MXT_TOUCH_MULTI_T9: +	case MXT_TOUCH_KEYARRAY_T15: +	case MXT_TOUCH_PROXIMITY_T23: +	case MXT_TOUCH_PROXKEY_T52: +	case MXT_PROCI_GRIPFACE_T20: +	case MXT_PROCG_NOISE_T22: +	case MXT_PROCI_ONETOUCH_T24: +	case MXT_PROCI_TWOTOUCH_T27: +	case MXT_PROCI_GRIP_T40: +	case MXT_PROCI_PALM_T41: +	case MXT_PROCI_TOUCHSUPPRESSION_T42: +	case MXT_PROCI_STYLUS_T47: +	case MXT_PROCG_NOISESUPPRESSION_T48: +	case MXT_SPT_COMMSCONFIG_T18: +	case MXT_SPT_GPIOPWM_T19: +	case MXT_SPT_SELFTEST_T25: +	case MXT_SPT_CTECONFIG_T28: +	case MXT_SPT_USERDATA_T38: +	case MXT_SPT_DIGITIZER_T43: +	case MXT_SPT_CTECONFIG_T46: +		return true; +	default: +		return false; +	} +} + +static bool mxt_object_writable(unsigned int type) +{ +	switch (type) { +	case MXT_GEN_COMMAND_T6: +	case MXT_GEN_POWER_T7: +	case MXT_GEN_ACQUIRE_T8: +	case MXT_TOUCH_MULTI_T9: +	case MXT_TOUCH_KEYARRAY_T15: +	case MXT_TOUCH_PROXIMITY_T23: +	case MXT_TOUCH_PROXKEY_T52: +	case MXT_PROCI_GRIPFACE_T20: +	case MXT_PROCG_NOISE_T22: +	case MXT_PROCI_ONETOUCH_T24: +	case MXT_PROCI_TWOTOUCH_T27: +	case MXT_PROCI_GRIP_T40: +	case MXT_PROCI_PALM_T41: +	case MXT_PROCI_TOUCHSUPPRESSION_T42: +	case MXT_PROCI_STYLUS_T47: +	case MXT_PROCG_NOISESUPPRESSION_T48: +	case MXT_SPT_COMMSCONFIG_T18: +	case MXT_SPT_GPIOPWM_T19: +	case MXT_SPT_SELFTEST_T25: +	case MXT_SPT_CTECONFIG_T28: +	case MXT_SPT_DIGITIZER_T43: +	case MXT_SPT_CTECONFIG_T46: +		return true; +	default: +		return false; +	} +} + +static void mxt_dump_message(struct device *dev, +			     struct mxt_message *message) +{ +	dev_dbg(dev, "reportid: %u\tmessage: %*ph\n", +		message->reportid, 7, message->message); +} + +static int mxt_wait_for_completion(struct mxt_data *data, +				   struct completion *comp, +				   unsigned int timeout_ms) +{ +	struct device *dev = &data->client->dev; +	unsigned long timeout = msecs_to_jiffies(timeout_ms); +	long ret; + +	ret = wait_for_completion_interruptible_timeout(comp, timeout); +	if (ret < 0) { +		return ret; +	} else if (ret == 0) { +		dev_err(dev, "Wait for completion timed out.\n"); +		return -ETIMEDOUT; +	} +	return 0; +} + +static int mxt_bootloader_read(struct mxt_data *data, +			       u8 *val, unsigned int count) +{ +	int ret; +	struct i2c_msg msg; + +	msg.addr = data->bootloader_addr; +	msg.flags = data->client->flags & I2C_M_TEN; +	msg.flags |= I2C_M_RD; +	msg.len = count; +	msg.buf = val; + +	ret = i2c_transfer(data->client->adapter, &msg, 1); + +	if (ret == 1) { +		ret = 0; +	} else { +		ret = ret < 0 ? ret : -EIO; +		dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n", +			__func__, ret); +	} + +	return ret; +} + +static int mxt_bootloader_write(struct mxt_data *data, +				const u8 * const val, unsigned int count) +{ +	int ret; +	struct i2c_msg msg; + +	msg.addr = data->bootloader_addr; +	msg.flags = data->client->flags & I2C_M_TEN; +	msg.len = count; +	msg.buf = (u8 *)val; + +	ret = i2c_transfer(data->client->adapter, &msg, 1); +	if (ret == 1) { +		ret = 0; +	} else { +		ret = ret < 0 ? ret : -EIO; +		dev_err(&data->client->dev, "%s: i2c send failed (%d)\n", +			__func__, ret); +	} + +	return ret; +} + +static int mxt_lookup_bootloader_address(struct mxt_data *data) +{ +	u8 appmode = data->client->addr; +	u8 bootloader; + +	switch (appmode) { +	case 0x4a: +	case 0x4b: +	case 0x4c: +	case 0x4d: +	case 0x5a: +	case 0x5b: +		bootloader = appmode - 0x26; +		break; +	default: +		dev_err(&data->client->dev, +			"Appmode i2c address 0x%02x not found\n", +			appmode); +		return -EINVAL; +	} + +	data->bootloader_addr = bootloader; +	return 0; +} + +static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) +{ +	struct device *dev = &data->client->dev; +	u8 buf[3]; + +	if (val & MXT_BOOT_EXTENDED_ID) { +		if (mxt_bootloader_read(data, &buf[0], 3) != 0) { +			dev_err(dev, "%s: i2c failure\n", __func__); +			return val; +		} + +		dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); + +		return buf[0]; +	} else { +		dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); + +		return val; +	} +} + +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) +{ +	struct device *dev = &data->client->dev; +	u8 val; +	int ret; + +recheck: +	if (state != MXT_WAITING_BOOTLOAD_CMD) { +		/* +		 * In application update mode, the interrupt +		 * line signals state transitions. We must wait for the +		 * CHG assertion before reading the status byte. +		 * Once the status byte has been read, the line is deasserted. +		 */ +		ret = mxt_wait_for_completion(data, &data->bl_completion, +					      MXT_FW_CHG_TIMEOUT); +		if (ret) { +			/* +			 * TODO: handle -ERESTARTSYS better by terminating +			 * fw update process before returning to userspace +			 * by writing length 0x000 to device (iff we are in +			 * WAITING_FRAME_DATA state). +			 */ +			dev_err(dev, "Update wait error %d\n", ret); +			return ret; +		} +	} + +	ret = mxt_bootloader_read(data, &val, 1); +	if (ret) +		return ret; + +	if (state == MXT_WAITING_BOOTLOAD_CMD) +		val = mxt_get_bootloader_version(data, val); + +	switch (state) { +	case MXT_WAITING_BOOTLOAD_CMD: +	case MXT_WAITING_FRAME_DATA: +		val &= ~MXT_BOOT_STATUS_MASK; +		break; +	case MXT_FRAME_CRC_PASS: +		if (val == MXT_FRAME_CRC_CHECK) { +			goto recheck; +		} else if (val == MXT_FRAME_CRC_FAIL) { +			dev_err(dev, "Bootloader CRC fail\n"); +			return -EINVAL; +		} +		break; +	default: +		return -EINVAL; +	} + +	if (val != state) { +		dev_err(dev, "Invalid bootloader state %02X != %02X\n", +			val, state); +		return -EINVAL; +	} + +	return 0; +} + +static int mxt_unlock_bootloader(struct mxt_data *data) +{ +	int ret; +	u8 buf[2]; + +	buf[0] = MXT_UNLOCK_CMD_LSB; +	buf[1] = MXT_UNLOCK_CMD_MSB; + +	ret = mxt_bootloader_write(data, buf, 2); +	if (ret) +		return ret; + +	return 0; +} + +static int __mxt_read_reg(struct i2c_client *client, +			       u16 reg, u16 len, void *val) +{ +	struct i2c_msg xfer[2]; +	u8 buf[2]; +	int ret; + +	buf[0] = reg & 0xff; +	buf[1] = (reg >> 8) & 0xff; + +	/* Write register */ +	xfer[0].addr = client->addr; +	xfer[0].flags = 0; +	xfer[0].len = 2; +	xfer[0].buf = buf; + +	/* Read data */ +	xfer[1].addr = client->addr; +	xfer[1].flags = I2C_M_RD; +	xfer[1].len = len; +	xfer[1].buf = val; + +	ret = i2c_transfer(client->adapter, xfer, 2); +	if (ret == 2) { +		ret = 0; +	} else { +		if (ret >= 0) +			ret = -EIO; +		dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", +			__func__, ret); +	} + +	return ret; +} + +static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, +			   const void *val) +{ +	u8 *buf; +	size_t count; +	int ret; + +	count = len + 2; +	buf = kmalloc(count, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	buf[0] = reg & 0xff; +	buf[1] = (reg >> 8) & 0xff; +	memcpy(&buf[2], val, len); + +	ret = i2c_master_send(client, buf, count); +	if (ret == count) { +		ret = 0; +	} else { +		if (ret >= 0) +			ret = -EIO; +		dev_err(&client->dev, "%s: i2c send failed (%d)\n", +			__func__, ret); +	} + +	kfree(buf); +	return ret; +} + +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ +	return __mxt_write_reg(client, reg, 1, &val); +} + +static struct mxt_object * +mxt_get_object(struct mxt_data *data, u8 type) +{ +	struct mxt_object *object; +	int i; + +	for (i = 0; i < data->info.object_num; i++) { +		object = data->object_table + i; +		if (object->type == type) +			return object; +	} + +	dev_err(&data->client->dev, "Invalid object type T%u\n", type); +	return NULL; +} + +static int mxt_read_message(struct mxt_data *data, +				 struct mxt_message *message) +{ +	struct mxt_object *object; +	u16 reg; + +	object = mxt_get_object(data, MXT_GEN_MESSAGE_T5); +	if (!object) +		return -EINVAL; + +	reg = object->start_address; +	return __mxt_read_reg(data->client, reg, +			sizeof(struct mxt_message), message); +} + +static int mxt_write_object(struct mxt_data *data, +				 u8 type, u8 offset, u8 val) +{ +	struct mxt_object *object; +	u16 reg; + +	object = mxt_get_object(data, type); +	if (!object || offset >= mxt_obj_size(object)) +		return -EINVAL; + +	reg = object->start_address; +	return mxt_write_reg(data->client, reg + offset, val); +} + +static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) +{ +	struct input_dev *input = data->input_dev; +	const struct mxt_platform_data *pdata = data->pdata; +	bool button; +	int i; + +	/* Active-low switch */ +	for (i = 0; i < pdata->t19_num_keys; i++) { +		if (pdata->t19_keymap[i] == KEY_RESERVED) +			continue; +		button = !(message->message[0] & (1 << i)); +		input_report_key(input, pdata->t19_keymap[i], button); +	} +} + +static void mxt_input_sync(struct input_dev *input_dev) +{ +	input_mt_report_pointer_emulation(input_dev, false); +	input_sync(input_dev); +} + +static void mxt_input_touchevent(struct mxt_data *data, +				      struct mxt_message *message, int id) +{ +	struct device *dev = &data->client->dev; +	u8 status = message->message[0]; +	struct input_dev *input_dev = data->input_dev; +	int x; +	int y; +	int area; +	int amplitude; + +	x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); +	y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + +	/* Handle 10/12 bit switching */ +	if (data->max_x < 1024) +		x >>= 2; +	if (data->max_y < 1024) +		y >>= 2; + +	area = message->message[4]; +	amplitude = message->message[5]; + +	dev_dbg(dev, +		"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", +		id, +		(status & MXT_T9_DETECT) ? 'D' : '.', +		(status & MXT_T9_PRESS) ? 'P' : '.', +		(status & MXT_T9_RELEASE) ? 'R' : '.', +		(status & MXT_T9_MOVE) ? 'M' : '.', +		(status & MXT_T9_VECTOR) ? 'V' : '.', +		(status & MXT_T9_AMP) ? 'A' : '.', +		(status & MXT_T9_SUPPRESS) ? 'S' : '.', +		(status & MXT_T9_UNGRIP) ? 'U' : '.', +		x, y, area, amplitude); + +	input_mt_slot(input_dev, id); + +	if (status & MXT_T9_DETECT) { +		/* +		 * Multiple bits may be set if the host is slow to read +		 * the status messages, indicating all the events that +		 * have happened. +		 */ +		if (status & MXT_T9_RELEASE) { +			input_mt_report_slot_state(input_dev, +						   MT_TOOL_FINGER, 0); +			mxt_input_sync(input_dev); +		} + +		/* Touch active */ +		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); +		input_report_abs(input_dev, ABS_MT_POSITION_X, x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, y); +		input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); +		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); +	} else { +		/* Touch no longer active, close out slot */ +		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); +	} +} + +static u16 mxt_extract_T6_csum(const u8 *csum) +{ +	return csum[0] | (csum[1] << 8) | (csum[2] << 16); +} + +static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) +{ +	u8 id = msg->reportid; +	return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); +} + +static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) +{ +	struct mxt_message message; +	const u8 *payload = &message.message[0]; +	struct device *dev = &data->client->dev; +	u8 reportid; +	bool update_input = false; +	u32 crc; + +	do { +		if (mxt_read_message(data, &message)) { +			dev_err(dev, "Failed to read message\n"); +			return IRQ_NONE; +		} + +		reportid = message.reportid; + +		if (reportid == data->T6_reportid) { +			u8 status = payload[0]; + +			crc = mxt_extract_T6_csum(&payload[1]); +			if (crc != data->config_crc) { +				data->config_crc = crc; +				complete(&data->crc_completion); +			} + +			dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", +				status, data->config_crc); + +			if (status & MXT_T6_STATUS_RESET) +				complete(&data->reset_completion); +		} else if (mxt_is_T9_message(data, &message)) { +			int id = reportid - data->T9_reportid_min; +			mxt_input_touchevent(data, &message, id); +			update_input = true; +		} else if (message.reportid == data->T19_reportid) { +			mxt_input_button(data, &message); +			update_input = true; +		} else { +			mxt_dump_message(dev, &message); +		} +	} while (reportid != 0xff); + +	if (update_input) +		mxt_input_sync(data->input_dev); + +	return IRQ_HANDLED; +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ +	struct mxt_data *data = dev_id; + +	if (data->in_bootloader) { +		/* bootloader state transition completion */ +		complete(&data->bl_completion); +		return IRQ_HANDLED; +	} + +	return mxt_process_messages_until_invalid(data); +} + +static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, +			  u8 value, bool wait) +{ +	u16 reg; +	u8 command_register; +	int timeout_counter = 0; +	int ret; + +	reg = data->T6_address + cmd_offset; + +	ret = mxt_write_reg(data->client, reg, value); +	if (ret) +		return ret; + +	if (!wait) +		return 0; + +	do { +		msleep(20); +		ret = __mxt_read_reg(data->client, reg, 1, &command_register); +		if (ret) +			return ret; +	} while (command_register != 0 && timeout_counter++ <= 100); + +	if (timeout_counter > 100) { +		dev_err(&data->client->dev, "Command failed!\n"); +		return -EIO; +	} + +	return 0; +} + +static int mxt_soft_reset(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	int ret = 0; + +	dev_info(dev, "Resetting chip\n"); + +	reinit_completion(&data->reset_completion); + +	ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); +	if (ret) +		return ret; + +	ret = mxt_wait_for_completion(data, &data->reset_completion, +				      MXT_RESET_TIMEOUT); +	if (ret) +		return ret; + +	return 0; +} + +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ +	/* +	 * On failure, CRC is set to 0 and config will always be +	 * downloaded. +	 */ +	data->config_crc = 0; +	reinit_completion(&data->crc_completion); + +	mxt_t6_command(data, cmd, value, true); + +	/* +	 * Wait for crc message. On failure, CRC is set to 0 and config will +	 * always be downloaded. +	 */ +	mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + +static int mxt_check_reg_init(struct mxt_data *data) +{ +	const struct mxt_platform_data *pdata = data->pdata; +	struct mxt_object *object; +	struct device *dev = &data->client->dev; +	int index = 0; +	int i, size; +	int ret; + +	if (!pdata->config) { +		dev_dbg(dev, "No cfg data defined, skipping reg init\n"); +		return 0; +	} + +	mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + +	if (data->config_crc == pdata->config_crc) { +		dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); +		return 0; +	} + +	dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", +		 data->config_crc, pdata->config_crc); + +	for (i = 0; i < data->info.object_num; i++) { +		object = data->object_table + i; + +		if (!mxt_object_writable(object->type)) +			continue; + +		size = mxt_obj_size(object) * mxt_obj_instances(object); +		if (index + size > pdata->config_length) { +			dev_err(dev, "Not enough config data!\n"); +			return -EINVAL; +		} + +		ret = __mxt_write_reg(data->client, object->start_address, +				size, &pdata->config[index]); +		if (ret) +			return ret; +		index += size; +	} + +	mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + +	ret = mxt_soft_reset(data); +	if (ret) +		return ret; + +	dev_info(dev, "Config successfully updated\n"); + +	return 0; +} + +static int mxt_make_highchg(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	struct mxt_message message; +	int count = 10; +	int error; + +	/* Read dummy message to make high CHG pin */ +	do { +		error = mxt_read_message(data, &message); +		if (error) +			return error; +	} while (message.reportid != 0xff && --count); + +	if (!count) { +		dev_err(dev, "CHG pin isn't cleared\n"); +		return -EBUSY; +	} + +	return 0; +} + +static int mxt_get_info(struct mxt_data *data) +{ +	struct i2c_client *client = data->client; +	struct mxt_info *info = &data->info; +	int error; + +	/* Read 7-byte info block starting at address 0 */ +	error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info); +	if (error) +		return error; + +	return 0; +} + +static int mxt_get_object_table(struct mxt_data *data) +{ +	struct i2c_client *client = data->client; +	size_t table_size; +	int error; +	int i; +	u8 reportid; + +	table_size = data->info.object_num * sizeof(struct mxt_object); +	error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, +			data->object_table); +	if (error) +		return error; + +	/* Valid Report IDs start counting from 1 */ +	reportid = 1; +	for (i = 0; i < data->info.object_num; i++) { +		struct mxt_object *object = data->object_table + i; +		u8 min_id, max_id; + +		le16_to_cpus(&object->start_address); + +		if (object->num_report_ids) { +			min_id = reportid; +			reportid += object->num_report_ids * +					mxt_obj_instances(object); +			max_id = reportid - 1; +		} else { +			min_id = 0; +			max_id = 0; +		} + +		dev_dbg(&data->client->dev, +			"T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n", +			object->type, object->start_address, +			mxt_obj_size(object), mxt_obj_instances(object), +			min_id, max_id); + +		switch (object->type) { +		case MXT_GEN_COMMAND_T6: +			data->T6_reportid = min_id; +			data->T6_address = object->start_address; +			break; +		case MXT_TOUCH_MULTI_T9: +			data->T9_reportid_min = min_id; +			data->T9_reportid_max = max_id; +			break; +		case MXT_SPT_GPIOPWM_T19: +			data->T19_reportid = min_id; +			break; +		} +	} + +	return 0; +} + +static void mxt_free_object_table(struct mxt_data *data) +{ +	kfree(data->object_table); +	data->object_table = NULL; +	data->T6_reportid = 0; +	data->T9_reportid_min = 0; +	data->T9_reportid_max = 0; +	data->T19_reportid = 0; +} + +static int mxt_read_t9_resolution(struct mxt_data *data) +{ +	struct i2c_client *client = data->client; +	int error; +	struct t9_range range; +	unsigned char orient; +	struct mxt_object *object; + +	object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); +	if (!object) +		return -EINVAL; + +	error = __mxt_read_reg(client, +			       object->start_address + MXT_T9_RANGE, +			       sizeof(range), &range); +	if (error) +		return error; + +	le16_to_cpus(&range.x); +	le16_to_cpus(&range.y); + +	error =  __mxt_read_reg(client, +				object->start_address + MXT_T9_ORIENT, +				1, &orient); +	if (error) +		return error; + +	/* Handle default values */ +	if (range.x == 0) +		range.x = 1023; + +	if (range.y == 0) +		range.y = 1023; + +	if (orient & MXT_T9_ORIENT_SWITCH) { +		data->max_x = range.y; +		data->max_y = range.x; +	} else { +		data->max_x = range.x; +		data->max_y = range.y; +	} + +	dev_dbg(&client->dev, +		"Touchscreen size X%uY%u\n", data->max_x, data->max_y); + +	return 0; +} + +static int mxt_initialize(struct mxt_data *data) +{ +	struct i2c_client *client = data->client; +	struct mxt_info *info = &data->info; +	int error; + +	error = mxt_get_info(data); +	if (error) +		return error; + +	data->object_table = kcalloc(info->object_num, +				     sizeof(struct mxt_object), +				     GFP_KERNEL); +	if (!data->object_table) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	/* Get object table information */ +	error = mxt_get_object_table(data); +	if (error) { +		dev_err(&client->dev, "Error %d reading object table\n", error); +		goto err_free_object_table; +	} + +	/* Check register init values */ +	error = mxt_check_reg_init(data); +	if (error) { +		dev_err(&client->dev, "Error %d initializing configuration\n", +			error); +		goto err_free_object_table; +	} + +	error = mxt_read_t9_resolution(data); +	if (error) { +		dev_err(&client->dev, "Failed to initialize T9 resolution\n"); +		goto err_free_object_table; +	} + +	dev_info(&client->dev, +		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", +		 info->family_id, info->variant_id, info->version >> 4, +		 info->version & 0xf, info->build, info->object_num); + +	return 0; + +err_free_object_table: +	mxt_free_object_table(data); +	return error; +} + +/* Firmware Version is returned as Major.Minor.Build */ +static ssize_t mxt_fw_version_show(struct device *dev, +				   struct device_attribute *attr, char *buf) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	struct mxt_info *info = &data->info; +	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", +			 info->version >> 4, info->version & 0xf, info->build); +} + +/* Hardware Version is returned as FamilyID.VariantID */ +static ssize_t mxt_hw_version_show(struct device *dev, +				   struct device_attribute *attr, char *buf) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	struct mxt_info *info = &data->info; +	return scnprintf(buf, PAGE_SIZE, "%u.%u\n", +			 info->family_id, info->variant_id); +} + +static ssize_t mxt_show_instance(char *buf, int count, +				 struct mxt_object *object, int instance, +				 const u8 *val) +{ +	int i; + +	if (mxt_obj_instances(object) > 1) +		count += scnprintf(buf + count, PAGE_SIZE - count, +				   "Instance %u\n", instance); + +	for (i = 0; i < mxt_obj_size(object); i++) +		count += scnprintf(buf + count, PAGE_SIZE - count, +				"\t[%2u]: %02x (%d)\n", i, val[i], val[i]); +	count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); + +	return count; +} + +static ssize_t mxt_object_show(struct device *dev, +				    struct device_attribute *attr, char *buf) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	struct mxt_object *object; +	int count = 0; +	int i, j; +	int error; +	u8 *obuf; + +	/* Pre-allocate buffer large enough to hold max sized object. */ +	obuf = kmalloc(256, GFP_KERNEL); +	if (!obuf) +		return -ENOMEM; + +	error = 0; +	for (i = 0; i < data->info.object_num; i++) { +		object = data->object_table + i; + +		if (!mxt_object_readable(object->type)) +			continue; + +		count += scnprintf(buf + count, PAGE_SIZE - count, +				"T%u:\n", object->type); + +		for (j = 0; j < mxt_obj_instances(object); j++) { +			u16 size = mxt_obj_size(object); +			u16 addr = object->start_address + j * size; + +			error = __mxt_read_reg(data->client, addr, size, obuf); +			if (error) +				goto done; + +			count = mxt_show_instance(buf, count, object, j, obuf); +		} +	} + +done: +	kfree(obuf); +	return error ?: count; +} + +static int mxt_check_firmware_format(struct device *dev, +				     const struct firmware *fw) +{ +	unsigned int pos = 0; +	char c; + +	while (pos < fw->size) { +		c = *(fw->data + pos); + +		if (c < '0' || (c > '9' && c < 'A') || c > 'F') +			return 0; + +		pos++; +	} + +	/* +	 * To convert file try: +	 * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw +	 */ +	dev_err(dev, "Aborting: firmware file must be in binary format\n"); + +	return -EINVAL; +} + +static int mxt_load_fw(struct device *dev, const char *fn) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	const struct firmware *fw = NULL; +	unsigned int frame_size; +	unsigned int pos = 0; +	unsigned int retry = 0; +	unsigned int frame = 0; +	int ret; + +	ret = request_firmware(&fw, fn, dev); +	if (ret) { +		dev_err(dev, "Unable to open firmware %s\n", fn); +		return ret; +	} + +	/* Check for incorrect enc file */ +	ret = mxt_check_firmware_format(dev, fw); +	if (ret) +		goto release_firmware; + +	ret = mxt_lookup_bootloader_address(data); +	if (ret) +		goto release_firmware; + +	/* Change to the bootloader mode */ +	data->in_bootloader = true; + +	ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); +	if (ret) +		goto release_firmware; + +	msleep(MXT_RESET_TIME); + +	reinit_completion(&data->bl_completion); + +	ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); +	if (ret) +		goto disable_irq; + +	/* Unlock bootloader */ +	mxt_unlock_bootloader(data); + +	while (pos < fw->size) { +		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); +		if (ret) +			goto disable_irq; + +		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + +		/* Take account of CRC bytes */ +		frame_size += 2; + +		/* Write one frame to device */ +		ret = mxt_bootloader_write(data, fw->data + pos, frame_size); +		if (ret) +			goto disable_irq; + +		ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); +		if (ret) { +			retry++; + +			/* Back off by 20ms per retry */ +			msleep(retry * 20); + +			if (retry > 20) { +				dev_err(dev, "Retry count exceeded\n"); +				goto disable_irq; +			} +		} else { +			retry = 0; +			pos += frame_size; +			frame++; +		} + +		if (frame % 50 == 0) +			dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n", +				frame, pos, fw->size); +	} + +	/* Wait for flash. */ +	ret = mxt_wait_for_completion(data, &data->bl_completion, +				      MXT_FW_RESET_TIME); +	if (ret) +		goto disable_irq; + +	dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); + +	/* +	 * Wait for device to reset. Some bootloader versions do not assert +	 * the CHG line after bootloading has finished, so ignore potential +	 * errors. +	 */ +	mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); + +	data->in_bootloader = false; + +disable_irq: +	disable_irq(data->irq); +release_firmware: +	release_firmware(fw); +	return ret; +} + +static ssize_t mxt_update_fw_store(struct device *dev, +					struct device_attribute *attr, +					const char *buf, size_t count) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	int error; + +	error = mxt_load_fw(dev, MXT_FW_NAME); +	if (error) { +		dev_err(dev, "The firmware update failed(%d)\n", error); +		count = error; +	} else { +		dev_info(dev, "The firmware update succeeded\n"); + +		mxt_free_object_table(data); + +		mxt_initialize(data); + +		enable_irq(data->irq); + +		error = mxt_make_highchg(data); +		if (error) +			return error; +	} + +	return count; +} + +static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); +static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); +static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); + +static struct attribute *mxt_attrs[] = { +	&dev_attr_fw_version.attr, +	&dev_attr_hw_version.attr, +	&dev_attr_object.attr, +	&dev_attr_update_fw.attr, +	NULL +}; + +static const struct attribute_group mxt_attr_group = { +	.attrs = mxt_attrs, +}; + +static void mxt_start(struct mxt_data *data) +{ +	/* Touch enable */ +	mxt_write_object(data, +			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); +} + +static void mxt_stop(struct mxt_data *data) +{ +	/* Touch disable */ +	mxt_write_object(data, +			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); +} + +static int mxt_input_open(struct input_dev *dev) +{ +	struct mxt_data *data = input_get_drvdata(dev); + +	mxt_start(data); + +	return 0; +} + +static void mxt_input_close(struct input_dev *dev) +{ +	struct mxt_data *data = input_get_drvdata(dev); + +	mxt_stop(data); +} + +static int mxt_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); +	struct mxt_data *data; +	struct input_dev *input_dev; +	int error; +	unsigned int num_mt_slots; +	unsigned int mt_flags = 0; +	int i; + +	if (!pdata) +		return -EINVAL; + +	data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!data || !input_dev) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		error = -ENOMEM; +		goto err_free_mem; +	} + +	input_dev->name = "Atmel maXTouch Touchscreen"; +	snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", +		 client->adapter->nr, client->addr); + +	input_dev->phys = data->phys; + +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &client->dev; +	input_dev->open = mxt_input_open; +	input_dev->close = mxt_input_close; + +	data->client = client; +	data->input_dev = input_dev; +	data->pdata = pdata; +	data->irq = client->irq; + +	init_completion(&data->bl_completion); +	init_completion(&data->reset_completion); +	init_completion(&data->crc_completion); + +	error = mxt_initialize(data); +	if (error) +		goto err_free_mem; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); + +	if (pdata->t19_num_keys) { +		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + +		for (i = 0; i < pdata->t19_num_keys; i++) +			if (pdata->t19_keymap[i] != KEY_RESERVED) +				input_set_capability(input_dev, EV_KEY, +						     pdata->t19_keymap[i]); + +		mt_flags |= INPUT_MT_POINTER; + +		input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); +		input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); +		input_abs_set_res(input_dev, ABS_MT_POSITION_X, +				  MXT_PIXELS_PER_MM); +		input_abs_set_res(input_dev, ABS_MT_POSITION_Y, +				  MXT_PIXELS_PER_MM); + +		input_dev->name = "Atmel maXTouch Touchpad"; +	} + +	/* For single touch */ +	input_set_abs_params(input_dev, ABS_X, +			     0, data->max_x, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, +			     0, data->max_y, 0, 0); +	input_set_abs_params(input_dev, ABS_PRESSURE, +			     0, 255, 0, 0); + +	/* For multi touch */ +	num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; +	error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); +	if (error) +		goto err_free_object; +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, +			     0, MXT_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, +			     0, data->max_x, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, +			     0, data->max_y, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_PRESSURE, +			     0, 255, 0, 0); + +	input_set_drvdata(input_dev, data); +	i2c_set_clientdata(client, data); + +	error = request_threaded_irq(client->irq, NULL, mxt_interrupt, +				     pdata->irqflags | IRQF_ONESHOT, +				     client->name, data); +	if (error) { +		dev_err(&client->dev, "Failed to register interrupt\n"); +		goto err_free_object; +	} + +	error = mxt_make_highchg(data); +	if (error) +		goto err_free_irq; + +	error = input_register_device(input_dev); +	if (error) { +		dev_err(&client->dev, "Error %d registering input device\n", +			error); +		goto err_free_irq; +	} + +	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); +	if (error) { +		dev_err(&client->dev, "Failure %d creating sysfs group\n", +			error); +		goto err_unregister_device; +	} + +	return 0; + +err_unregister_device: +	input_unregister_device(input_dev); +	input_dev = NULL; +err_free_irq: +	free_irq(client->irq, data); +err_free_object: +	kfree(data->object_table); +err_free_mem: +	input_free_device(input_dev); +	kfree(data); +	return error; +} + +static int mxt_remove(struct i2c_client *client) +{ +	struct mxt_data *data = i2c_get_clientdata(client); + +	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); +	free_irq(data->irq, data); +	input_unregister_device(data->input_dev); +	kfree(data->object_table); +	kfree(data); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mxt_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct mxt_data *data = i2c_get_clientdata(client); +	struct input_dev *input_dev = data->input_dev; + +	mutex_lock(&input_dev->mutex); + +	if (input_dev->users) +		mxt_stop(data); + +	mutex_unlock(&input_dev->mutex); + +	return 0; +} + +static int mxt_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct mxt_data *data = i2c_get_clientdata(client); +	struct input_dev *input_dev = data->input_dev; + +	mxt_soft_reset(data); + +	mutex_lock(&input_dev->mutex); + +	if (input_dev->users) +		mxt_start(data); + +	mutex_unlock(&input_dev->mutex); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); + +static const struct i2c_device_id mxt_id[] = { +	{ "qt602240_ts", 0 }, +	{ "atmel_mxt_ts", 0 }, +	{ "atmel_mxt_tp", 0 }, +	{ "mXT224", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, mxt_id); + +static struct i2c_driver mxt_driver = { +	.driver = { +		.name	= "atmel_mxt_ts", +		.owner	= THIS_MODULE, +		.pm	= &mxt_pm_ops, +	}, +	.probe		= mxt_probe, +	.remove		= mxt_remove, +	.id_table	= mxt_id, +}; + +module_i2c_driver(mxt_driver); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c deleted file mode 100644 index 3d9b5166ebe..00000000000 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - *  Atmel Touch Screen Driver - * - *  Copyright (c) 2008 ATMEL - *  Copyright (c) 2008 Dan Liang - *  Copyright (c) 2008 TimeSys Corporation - *  Copyright (c) 2008 Justin Waters - * - *  Based on touchscreen code from Atmel Corporation. - * - *  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/init.h> -#include <linux/err.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/input.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/clk.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <mach/board.h> -#include <mach/cpu.h> - -/* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */ - -#define ATMEL_TSADCC_CR		0x00	/* Control register */ -#define   ATMEL_TSADCC_SWRST	(1 << 0)	/* Software Reset*/ -#define	  ATMEL_TSADCC_START	(1 << 1)	/* Start conversion */ - -#define ATMEL_TSADCC_MR		0x04	/* Mode register */ -#define	  ATMEL_TSADCC_TSAMOD	(3    <<  0)	/* ADC mode */ -#define	    ATMEL_TSADCC_TSAMOD_ADC_ONLY_MODE	(0x0)	/* ADC Mode */ -#define	    ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE	(0x1)	/* Touch Screen Only Mode */ -#define	  ATMEL_TSADCC_LOWRES	(1    <<  4)	/* Resolution selection */ -#define	  ATMEL_TSADCC_SLEEP	(1    <<  5)	/* Sleep mode */ -#define	  ATMEL_TSADCC_PENDET	(1    <<  6)	/* Pen Detect selection */ -#define	  ATMEL_TSADCC_PRES	(1    <<  7)	/* Pressure Measurement Selection */ -#define	  ATMEL_TSADCC_PRESCAL	(0x3f <<  8)	/* Prescalar Rate Selection */ -#define	  ATMEL_TSADCC_EPRESCAL	(0xff <<  8)	/* Prescalar Rate Selection (Extended) */ -#define	  ATMEL_TSADCC_STARTUP	(0x7f << 16)	/* Start Up time */ -#define	  ATMEL_TSADCC_SHTIM	(0xf  << 24)	/* Sample & Hold time */ -#define	  ATMEL_TSADCC_PENDBC	(0xf  << 28)	/* Pen Detect debouncing time */ - -#define ATMEL_TSADCC_TRGR	0x08	/* Trigger register */ -#define	  ATMEL_TSADCC_TRGMOD	(7      <<  0)	/* Trigger mode */ -#define	    ATMEL_TSADCC_TRGMOD_NONE		(0 << 0) -#define     ATMEL_TSADCC_TRGMOD_EXT_RISING	(1 << 0) -#define     ATMEL_TSADCC_TRGMOD_EXT_FALLING	(2 << 0) -#define     ATMEL_TSADCC_TRGMOD_EXT_ANY		(3 << 0) -#define     ATMEL_TSADCC_TRGMOD_PENDET		(4 << 0) -#define     ATMEL_TSADCC_TRGMOD_PERIOD		(5 << 0) -#define     ATMEL_TSADCC_TRGMOD_CONTINUOUS	(6 << 0) -#define   ATMEL_TSADCC_TRGPER	(0xffff << 16)	/* Trigger period */ - -#define ATMEL_TSADCC_TSR	0x0C	/* Touch Screen register */ -#define	  ATMEL_TSADCC_TSFREQ	(0xf <<  0)	/* TS Frequency in Interleaved mode */ -#define	  ATMEL_TSADCC_TSSHTIM	(0xf << 24)	/* Sample & Hold time */ - -#define ATMEL_TSADCC_CHER	0x10	/* Channel Enable register */ -#define ATMEL_TSADCC_CHDR	0x14	/* Channel Disable register */ -#define ATMEL_TSADCC_CHSR	0x18	/* Channel Status register */ -#define	  ATMEL_TSADCC_CH(n)	(1 << (n))	/* Channel number */ - -#define ATMEL_TSADCC_SR		0x1C	/* Status register */ -#define	  ATMEL_TSADCC_EOC(n)	(1 << ((n)+0))	/* End of conversion for channel N */ -#define	  ATMEL_TSADCC_OVRE(n)	(1 << ((n)+8))	/* Overrun error for channel N */ -#define	  ATMEL_TSADCC_DRDY	(1 << 16)	/* Data Ready */ -#define	  ATMEL_TSADCC_GOVRE	(1 << 17)	/* General Overrun Error */ -#define	  ATMEL_TSADCC_ENDRX	(1 << 18)	/* End of RX Buffer */ -#define	  ATMEL_TSADCC_RXBUFF	(1 << 19)	/* TX Buffer full */ -#define	  ATMEL_TSADCC_PENCNT	(1 << 20)	/* Pen contact */ -#define	  ATMEL_TSADCC_NOCNT	(1 << 21)	/* No contact */ - -#define ATMEL_TSADCC_LCDR	0x20	/* Last Converted Data register */ -#define	  ATMEL_TSADCC_DATA	(0x3ff << 0)	/* Channel data */ - -#define ATMEL_TSADCC_IER	0x24	/* Interrupt Enable register */ -#define ATMEL_TSADCC_IDR	0x28	/* Interrupt Disable register */ -#define ATMEL_TSADCC_IMR	0x2C	/* Interrupt Mask register */ -#define ATMEL_TSADCC_CDR0	0x30	/* Channel Data 0 */ -#define ATMEL_TSADCC_CDR1	0x34	/* Channel Data 1 */ -#define ATMEL_TSADCC_CDR2	0x38	/* Channel Data 2 */ -#define ATMEL_TSADCC_CDR3	0x3C	/* Channel Data 3 */ -#define ATMEL_TSADCC_CDR4	0x40	/* Channel Data 4 */ -#define ATMEL_TSADCC_CDR5	0x44	/* Channel Data 5 */ - -#define ATMEL_TSADCC_XPOS	0x50 -#define ATMEL_TSADCC_Z1DAT	0x54 -#define ATMEL_TSADCC_Z2DAT	0x58 - -#define PRESCALER_VAL(x)	((x) >> 8) - -#define ADC_DEFAULT_CLOCK	100000 - -struct atmel_tsadcc { -	struct input_dev	*input; -	char			phys[32]; -	struct clk		*clk; -	int			irq; -	unsigned int		prev_absx; -	unsigned int		prev_absy; -	unsigned char		bufferedmeasure; -}; - -static void __iomem		*tsc_base; - -#define atmel_tsadcc_read(reg)		__raw_readl(tsc_base + (reg)) -#define atmel_tsadcc_write(reg, val)	__raw_writel((val), tsc_base + (reg)) - -static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev) -{ -	struct atmel_tsadcc	*ts_dev = (struct atmel_tsadcc *)dev; -	struct input_dev	*input_dev = ts_dev->input; - -	unsigned int status; -	unsigned int reg; - -	status = atmel_tsadcc_read(ATMEL_TSADCC_SR); -	status &= atmel_tsadcc_read(ATMEL_TSADCC_IMR); - -	if (status & ATMEL_TSADCC_NOCNT) { -		/* Contact lost */ -		reg = atmel_tsadcc_read(ATMEL_TSADCC_MR) | ATMEL_TSADCC_PENDBC; - -		atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); -		atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE); -		atmel_tsadcc_write(ATMEL_TSADCC_IDR, -				   ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT); -		atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT); - -		input_report_key(input_dev, BTN_TOUCH, 0); -		ts_dev->bufferedmeasure = 0; -		input_sync(input_dev); - -	} else if (status & ATMEL_TSADCC_PENCNT) { -		/* Pen detected */ -		reg = atmel_tsadcc_read(ATMEL_TSADCC_MR); -		reg &= ~ATMEL_TSADCC_PENDBC; - -		atmel_tsadcc_write(ATMEL_TSADCC_IDR, ATMEL_TSADCC_PENCNT); -		atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); -		atmel_tsadcc_write(ATMEL_TSADCC_IER, -				   ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT); -		atmel_tsadcc_write(ATMEL_TSADCC_TRGR, -				   ATMEL_TSADCC_TRGMOD_PERIOD | (0x0FFF << 16)); - -	} else if (status & ATMEL_TSADCC_EOC(3)) { -		/* Conversion finished */ - -		if (ts_dev->bufferedmeasure) { -			/* Last measurement is always discarded, since it can -			 * be erroneous. -			 * Always report previous measurement */ -			input_report_abs(input_dev, ABS_X, ts_dev->prev_absx); -			input_report_abs(input_dev, ABS_Y, ts_dev->prev_absy); -			input_report_key(input_dev, BTN_TOUCH, 1); -			input_sync(input_dev); -		} else -			ts_dev->bufferedmeasure = 1; - -		/* Now make new measurement */ -		ts_dev->prev_absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10; -		ts_dev->prev_absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2); - -		ts_dev->prev_absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10; -		ts_dev->prev_absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0); -	} - -	return IRQ_HANDLED; -} - -/* - * The functions for inserting/removing us as a module. - */ - -static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) -{ -	struct atmel_tsadcc	*ts_dev; -	struct input_dev	*input_dev; -	struct resource		*res; -	struct at91_tsadcc_data *pdata = pdev->dev.platform_data; -	int		err = 0; -	unsigned int	prsc; -	unsigned int	reg; - -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!res) { -		dev_err(&pdev->dev, "no mmio resource defined.\n"); -		return -ENXIO; -	} - -	/* Allocate memory for device */ -	ts_dev = kzalloc(sizeof(struct atmel_tsadcc), GFP_KERNEL); -	if (!ts_dev) { -		dev_err(&pdev->dev, "failed to allocate memory.\n"); -		return -ENOMEM; -	} -	platform_set_drvdata(pdev, ts_dev); - -	input_dev = input_allocate_device(); -	if (!input_dev) { -		dev_err(&pdev->dev, "failed to allocate input device.\n"); -		err = -EBUSY; -		goto err_free_mem; -	} - -	ts_dev->irq = platform_get_irq(pdev, 0); -	if (ts_dev->irq < 0) { -		dev_err(&pdev->dev, "no irq ID is designated.\n"); -		err = -ENODEV; -		goto err_free_dev; -	} - -	if (!request_mem_region(res->start, resource_size(res), -				"atmel tsadcc regs")) { -		dev_err(&pdev->dev, "resources is unavailable.\n"); -		err = -EBUSY; -		goto err_free_dev; -	} - -	tsc_base = ioremap(res->start, resource_size(res)); -	if (!tsc_base) { -		dev_err(&pdev->dev, "failed to map registers.\n"); -		err = -ENOMEM; -		goto err_release_mem; -	} - -	err = request_irq(ts_dev->irq, atmel_tsadcc_interrupt, IRQF_DISABLED, -			pdev->dev.driver->name, ts_dev); -	if (err) { -		dev_err(&pdev->dev, "failed to allocate irq.\n"); -		goto err_unmap_regs; -	} - -	ts_dev->clk = clk_get(&pdev->dev, "tsc_clk"); -	if (IS_ERR(ts_dev->clk)) { -		dev_err(&pdev->dev, "failed to get ts_clk\n"); -		err = PTR_ERR(ts_dev->clk); -		goto err_free_irq; -	} - -	ts_dev->input = input_dev; -	ts_dev->bufferedmeasure = 0; - -	snprintf(ts_dev->phys, sizeof(ts_dev->phys), -		 "%s/input0", dev_name(&pdev->dev)); - -	input_dev->name = "atmel touch screen controller"; -	input_dev->phys = ts_dev->phys; -	input_dev->dev.parent = &pdev->dev; - -	__set_bit(EV_ABS, input_dev->evbit); -	input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0); -	input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0); - -	input_set_capability(input_dev, EV_KEY, BTN_TOUCH); - -	/* clk_enable() always returns 0, no need to check it */ -	clk_enable(ts_dev->clk); - -	prsc = clk_get_rate(ts_dev->clk); -	dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc); - -	if (!pdata) -		goto err_fail; - -	if (!pdata->adc_clock) -		pdata->adc_clock = ADC_DEFAULT_CLOCK; - -	prsc = (prsc / (2 * pdata->adc_clock)) - 1; - -	/* saturate if this value is too high */ -	if (cpu_is_at91sam9rl()) { -		if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL)) -			prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL); -	} else { -		if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL)) -			prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL); -	} - -	dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc); - -	reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE		| -		((0x00 << 5) & ATMEL_TSADCC_SLEEP)	|	/* Normal Mode */ -		((0x01 << 6) & ATMEL_TSADCC_PENDET)	|	/* Enable Pen Detect */ -		(prsc << 8)				| -		((0x26 << 16) & ATMEL_TSADCC_STARTUP)	| -		((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC); - -	atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST); -	atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); -	atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE); -	atmel_tsadcc_write(ATMEL_TSADCC_TSR, -		(pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM); - -	atmel_tsadcc_read(ATMEL_TSADCC_SR); -	atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT); - -	/* All went ok, so register to the input system */ -	err = input_register_device(input_dev); -	if (err) -		goto err_fail; - -	return 0; - -err_fail: -	clk_disable(ts_dev->clk); -	clk_put(ts_dev->clk); -err_free_irq: -	free_irq(ts_dev->irq, ts_dev); -err_unmap_regs: -	iounmap(tsc_base); -err_release_mem: -	release_mem_region(res->start, resource_size(res)); -err_free_dev: -	input_free_device(ts_dev->input); -err_free_mem: -	kfree(ts_dev); -	return err; -} - -static int __devexit atmel_tsadcc_remove(struct platform_device *pdev) -{ -	struct atmel_tsadcc *ts_dev = dev_get_drvdata(&pdev->dev); -	struct resource *res; - -	free_irq(ts_dev->irq, ts_dev); - -	input_unregister_device(ts_dev->input); - -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	iounmap(tsc_base); -	release_mem_region(res->start, resource_size(res)); - -	clk_disable(ts_dev->clk); -	clk_put(ts_dev->clk); - -	kfree(ts_dev); - -	return 0; -} - -static struct platform_driver atmel_tsadcc_driver = { -	.probe		= atmel_tsadcc_probe, -	.remove		= __devexit_p(atmel_tsadcc_remove), -	.driver		= { -		.name	= "atmel_tsadcc", -	}, -}; - -static int __init atmel_tsadcc_init(void) -{ -	return platform_driver_register(&atmel_tsadcc_driver); -} - -static void __exit atmel_tsadcc_exit(void) -{ -	platform_driver_unregister(&atmel_tsadcc_driver); -} - -module_init(atmel_tsadcc_init); -module_exit(atmel_tsadcc_exit); - - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Atmel TouchScreen Driver"); -MODULE_AUTHOR("Dan Liang <dan.liang@atmel.com>"); - diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c new file mode 100644 index 00000000000..7f3c9478778 --- /dev/null +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -0,0 +1,704 @@ +/* + * Driver for AUO in-cell touchscreens + * + * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de> + * + * loosely based on auo_touch.c from Dell Streak vendor-kernel + * + * Copyright (c) 2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, INC. + * + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/input/auo-pixcir-ts.h> +#include <linux/of.h> +#include <linux/of_gpio.h> + +/* + * Coordinate calculation: + * X1 = X1_LSB + X1_MSB*256 + * Y1 = Y1_LSB + Y1_MSB*256 + * X2 = X2_LSB + X2_MSB*256 + * Y2 = Y2_LSB + Y2_MSB*256 + */ +#define AUO_PIXCIR_REG_X1_LSB		0x00 +#define AUO_PIXCIR_REG_X1_MSB		0x01 +#define AUO_PIXCIR_REG_Y1_LSB		0x02 +#define AUO_PIXCIR_REG_Y1_MSB		0x03 +#define AUO_PIXCIR_REG_X2_LSB		0x04 +#define AUO_PIXCIR_REG_X2_MSB		0x05 +#define AUO_PIXCIR_REG_Y2_LSB		0x06 +#define AUO_PIXCIR_REG_Y2_MSB		0x07 + +#define AUO_PIXCIR_REG_STRENGTH		0x0d +#define AUO_PIXCIR_REG_STRENGTH_X1_LSB	0x0e +#define AUO_PIXCIR_REG_STRENGTH_X1_MSB	0x0f + +#define AUO_PIXCIR_REG_RAW_DATA_X	0x2b +#define AUO_PIXCIR_REG_RAW_DATA_Y	0x4f + +#define AUO_PIXCIR_REG_X_SENSITIVITY	0x6f +#define AUO_PIXCIR_REG_Y_SENSITIVITY	0x70 +#define AUO_PIXCIR_REG_INT_SETTING	0x71 +#define AUO_PIXCIR_REG_INT_WIDTH	0x72 +#define AUO_PIXCIR_REG_POWER_MODE	0x73 + +#define AUO_PIXCIR_REG_VERSION		0x77 +#define AUO_PIXCIR_REG_CALIBRATE	0x78 + +#define AUO_PIXCIR_REG_TOUCHAREA_X1	0x1e +#define AUO_PIXCIR_REG_TOUCHAREA_Y1	0x1f +#define AUO_PIXCIR_REG_TOUCHAREA_X2	0x20 +#define AUO_PIXCIR_REG_TOUCHAREA_Y2	0x21 + +#define AUO_PIXCIR_REG_EEPROM_CALIB_X	0x42 +#define AUO_PIXCIR_REG_EEPROM_CALIB_Y	0xad + +#define AUO_PIXCIR_INT_TPNUM_MASK	0xe0 +#define AUO_PIXCIR_INT_TPNUM_SHIFT	5 +#define AUO_PIXCIR_INT_RELEASE		(1 << 4) +#define AUO_PIXCIR_INT_ENABLE		(1 << 3) +#define AUO_PIXCIR_INT_POL_HIGH		(1 << 2) +#define AUO_PIXCIR_INT_MODE_MASK	0x03 + +/* + * Power modes: + * active:	scan speed 60Hz + * sleep:	scan speed 10Hz can be auto-activated, wakeup on 1st touch + * deep sleep:	scan speed 1Hz can only be entered or left manually. + */ +#define AUO_PIXCIR_POWER_ACTIVE		0x00 +#define AUO_PIXCIR_POWER_SLEEP		0x01 +#define AUO_PIXCIR_POWER_DEEP_SLEEP	0x02 +#define AUO_PIXCIR_POWER_MASK		0x03 + +#define AUO_PIXCIR_POWER_ALLOW_SLEEP	(1 << 2) +#define AUO_PIXCIR_POWER_IDLE_TIME(ms)	((ms & 0xf) << 4) + +#define AUO_PIXCIR_CALIBRATE		0x03 + +#define AUO_PIXCIR_EEPROM_CALIB_X_LEN	62 +#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN	36 + +#define AUO_PIXCIR_RAW_DATA_X_LEN	18 +#define AUO_PIXCIR_RAW_DATA_Y_LEN	11 + +#define AUO_PIXCIR_STRENGTH_ENABLE	(1 << 0) + +/* Touchscreen absolute values */ +#define AUO_PIXCIR_REPORT_POINTS	2 +#define AUO_PIXCIR_MAX_AREA		0xff +#define AUO_PIXCIR_PENUP_TIMEOUT_MS	10 + +struct auo_pixcir_ts { +	struct i2c_client	*client; +	struct input_dev	*input; +	const struct auo_pixcir_ts_platdata *pdata; +	char			phys[32]; + +	/* special handling for touch_indicate interupt mode */ +	bool			touch_ind_mode; + +	wait_queue_head_t	wait; +	bool			stopped; +}; + +struct auo_point_t { +	int	coord_x; +	int	coord_y; +	int	area_major; +	int	area_minor; +	int	orientation; +}; + +static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts, +				   struct auo_point_t *point) +{ +	struct i2c_client *client = ts->client; +	const struct auo_pixcir_ts_platdata *pdata = ts->pdata; +	uint8_t raw_coord[8]; +	uint8_t raw_area[4]; +	int i, ret; + +	/* touch coordinates */ +	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB, +					    8, raw_coord); +	if (ret < 0) { +		dev_err(&client->dev, "failed to read coordinate, %d\n", ret); +		return ret; +	} + +	/* touch area */ +	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1, +					    4, raw_area); +	if (ret < 0) { +		dev_err(&client->dev, "could not read touch area, %d\n", ret); +		return ret; +	} + +	for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) { +		point[i].coord_x = +			raw_coord[4 * i + 1] << 8 | raw_coord[4 * i]; +		point[i].coord_y = +			raw_coord[4 * i + 3] << 8 | raw_coord[4 * i + 2]; + +		if (point[i].coord_x > pdata->x_max || +		    point[i].coord_y > pdata->y_max) { +			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n", +				point[i].coord_x, point[i].coord_y); +			point[i].coord_x = point[i].coord_y = 0; +		} + +		/* determine touch major, minor and orientation */ +		point[i].area_major = max(raw_area[2 * i], raw_area[2 * i + 1]); +		point[i].area_minor = min(raw_area[2 * i], raw_area[2 * i + 1]); +		point[i].orientation = raw_area[2 * i] > raw_area[2 * i + 1]; +	} + +	return 0; +} + +static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id) +{ +	struct auo_pixcir_ts *ts = dev_id; +	const struct auo_pixcir_ts_platdata *pdata = ts->pdata; +	struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS]; +	int i; +	int ret; +	int fingers = 0; +	int abs = -1; + +	while (!ts->stopped) { + +		/* check for up event in touch touch_ind_mode */ +		if (ts->touch_ind_mode) { +			if (gpio_get_value(pdata->gpio_int) == 0) { +				input_mt_sync(ts->input); +				input_report_key(ts->input, BTN_TOUCH, 0); +				input_sync(ts->input); +				break; +			} +		} + +		ret = auo_pixcir_collect_data(ts, point); +		if (ret < 0) { +			/* we want to loop only in touch_ind_mode */ +			if (!ts->touch_ind_mode) +				break; + +			wait_event_timeout(ts->wait, ts->stopped, +				msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS)); +			continue; +		} + +		for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) { +			if (point[i].coord_x > 0 || point[i].coord_y > 0) { +				input_report_abs(ts->input, ABS_MT_POSITION_X, +						 point[i].coord_x); +				input_report_abs(ts->input, ABS_MT_POSITION_Y, +						 point[i].coord_y); +				input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, +						 point[i].area_major); +				input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, +						 point[i].area_minor); +				input_report_abs(ts->input, ABS_MT_ORIENTATION, +						 point[i].orientation); +				input_mt_sync(ts->input); + +				/* use first finger as source for singletouch */ +				if (fingers == 0) +					abs = i; + +				/* number of touch points could also be queried +				 * via i2c but would require an additional call +				 */ +				fingers++; +			} +		} + +		input_report_key(ts->input, BTN_TOUCH, fingers > 0); + +		if (abs > -1) { +			input_report_abs(ts->input, ABS_X, point[abs].coord_x); +			input_report_abs(ts->input, ABS_Y, point[abs].coord_y); +		} + +		input_sync(ts->input); + +		/* we want to loop only in touch_ind_mode */ +		if (!ts->touch_ind_mode) +			break; + +		wait_event_timeout(ts->wait, ts->stopped, +				 msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS)); +	} + +	return IRQ_HANDLED; +} + +/* + * Set the power mode of the device. + * Valid modes are + * - AUO_PIXCIR_POWER_ACTIVE + * - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch + * - AUO_PIXCIR_POWER_DEEP_SLEEP + */ +static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode) +{ +	struct i2c_client *client = ts->client; +	int ret; + +	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE); +	if (ret < 0) { +		dev_err(&client->dev, "unable to read reg %Xh, %d\n", +			AUO_PIXCIR_REG_POWER_MODE, ret); +		return ret; +	} + +	ret &= ~AUO_PIXCIR_POWER_MASK; +	ret |= mode; + +	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret); +	if (ret) { +		dev_err(&client->dev, "unable to write reg %Xh, %d\n", +			AUO_PIXCIR_REG_POWER_MODE, ret); +		return ret; +	} + +	return 0; +} + +static int auo_pixcir_int_config(struct auo_pixcir_ts *ts, +					   int int_setting) +{ +	struct i2c_client *client = ts->client; +	const struct auo_pixcir_ts_platdata *pdata = ts->pdata; +	int ret; + +	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING); +	if (ret < 0) { +		dev_err(&client->dev, "unable to read reg %Xh, %d\n", +			AUO_PIXCIR_REG_INT_SETTING, ret); +		return ret; +	} + +	ret &= ~AUO_PIXCIR_INT_MODE_MASK; +	ret |= int_setting; +	ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */ + +	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING, +					ret); +	if (ret < 0) { +		dev_err(&client->dev, "unable to write reg %Xh, %d\n", +			AUO_PIXCIR_REG_INT_SETTING, ret); +		return ret; +	} + +	ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND; + +	return 0; +} + +/* control the generation of interrupts on the device side */ +static int auo_pixcir_int_toggle(struct auo_pixcir_ts *ts, bool enable) +{ +	struct i2c_client *client = ts->client; +	int ret; + +	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING); +	if (ret < 0) { +		dev_err(&client->dev, "unable to read reg %Xh, %d\n", +			AUO_PIXCIR_REG_INT_SETTING, ret); +		return ret; +	} + +	if (enable) +		ret |= AUO_PIXCIR_INT_ENABLE; +	else +		ret &= ~AUO_PIXCIR_INT_ENABLE; + +	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING, +					ret); +	if (ret < 0) { +		dev_err(&client->dev, "unable to write reg %Xh, %d\n", +			AUO_PIXCIR_REG_INT_SETTING, ret); +		return ret; +	} + +	return 0; +} + +static int auo_pixcir_start(struct auo_pixcir_ts *ts) +{ +	struct i2c_client *client = ts->client; +	int ret; + +	ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE); +	if (ret < 0) { +		dev_err(&client->dev, "could not set power mode, %d\n", +			ret); +		return ret; +	} + +	ts->stopped = false; +	mb(); +	enable_irq(client->irq); + +	ret = auo_pixcir_int_toggle(ts, 1); +	if (ret < 0) { +		dev_err(&client->dev, "could not enable interrupt, %d\n", +			ret); +		disable_irq(client->irq); +		return ret; +	} + +	return 0; +} + +static int auo_pixcir_stop(struct auo_pixcir_ts *ts) +{ +	struct i2c_client *client = ts->client; +	int ret; + +	ret = auo_pixcir_int_toggle(ts, 0); +	if (ret < 0) { +		dev_err(&client->dev, "could not disable interrupt, %d\n", +			ret); +		return ret; +	} + +	/* disable receiving of interrupts */ +	disable_irq(client->irq); +	ts->stopped = true; +	mb(); +	wake_up(&ts->wait); + +	return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP); +} + +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; +} + +static void auo_pixcir_input_close(struct input_dev *dev) +{ +	struct auo_pixcir_ts *ts = input_get_drvdata(dev); + +	auo_pixcir_stop(ts); + +	return; +} + +#ifdef CONFIG_PM_SLEEP +static int auo_pixcir_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct auo_pixcir_ts *ts = i2c_get_clientdata(client); +	struct input_dev *input = ts->input; +	int ret = 0; + +	mutex_lock(&input->mutex); + +	/* when configured as wakeup source, device should always wake system +	 * therefore start device if necessary +	 */ +	if (device_may_wakeup(&client->dev)) { +		/* need to start device if not open, to be wakeup source */ +		if (!input->users) { +			ret = auo_pixcir_start(ts); +			if (ret) +				goto unlock; +		} + +		enable_irq_wake(client->irq); +		ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP); +	} else if (input->users) { +		ret = auo_pixcir_stop(ts); +	} + +unlock: +	mutex_unlock(&input->mutex); + +	return ret; +} + +static int auo_pixcir_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct auo_pixcir_ts *ts = i2c_get_clientdata(client); +	struct input_dev *input = ts->input; +	int ret = 0; + +	mutex_lock(&input->mutex); + +	if (device_may_wakeup(&client->dev)) { +		disable_irq_wake(client->irq); + +		/* need to stop device if it was not open on suspend */ +		if (!input->users) { +			ret = auo_pixcir_stop(ts); +			if (ret) +				goto unlock; +		} + +		/* device wakes automatically from SLEEP */ +	} else if (input->users) { +		ret = auo_pixcir_start(ts); +	} + +unlock: +	mutex_unlock(&input->mutex); + +	return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, +			 auo_pixcir_suspend, auo_pixcir_resume); + +#ifdef CONFIG_OF +static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev) +{ +	struct auo_pixcir_ts_platdata *pdata; +	struct device_node *np = dev->of_node; + +	if (!np) +		return ERR_PTR(-ENOENT); + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) { +		dev_err(dev, "failed to allocate platform data\n"); +		return ERR_PTR(-ENOMEM); +	} + +	pdata->gpio_int = of_get_gpio(np, 0); +	if (!gpio_is_valid(pdata->gpio_int)) { +		dev_err(dev, "failed to get interrupt gpio\n"); +		return ERR_PTR(-EINVAL); +	} + +	pdata->gpio_rst = of_get_gpio(np, 1); +	if (!gpio_is_valid(pdata->gpio_rst)) { +		dev_err(dev, "failed to get reset gpio\n"); +		return ERR_PTR(-EINVAL); +	} + +	if (of_property_read_u32(np, "x-size", &pdata->x_max)) { +		dev_err(dev, "failed to get x-size property\n"); +		return ERR_PTR(-EINVAL); +	} + +	if (of_property_read_u32(np, "y-size", &pdata->y_max)) { +		dev_err(dev, "failed to get y-size property\n"); +		return ERR_PTR(-EINVAL); +	} + +	/* default to asserting the interrupt when the screen is touched */ +	pdata->int_setting = AUO_PIXCIR_INT_TOUCH_IND; + +	return pdata; +} +#else +static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev) +{ +	return ERR_PTR(-EINVAL); +} +#endif + +static void auo_pixcir_reset(void *data) +{ +	struct auo_pixcir_ts *ts = data; + +	gpio_set_value(ts->pdata->gpio_rst, 0); +} + +static int auo_pixcir_probe(struct i2c_client *client, +			    const struct i2c_device_id *id) +{ +	const struct auo_pixcir_ts_platdata *pdata; +	struct auo_pixcir_ts *ts; +	struct input_dev *input_dev; +	int version; +	int error; + +	pdata = dev_get_platdata(&client->dev); +	if (!pdata) { +		pdata = auo_pixcir_parse_dt(&client->dev); +		if (IS_ERR(pdata)) +			return PTR_ERR(pdata); +	} + +	ts = devm_kzalloc(&client->dev, +			  sizeof(struct auo_pixcir_ts), GFP_KERNEL); +	if (!ts) +		return -ENOMEM; + +	input_dev = devm_input_allocate_device(&client->dev); +	if (!input_dev) { +		dev_err(&client->dev, "could not allocate input device\n"); +		return -ENOMEM; +	} + +	ts->pdata = pdata; +	ts->client = client; +	ts->input = input_dev; +	ts->touch_ind_mode = 0; +	ts->stopped = true; +	init_waitqueue_head(&ts->wait); + +	snprintf(ts->phys, sizeof(ts->phys), +		 "%s/input0", dev_name(&client->dev)); + +	input_dev->name = "AUO-Pixcir touchscreen"; +	input_dev->phys = ts->phys; +	input_dev->id.bustype = BUS_I2C; + +	input_dev->open = auo_pixcir_input_open; +	input_dev->close = auo_pixcir_input_close; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); + +	__set_bit(BTN_TOUCH, input_dev->keybit); + +	/* For single touch */ +	input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0); + +	/* For multi touch */ +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, +			     pdata->x_max, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, +			     pdata->y_max, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, +			     AUO_PIXCIR_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, +			     AUO_PIXCIR_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); + +	input_set_drvdata(ts->input, ts); + +	error = devm_gpio_request_one(&client->dev, pdata->gpio_int, +				      GPIOF_DIR_IN, "auo_pixcir_ts_int"); +	if (error) { +		dev_err(&client->dev, "request of gpio %d failed, %d\n", +			pdata->gpio_int, error); +		return error; +	} + +	error = devm_gpio_request_one(&client->dev, pdata->gpio_rst, +				      GPIOF_DIR_OUT | GPIOF_INIT_HIGH, +				      "auo_pixcir_ts_rst"); +	if (error) { +		dev_err(&client->dev, "request of gpio %d failed, %d\n", +			pdata->gpio_rst, error); +		return error; +	} + +	error = devm_add_action(&client->dev, auo_pixcir_reset, ts); +	if (error) { +		auo_pixcir_reset(ts); +		dev_err(&client->dev, "failed to register reset action, %d\n", +			error); +		return error; +	} + +	msleep(200); + +	version = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION); +	if (version < 0) { +		error = version; +		return error; +	} + +	dev_info(&client->dev, "firmware version 0x%X\n", version); + +	error = auo_pixcir_int_config(ts, pdata->int_setting); +	if (error) +		return error; + +	error = devm_request_threaded_irq(&client->dev, client->irq, +					  NULL, auo_pixcir_interrupt, +					  IRQF_TRIGGER_RISING | IRQF_ONESHOT, +					  input_dev->name, ts); +	if (error) { +		dev_err(&client->dev, "irq %d requested failed, %d\n", +			client->irq, error); +		return error; +	} + +	/* stop device and put it into deep sleep until it is opened */ +	error = auo_pixcir_stop(ts); +	if (error) +		return error; + +	error = input_register_device(input_dev); +	if (error) { +		dev_err(&client->dev, "could not register input device, %d\n", +			error); +		return error; +	} + +	i2c_set_clientdata(client, ts); + +	return 0; +} + +static const struct i2c_device_id auo_pixcir_idtable[] = { +	{ "auo_pixcir_ts", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable); + +#ifdef CONFIG_OF +static const struct of_device_id auo_pixcir_ts_dt_idtable[] = { +	{ .compatible = "auo,auo_pixcir_ts" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, auo_pixcir_ts_dt_idtable); +#endif + +static struct i2c_driver auo_pixcir_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "auo_pixcir_ts", +		.pm	= &auo_pixcir_pm_ops, +		.of_match_table	= of_match_ptr(auo_pixcir_ts_dt_idtable), +	}, +	.probe		= auo_pixcir_probe, +	.id_table	= auo_pixcir_idtable, +}; + +module_i2c_driver(auo_pixcir_driver); + +MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 2ca9e5d6646..b9b5ddad665 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -12,6 +12,11 @@  #include <linux/input.h>  #include <linux/input/bu21013.h>  #include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h>  #define PEN_DOWN_INTR	0  #define MAX_FINGERS	2 @@ -139,16 +144,19 @@   * @chip: pointer to the touch panel controller   * @in_dev: pointer to the input device structure   * @intr_pin: interrupt pin value + * @regulator: pointer to the Regulator used for touch screen   *   * Touch panel device data structure   */  struct bu21013_ts_data {  	struct i2c_client *client;  	wait_queue_head_t wait; -	bool touch_stopped;  	const struct bu21013_platform_device *chip;  	struct input_dev *in_dev; +	struct regulator *regulator; +	unsigned int irq;  	unsigned int intr_pin; +	bool touch_stopped;  };  /** @@ -258,7 +266,7 @@ static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)  			return IRQ_NONE;  		} -		data->intr_pin = data->chip->irq_read_val(); +		data->intr_pin = gpio_get_value(data->chip->touch_pin);  		if (data->intr_pin == PEN_DOWN_INTR)  			wait_event_timeout(data->wait, data->touch_stopped,  					   msecs_to_jiffies(2)); @@ -365,7 +373,7 @@ static int bu21013_init_chip(struct bu21013_ts_data *data)  	}  	retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, -				BU21013_TH_OFF_4 || BU21013_TH_OFF_3); +				BU21013_TH_OFF_4 | BU21013_TH_OFF_3);  	if (retval < 0) {  		dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");  		return retval; @@ -414,8 +422,70 @@ static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)  {  	bu21013_data->touch_stopped = true;  	wake_up(&bu21013_data->wait); -	free_irq(bu21013_data->chip->irq, bu21013_data); +	free_irq(bu21013_data->irq, bu21013_data); +} + +/** + * bu21013_cs_disable() - deconfigures the touch panel controller + * @bu21013_data: device structure pointer + * + * This function is used to deconfigure the chip selection + * for touch panel controller. + */ +static void bu21013_cs_disable(struct bu21013_ts_data *bu21013_data) +{ +	int error; + +	error = gpio_direction_output(bu21013_data->chip->cs_pin, 0); +	if (error < 0) +		dev_warn(&bu21013_data->client->dev, +			 "%s: gpio direction failed, error: %d\n", +			 __func__, error); +	else +		gpio_set_value(bu21013_data->chip->cs_pin, 0); + +	gpio_free(bu21013_data->chip->cs_pin); +} + +#ifdef CONFIG_OF +static const struct bu21013_platform_device * +bu21013_parse_dt(struct device *dev) +{ +	struct device_node *np = dev->of_node; +	struct bu21013_platform_device *pdata; + +	if (!np) { +		dev_err(dev, "no device tree or platform data\n"); +		return ERR_PTR(-EINVAL); +	} + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return ERR_PTR(-ENOMEM); + +	pdata->y_flip = pdata->x_flip = false; + +	pdata->x_flip = of_property_read_bool(np, "rohm,flip-x"); +	pdata->y_flip = of_property_read_bool(np, "rohm,flip-y"); + +	of_property_read_u32(np, "rohm,touch-max-x", &pdata->touch_x_max); +	of_property_read_u32(np, "rohm,touch-max-y", &pdata->touch_y_max); + +	pdata->touch_pin = of_get_named_gpio(np, "touch-gpio", 0); +	pdata->cs_pin = of_get_named_gpio(np, "reset-gpio", 0); + +	pdata->ext_clk = false; + +	return pdata; +} +#else +static inline const struct bu21013_platform_device * +bu21013_parse_dt(struct device *dev) +{ +	dev_err(dev, "no platform data available\n"); +	return ERR_PTR(-EINVAL);  } +#endif  /**   * bu21013_probe() - initializes the i2c-client touchscreen driver @@ -425,13 +495,13 @@ static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)   * This function used to initializes the i2c-client touchscreen   * driver and returns integer.   */ -static int __devinit bu21013_probe(struct i2c_client *client, -					const struct i2c_device_id *id) +static int bu21013_probe(struct i2c_client *client, +			 const struct i2c_device_id *id)  { +	const struct bu21013_platform_device *pdata = +					dev_get_platdata(&client->dev);  	struct bu21013_ts_data *bu21013_data;  	struct input_dev *in_dev; -	const struct bu21013_platform_device *pdata = -					client->dev.platform_data;  	int error;  	if (!i2c_check_functionality(client->adapter, @@ -441,7 +511,13 @@ static int __devinit bu21013_probe(struct i2c_client *client,  	}  	if (!pdata) { -		dev_err(&client->dev, "platform data not defined\n"); +		pdata = bu21013_parse_dt(&client->dev); +		if (IS_ERR(pdata)) +			return PTR_ERR(pdata); +	} + +	if (!gpio_is_valid(pdata->touch_pin)) { +		dev_err(&client->dev, "invalid touch_pin supplied\n");  		return -EINVAL;  	} @@ -456,16 +532,30 @@ static int __devinit bu21013_probe(struct i2c_client *client,  	bu21013_data->in_dev = in_dev;  	bu21013_data->chip = pdata;  	bu21013_data->client = client; +	bu21013_data->irq = gpio_to_irq(pdata->touch_pin); + +	bu21013_data->regulator = regulator_get(&client->dev, "avdd"); +	if (IS_ERR(bu21013_data->regulator)) { +		dev_err(&client->dev, "regulator_get failed\n"); +		error = PTR_ERR(bu21013_data->regulator); +		goto err_free_mem; +	} + +	error = regulator_enable(bu21013_data->regulator); +	if (error < 0) { +		dev_err(&client->dev, "regulator enable failed\n"); +		goto err_put_regulator; +	} +  	bu21013_data->touch_stopped = false;  	init_waitqueue_head(&bu21013_data->wait);  	/* configure the gpio pins */ -	if (pdata->cs_en) { -		error = pdata->cs_en(pdata->cs_pin); -		if (error < 0) { -			dev_err(&client->dev, "chip init failed\n"); -			goto err_free_mem; -		} +	error = gpio_request_one(pdata->cs_pin, GPIOF_OUT_INIT_HIGH, +				 "touchp_reset"); +	if (error < 0) { +		dev_err(&client->dev, "Unable to request gpio reset_pin\n"); +		goto err_disable_regulator;  	}  	/* configure the touch panel controller */ @@ -485,16 +575,18 @@ static int __devinit bu21013_probe(struct i2c_client *client,  	__set_bit(EV_ABS, in_dev->evbit);  	input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, -						pdata->x_max_res, 0, 0); +						pdata->touch_x_max, 0, 0);  	input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, -						pdata->y_max_res, 0, 0); +						pdata->touch_y_max, 0, 0);  	input_set_drvdata(in_dev, bu21013_data); -	error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, -				     IRQF_TRIGGER_FALLING | IRQF_SHARED, +	error = request_threaded_irq(bu21013_data->irq, NULL, bu21013_gpio_irq, +				     IRQF_TRIGGER_FALLING | IRQF_SHARED | +					IRQF_ONESHOT,  				     DRIVER_TP, bu21013_data);  	if (error) { -		dev_err(&client->dev, "request irq %d failed\n", pdata->irq); +		dev_err(&client->dev, "request irq %d failed\n", +			bu21013_data->irq);  		goto err_cs_disable;  	} @@ -512,7 +604,11 @@ static int __devinit bu21013_probe(struct i2c_client *client,  err_free_irq:  	bu21013_free_irq(bu21013_data);  err_cs_disable: -	pdata->cs_dis(pdata->cs_pin); +	bu21013_cs_disable(bu21013_data); +err_disable_regulator: +	regulator_disable(bu21013_data->regulator); +err_put_regulator: +	regulator_put(bu21013_data->regulator);  err_free_mem:  	input_free_device(in_dev);  	kfree(bu21013_data); @@ -526,15 +622,19 @@ err_free_mem:   * This function uses to remove the i2c-client   * touchscreen driver and returns integer.   */ -static int __devexit bu21013_remove(struct i2c_client *client) +static int bu21013_remove(struct i2c_client *client)  {  	struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);  	bu21013_free_irq(bu21013_data); -	bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); +	bu21013_cs_disable(bu21013_data);  	input_unregister_device(bu21013_data->in_dev); + +	regulator_disable(bu21013_data->regulator); +	regulator_put(bu21013_data->regulator); +  	kfree(bu21013_data);  	device_init_wakeup(&client->dev, false); @@ -557,9 +657,11 @@ static int bu21013_suspend(struct device *dev)  	bu21013_data->touch_stopped = true;  	if (device_may_wakeup(&client->dev)) -		enable_irq_wake(bu21013_data->chip->irq); +		enable_irq_wake(bu21013_data->irq);  	else -		disable_irq(bu21013_data->chip->irq); +		disable_irq(bu21013_data->irq); + +	regulator_disable(bu21013_data->regulator);  	return 0;  } @@ -577,6 +679,12 @@ static int bu21013_resume(struct device *dev)  	struct i2c_client *client = bu21013_data->client;  	int retval; +	retval = regulator_enable(bu21013_data->regulator); +	if (retval < 0) { +		dev_err(&client->dev, "bu21013 regulator enable failed\n"); +		return retval; +	} +  	retval = bu21013_init_chip(bu21013_data);  	if (retval < 0) {  		dev_err(&client->dev, "bu21013 controller config failed\n"); @@ -586,9 +694,9 @@ static int bu21013_resume(struct device *dev)  	bu21013_data->touch_stopped = false;  	if (device_may_wakeup(&client->dev)) -		disable_irq_wake(bu21013_data->chip->irq); +		disable_irq_wake(bu21013_data->irq);  	else -		enable_irq(bu21013_data->chip->irq); +		enable_irq(bu21013_data->irq);  	return 0;  } @@ -614,34 +722,11 @@ static struct i2c_driver bu21013_driver = {  #endif  	},  	.probe		=	bu21013_probe, -	.remove		=	__devexit_p(bu21013_remove), +	.remove		=	bu21013_remove,  	.id_table	=	bu21013_id,  }; -/** - * bu21013_init() - initializes the bu21013 touchscreen driver - * - * This function used to initializes the bu21013 - * touchscreen driver and returns integer. - */ -static int __init bu21013_init(void) -{ -	return i2c_add_driver(&bu21013_driver); -} - -/** - * bu21013_exit() - de-initializes the bu21013 touchscreen driver - * - * This function uses to de-initializes the bu21013 - * touchscreen driver and returns none. - */ -static void __exit bu21013_exit(void) -{ -	i2c_del_driver(&bu21013_driver); -} - -module_init(bu21013_init); -module_exit(bu21013_exit); +module_i2c_driver(bu21013_driver);  MODULE_LICENSE("GPL v2");  MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>"); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index d0c3a7229ad..5bf1aeeea82 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -84,9 +84,9 @@ static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,  	memcpy(i2c_data + 1, value, len);  	ret = i2c_master_send(client, i2c_data, len + 1); -	if (ret != 1) { +	if (ret != len + 1) {  		dev_err(&client->dev, "i2c write data cmd failed\n"); -		return ret ? ret : -EIO; +		return ret < 0 ? ret : -EIO;  	}  	return 0; @@ -99,9 +99,18 @@ static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc,  	int ret;  	struct i2c_msg msg[2] = {  		/* first write slave position to i2c devices */ -		{ client->addr, 0, 1, &cmd }, +		{ +			.addr = client->addr, +			.len = 1, +			.buf = &cmd +		},  		/* Second read data from position */ -		{ client->addr, I2C_M_RD, len, data } +		{ +			.addr = client->addr, +			.flags = I2C_M_RD, +			.len = len, +			.buf = data +		}  	};  	ret = i2c_transfer(client->adapter, msg, 2); @@ -166,10 +175,10 @@ static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static int __devinit cy8ctmg110_probe(struct i2c_client *client, +static int cy8ctmg110_probe(struct i2c_client *client,  					const struct i2c_device_id *id)  { -	const struct cy8ctmg110_pdata *pdata = client->dev.platform_data; +	const struct cy8ctmg110_pdata *pdata = dev_get_platdata(&client->dev);  	struct cy8ctmg110 *ts;  	struct input_dev *input_dev;  	int err; @@ -193,6 +202,8 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client,  	ts->client = client;  	ts->input = input_dev; +	ts->reset_pin = pdata->reset_pin; +	ts->irq_pin = pdata->irq_pin;  	snprintf(ts->phys, sizeof(ts->phys),  		 "%s/input0", dev_name(&client->dev)); @@ -249,7 +260,8 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client,  	}  	err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread, -				   IRQF_TRIGGER_RISING, "touch_reset_key", ts); +				   IRQF_TRIGGER_RISING | IRQF_ONESHOT, +				   "touch_reset_key", ts);  	if (err < 0) {  		dev_err(&client->dev,  			"irq %d busy? error %d\n", client->irq, err); @@ -279,9 +291,10 @@ err_free_mem:  	return err;  } -#ifdef CONFIG_PM -static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int cy8ctmg110_suspend(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct cy8ctmg110 *ts = i2c_get_clientdata(client);  	if (device_may_wakeup(&client->dev)) @@ -293,8 +306,9 @@ static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg)  	return 0;  } -static int cy8ctmg110_resume(struct i2c_client *client) +static int cy8ctmg110_resume(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct cy8ctmg110 *ts = i2c_get_clientdata(client);  	if (device_may_wakeup(&client->dev)) @@ -307,7 +321,9 @@ static int cy8ctmg110_resume(struct i2c_client *client)  }  #endif -static int __devexit cy8ctmg110_remove(struct i2c_client *client) +static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume); + +static int cy8ctmg110_remove(struct i2c_client *client)  {  	struct cy8ctmg110 *ts = i2c_get_clientdata(client); @@ -324,7 +340,7 @@ static int __devexit cy8ctmg110_remove(struct i2c_client *client)  	return 0;  } -static struct i2c_device_id cy8ctmg110_idtable[] = { +static const struct i2c_device_id cy8ctmg110_idtable[] = {  	{ CY8CTMG110_DRIVER_NAME, 1 },  	{ }  }; @@ -335,28 +351,14 @@ static struct i2c_driver cy8ctmg110_driver = {  	.driver		= {  		.owner	= THIS_MODULE,  		.name	= CY8CTMG110_DRIVER_NAME, +		.pm	= &cy8ctmg110_pm,  	},  	.id_table	= cy8ctmg110_idtable,  	.probe		= cy8ctmg110_probe, -	.remove		= __devexit_p(cy8ctmg110_remove), -#ifdef CONFIG_PM -	.suspend	= cy8ctmg110_suspend, -	.resume		= cy8ctmg110_resume, -#endif +	.remove		= cy8ctmg110_remove,  }; -static int __init cy8ctmg110_init(void) -{ -	return i2c_add_driver(&cy8ctmg110_driver); -} - -static void __exit cy8ctmg110_exit(void) -{ -	i2c_del_driver(&cy8ctmg110_driver); -} - -module_init(cy8ctmg110_init); -module_exit(cy8ctmg110_exit); +module_i2c_driver(cy8ctmg110_driver);  MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>");  MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c new file mode 100644 index 00000000000..a035a390f8e --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -0,0 +1,2160 @@ +/* + * cyttsp4_core.c + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp4_core.h" +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/sched.h> +#include <linux/slab.h> + +/* Timeout in ms. */ +#define CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT	500 +#define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT	5000 +#define CY_CORE_MODE_CHANGE_TIMEOUT		1000 +#define CY_CORE_RESET_AND_WAIT_TIMEOUT		500 +#define CY_CORE_WAKEUP_TIMEOUT			500 + +#define CY_CORE_STARTUP_RETRY_COUNT		3 + +static const u8 ldr_exit[] = { +	0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17 +}; + +static const u8 ldr_err_app[] = { +	0x01, 0x02, 0x00, 0x00, 0x55, 0xDD, 0x17 +}; + +static inline size_t merge_bytes(u8 high, u8 low) +{ +	return (high << 8) + low; +} + +#ifdef VERBOSE_DEBUG +static void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size, +		const char *data_name) +{ +	int i, k; +	const char fmt[] = "%02X "; +	int max; + +	if (!size) +		return; + +	max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED); + +	pr_buf[0] = 0; +	for (i = k = 0; i < size && k < max; i++, k += 3) +		scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]); + +	dev_vdbg(dev, "%s:  %s[0..%d]=%s%s\n", __func__, data_name, size - 1, +			pr_buf, size <= max ? "" : CY_PR_TRUNCATED); +} +#else +#define cyttsp4_pr_buf(dev, pr_buf, dptr, size, data_name) do { } while (0) +#endif + +static int cyttsp4_load_status_regs(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	struct device *dev = cd->dev; +	int rc; + +	rc = cyttsp4_adap_read(cd, CY_REG_BASE, si->si_ofs.mode_size, +			si->xy_mode); +	if (rc < 0) +		dev_err(dev, "%s: fail read mode regs r=%d\n", +			__func__, rc); +	else +		cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode, +			si->si_ofs.mode_size, "xy_mode"); + +	return rc; +} + +static int cyttsp4_handshake(struct cyttsp4 *cd, u8 mode) +{ +	u8 cmd = mode ^ CY_HST_TOGGLE; +	int rc; + +	/* +	 * Mode change issued, handshaking now will cause endless mode change +	 * requests, for sync mode modechange will do same with handshake +	 * */ +	if (mode & CY_HST_MODE_CHANGE) +		return 0; + +	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); +	if (rc < 0) +		dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n", +				__func__, rc); + +	return rc; +} + +static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd) +{ +	u8 cmd = CY_HST_RESET; +	int rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); +	if (rc < 0) { +		dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n", +				__func__); +		return rc; +	} +	return 0; +} + +static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd) +{ +	if (cd->cpdata->xres) { +		cd->cpdata->xres(cd->cpdata, cd->dev); +		dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__); +		return 0; +	} +	dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); +	return -ENOSYS; +} + +static int cyttsp4_hw_reset(struct cyttsp4 *cd) +{ +	int rc = cyttsp4_hw_hard_reset(cd); +	if (rc == -ENOSYS) +		rc = cyttsp4_hw_soft_reset(cd); +	return rc; +} + +/* + * Gets number of bits for a touch filed as parameter, + * sets maximum value for field which is used as bit mask + * and returns number of bytes required for that field + */ +static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max) +{ +	*max = 1UL << nbits; +	return (nbits + 7) / 8; +} + +static int cyttsp4_si_data_offsets(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	int rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(si->si_data), +			&si->si_data); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n", +			__func__, rc); +		return rc; +	} + +	/* Print sysinfo data offsets */ +	cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data, +		       sizeof(si->si_data), "sysinfo_data_offsets"); + +	/* convert sysinfo data offset bytes into integers */ + +	si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, +			si->si_data.map_szl); +	si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, +			si->si_data.map_szl); +	si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh, +			si->si_data.cydata_ofsl); +	si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh, +			si->si_data.test_ofsl); +	si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh, +			si->si_data.pcfg_ofsl); +	si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh, +			si->si_data.opcfg_ofsl); +	si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh, +			si->si_data.ddata_ofsl); +	si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh, +			si->si_data.mdata_ofsl); +	return rc; +} + +static int cyttsp4_si_get_cydata(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	int read_offset; +	int mfgid_sz, calc_mfgid_sz; +	void *p; +	int rc; + +	si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs; +	dev_dbg(cd->dev, "%s: cydata size: %Zd\n", __func__, +			si->si_ofs.cydata_size); + +	p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL); +	if (p == NULL) { +		dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__); +		return -ENOMEM; +	} +	si->si_ptrs.cydata = p; + +	read_offset = si->si_ofs.cydata_ofs; + +	/* Read the CYDA registers up to MFGID field */ +	rc = cyttsp4_adap_read(cd, read_offset, +			offsetof(struct cyttsp4_cydata, mfgid_sz) +				+ sizeof(si->si_ptrs.cydata->mfgid_sz), +			si->si_ptrs.cydata); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail read cydata r=%d\n", +			__func__, rc); +		return rc; +	} + +	/* Check MFGID size */ +	mfgid_sz = si->si_ptrs.cydata->mfgid_sz; +	calc_mfgid_sz = si->si_ofs.cydata_size - sizeof(struct cyttsp4_cydata); +	if (mfgid_sz != calc_mfgid_sz) { +		dev_err(cd->dev, "%s: mismatch in MFGID size, reported:%d calculated:%d\n", +			__func__, mfgid_sz, calc_mfgid_sz); +		return -EINVAL; +	} + +	read_offset += offsetof(struct cyttsp4_cydata, mfgid_sz) +			+ sizeof(si->si_ptrs.cydata->mfgid_sz); + +	/* Read the CYDA registers for MFGID field */ +	rc = cyttsp4_adap_read(cd, read_offset, si->si_ptrs.cydata->mfgid_sz, +			si->si_ptrs.cydata->mfg_id); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail read cydata r=%d\n", +			__func__, rc); +		return rc; +	} + +	read_offset += si->si_ptrs.cydata->mfgid_sz; + +	/* Read the rest of the CYDA registers */ +	rc = cyttsp4_adap_read(cd, read_offset, +			sizeof(struct cyttsp4_cydata) +				- offsetof(struct cyttsp4_cydata, cyito_idh), +			&si->si_ptrs.cydata->cyito_idh); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail read cydata r=%d\n", +			__func__, rc); +		return rc; +	} + +	cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata, +		si->si_ofs.cydata_size, "sysinfo_cydata"); +	return rc; +} + +static int cyttsp4_si_get_test_data(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	void *p; +	int rc; + +	si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs; + +	p = krealloc(si->si_ptrs.test, si->si_ofs.test_size, GFP_KERNEL); +	if (p == NULL) { +		dev_err(cd->dev, "%s: fail alloc test memory\n", __func__); +		return -ENOMEM; +	} +	si->si_ptrs.test = p; + +	rc = cyttsp4_adap_read(cd, si->si_ofs.test_ofs, si->si_ofs.test_size, +			si->si_ptrs.test); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail read test data r=%d\n", +			__func__, rc); +		return rc; +	} + +	cyttsp4_pr_buf(cd->dev, cd->pr_buf, +		       (u8 *)si->si_ptrs.test, si->si_ofs.test_size, +		       "sysinfo_test_data"); +	if (si->si_ptrs.test->post_codel & +	    CY_POST_CODEL_WDG_RST) +		dev_info(cd->dev, "%s: %s codel=%02X\n", +			 __func__, "Reset was a WATCHDOG RESET", +			 si->si_ptrs.test->post_codel); + +	if (!(si->si_ptrs.test->post_codel & +	      CY_POST_CODEL_CFG_DATA_CRC_FAIL)) +		dev_info(cd->dev, "%s: %s codel=%02X\n", __func__, +			 "Config Data CRC FAIL", +			 si->si_ptrs.test->post_codel); + +	if (!(si->si_ptrs.test->post_codel & +	      CY_POST_CODEL_PANEL_TEST_FAIL)) +		dev_info(cd->dev, "%s: %s codel=%02X\n", +			 __func__, "PANEL TEST FAIL", +			 si->si_ptrs.test->post_codel); + +	dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n", +		 __func__, si->si_ptrs.test->post_codel & 0x08 ? +		 "ENABLED" : "DISABLED", +		 si->si_ptrs.test->post_codel); +	return rc; +} + +static int cyttsp4_si_get_pcfg_data(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	void *p; +	int rc; + +	si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs; + +	p = krealloc(si->si_ptrs.pcfg, si->si_ofs.pcfg_size, GFP_KERNEL); +	if (p == NULL) { +		rc = -ENOMEM; +		dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n", +			__func__, rc); +		return rc; +	} +	si->si_ptrs.pcfg = p; + +	rc = cyttsp4_adap_read(cd, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size, +			si->si_ptrs.pcfg); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail read pcfg data r=%d\n", +			__func__, rc); +		return rc; +	} + +	si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh +			& CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl); +	si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh +			& CY_PCFG_ORIGIN_X_MASK); +	si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh +			& CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl); +	si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh +			& CY_PCFG_ORIGIN_Y_MASK); +	si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh, +			si->si_ptrs.pcfg->max_zl); + +	cyttsp4_pr_buf(cd->dev, cd->pr_buf, +		       (u8 *)si->si_ptrs.pcfg, +		       si->si_ofs.pcfg_size, "sysinfo_pcfg_data"); +	return rc; +} + +static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	struct cyttsp4_tch_abs_params *tch; +	struct cyttsp4_tch_rec_params *tch_old, *tch_new; +	enum cyttsp4_tch_abs abs; +	int i; +	void *p; +	int rc; + +	si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs; + +	p = krealloc(si->si_ptrs.opcfg, si->si_ofs.opcfg_size, GFP_KERNEL); +	if (p == NULL) { +		dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__); +		rc = -ENOMEM; +		goto cyttsp4_si_get_opcfg_data_exit; +	} +	si->si_ptrs.opcfg = p; + +	rc = cyttsp4_adap_read(cd, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size, +			si->si_ptrs.opcfg); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail read opcfg data r=%d\n", +			__func__, rc); +		goto cyttsp4_si_get_opcfg_data_exit; +	} +	si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs; +	si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs; +	si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) + +		si->si_ptrs.opcfg->rep_szl; +	si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns; +	si->si_ofs.num_btn_regs = (si->si_ofs.num_btns + +		CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG; +	si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs; +	si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0; +	si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs & +		CY_BYTE_OFS_MASK; +	si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size & +		CY_BYTE_OFS_MASK; + +	/* Get the old touch fields */ +	for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) { +		tch = &si->si_ofs.tch_abs[abs]; +		tch_old = &si->si_ptrs.opcfg->tch_rec_old[abs]; + +		tch->ofs = tch_old->loc & CY_BYTE_OFS_MASK; +		tch->size = cyttsp4_bits_2_bytes(tch_old->size, +						 &tch->max); +		tch->bofs = (tch_old->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; +	} + +	/* button fields */ +	si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size; +	si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs; +	si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size; + +	if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { +		/* Get the extended touch fields */ +		for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) { +			tch = &si->si_ofs.tch_abs[abs]; +			tch_new = &si->si_ptrs.opcfg->tch_rec_new[i]; + +			tch->ofs = tch_new->loc & CY_BYTE_OFS_MASK; +			tch->size = cyttsp4_bits_2_bytes(tch_new->size, +							 &tch->max); +			tch->bofs = (tch_new->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; +		} +	} + +	for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) { +		dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__, +			cyttsp4_tch_abs_string[abs]); +		dev_dbg(cd->dev, "%s:     ofs =%2Zd\n", __func__, +			si->si_ofs.tch_abs[abs].ofs); +		dev_dbg(cd->dev, "%s:     siz =%2Zd\n", __func__, +			si->si_ofs.tch_abs[abs].size); +		dev_dbg(cd->dev, "%s:     max =%2Zd\n", __func__, +			si->si_ofs.tch_abs[abs].max); +		dev_dbg(cd->dev, "%s:     bofs=%2Zd\n", __func__, +			si->si_ofs.tch_abs[abs].bofs); +	} + +	si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1; +	si->si_ofs.data_size = si->si_ofs.max_tchs * +		si->si_ptrs.opcfg->tch_rec_size; + +	cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg, +		si->si_ofs.opcfg_size, "sysinfo_opcfg_data"); + +cyttsp4_si_get_opcfg_data_exit: +	return rc; +} + +static int cyttsp4_si_get_ddata(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	void *p; +	int rc; + +	si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs; + +	p = krealloc(si->si_ptrs.ddata, si->si_ofs.ddata_size, GFP_KERNEL); +	if (p == NULL) { +		dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__); +		return -ENOMEM; +	} +	si->si_ptrs.ddata = p; + +	rc = cyttsp4_adap_read(cd, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size, +			si->si_ptrs.ddata); +	if (rc < 0) +		dev_err(cd->dev, "%s: fail read ddata data r=%d\n", +			__func__, rc); +	else +		cyttsp4_pr_buf(cd->dev, cd->pr_buf, +			       (u8 *)si->si_ptrs.ddata, +			       si->si_ofs.ddata_size, "sysinfo_ddata"); +	return rc; +} + +static int cyttsp4_si_get_mdata(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	void *p; +	int rc; + +	si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs; + +	p = krealloc(si->si_ptrs.mdata, si->si_ofs.mdata_size, GFP_KERNEL); +	if (p == NULL) { +		dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__); +		return -ENOMEM; +	} +	si->si_ptrs.mdata = p; + +	rc = cyttsp4_adap_read(cd, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size, +			si->si_ptrs.mdata); +	if (rc < 0) +		dev_err(cd->dev, "%s: fail read mdata data r=%d\n", +			__func__, rc); +	else +		cyttsp4_pr_buf(cd->dev, cd->pr_buf, +			       (u8 *)si->si_ptrs.mdata, +			       si->si_ofs.mdata_size, "sysinfo_mdata"); +	return rc; +} + +static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	int btn; +	int num_defined_keys; +	u16 *key_table; +	void *p; +	int rc = 0; + +	if (si->si_ofs.num_btns) { +		si->si_ofs.btn_keys_size = si->si_ofs.num_btns * +			sizeof(struct cyttsp4_btn); + +		p = krealloc(si->btn, si->si_ofs.btn_keys_size, +				GFP_KERNEL|__GFP_ZERO); +		if (p == NULL) { +			dev_err(cd->dev, "%s: %s\n", __func__, +				"fail alloc btn_keys memory"); +			return -ENOMEM; +		} +		si->btn = p; + +		if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) +			num_defined_keys = 0; +		else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) +			num_defined_keys = 0; +		else +			num_defined_keys = cd->cpdata->sett +				[CY_IC_GRPNUM_BTN_KEYS]->size; + +		for (btn = 0; btn < si->si_ofs.num_btns && +			btn < num_defined_keys; btn++) { +			key_table = (u16 *)cd->cpdata->sett +				[CY_IC_GRPNUM_BTN_KEYS]->data; +			si->btn[btn].key_code = key_table[btn]; +			si->btn[btn].state = CY_BTN_RELEASED; +			si->btn[btn].enabled = true; +		} +		for (; btn < si->si_ofs.num_btns; btn++) { +			si->btn[btn].key_code = KEY_RESERVED; +			si->btn[btn].state = CY_BTN_RELEASED; +			si->btn[btn].enabled = true; +		} + +		return rc; +	} + +	si->si_ofs.btn_keys_size = 0; +	kfree(si->btn); +	si->btn = NULL; +	return rc; +} + +static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	void *p; + +	p = krealloc(si->xy_mode, si->si_ofs.mode_size, GFP_KERNEL|__GFP_ZERO); +	if (p == NULL) +		return -ENOMEM; +	si->xy_mode = p; + +	p = krealloc(si->xy_data, si->si_ofs.data_size, GFP_KERNEL|__GFP_ZERO); +	if (p == NULL) +		return -ENOMEM; +	si->xy_data = p; + +	p = krealloc(si->btn_rec_data, +			si->si_ofs.btn_rec_size * si->si_ofs.num_btns, +			GFP_KERNEL|__GFP_ZERO); +	if (p == NULL) +		return -ENOMEM; +	si->btn_rec_data = p; + +	return 0; +} + +static void cyttsp4_si_put_log_data(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	dev_dbg(cd->dev, "%s: cydata_ofs =%4Zd siz=%4Zd\n", __func__, +		si->si_ofs.cydata_ofs, si->si_ofs.cydata_size); +	dev_dbg(cd->dev, "%s: test_ofs   =%4Zd siz=%4Zd\n", __func__, +		si->si_ofs.test_ofs, si->si_ofs.test_size); +	dev_dbg(cd->dev, "%s: pcfg_ofs   =%4Zd siz=%4Zd\n", __func__, +		si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size); +	dev_dbg(cd->dev, "%s: opcfg_ofs  =%4Zd siz=%4Zd\n", __func__, +		si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size); +	dev_dbg(cd->dev, "%s: ddata_ofs  =%4Zd siz=%4Zd\n", __func__, +		si->si_ofs.ddata_ofs, si->si_ofs.ddata_size); +	dev_dbg(cd->dev, "%s: mdata_ofs  =%4Zd siz=%4Zd\n", __func__, +		si->si_ofs.mdata_ofs, si->si_ofs.mdata_size); + +	dev_dbg(cd->dev, "%s: cmd_ofs       =%4Zd\n", __func__, +		si->si_ofs.cmd_ofs); +	dev_dbg(cd->dev, "%s: rep_ofs       =%4Zd\n", __func__, +		si->si_ofs.rep_ofs); +	dev_dbg(cd->dev, "%s: rep_sz        =%4Zd\n", __func__, +		si->si_ofs.rep_sz); +	dev_dbg(cd->dev, "%s: num_btns      =%4Zd\n", __func__, +		si->si_ofs.num_btns); +	dev_dbg(cd->dev, "%s: num_btn_regs  =%4Zd\n", __func__, +		si->si_ofs.num_btn_regs); +	dev_dbg(cd->dev, "%s: tt_stat_ofs   =%4Zd\n", __func__, +		si->si_ofs.tt_stat_ofs); +	dev_dbg(cd->dev, "%s: tch_rec_size  =%4Zd\n", __func__, +		si->si_ofs.tch_rec_size); +	dev_dbg(cd->dev, "%s: max_tchs      =%4Zd\n", __func__, +		si->si_ofs.max_tchs); +	dev_dbg(cd->dev, "%s: mode_size     =%4Zd\n", __func__, +		si->si_ofs.mode_size); +	dev_dbg(cd->dev, "%s: data_size     =%4Zd\n", __func__, +		si->si_ofs.data_size); +	dev_dbg(cd->dev, "%s: map_sz        =%4Zd\n", __func__, +		si->si_ofs.map_sz); + +	dev_dbg(cd->dev, "%s: btn_rec_size   =%2Zd\n", __func__, +		si->si_ofs.btn_rec_size); +	dev_dbg(cd->dev, "%s: btn_diff_ofs   =%2Zd\n", __func__, +		si->si_ofs.btn_diff_ofs); +	dev_dbg(cd->dev, "%s: btn_diff_size  =%2Zd\n", __func__, +		si->si_ofs.btn_diff_size); + +	dev_dbg(cd->dev, "%s: max_x    = 0x%04ZX (%Zd)\n", __func__, +		si->si_ofs.max_x, si->si_ofs.max_x); +	dev_dbg(cd->dev, "%s: x_origin = %Zd (%s)\n", __func__, +		si->si_ofs.x_origin, +		si->si_ofs.x_origin == CY_NORMAL_ORIGIN ? +		"left corner" : "right corner"); +	dev_dbg(cd->dev, "%s: max_y    = 0x%04ZX (%Zd)\n", __func__, +		si->si_ofs.max_y, si->si_ofs.max_y); +	dev_dbg(cd->dev, "%s: y_origin = %Zd (%s)\n", __func__, +		si->si_ofs.y_origin, +		si->si_ofs.y_origin == CY_NORMAL_ORIGIN ? +		"upper corner" : "lower corner"); +	dev_dbg(cd->dev, "%s: max_p    = 0x%04ZX (%Zd)\n", __func__, +		si->si_ofs.max_p, si->si_ofs.max_p); + +	dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__, +		si->xy_mode, si->xy_data); +} + +static int cyttsp4_get_sysinfo_regs(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; +	int rc; + +	rc = cyttsp4_si_data_offsets(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_cydata(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_test_data(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_pcfg_data(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_opcfg_data(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_ddata(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_mdata(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_btn_data(cd); +	if (rc < 0) +		return rc; + +	rc = cyttsp4_si_get_op_data_ptrs(cd); +	if (rc < 0) { +		dev_err(cd->dev, "%s: failed to get_op_data\n", +			__func__); +		return rc; +	} + +	cyttsp4_si_put_log_data(cd); + +	/* provide flow control handshake */ +	rc = cyttsp4_handshake(cd, si->si_data.hst_mode); +	if (rc < 0) +		dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n", +			__func__); + +	si->ready = true; +	return rc; +} + +static void cyttsp4_queue_startup_(struct cyttsp4 *cd) +{ +	if (cd->startup_state == STARTUP_NONE) { +		cd->startup_state = STARTUP_QUEUED; +		schedule_work(&cd->startup_work); +		dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__); +	} else { +		dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__, +			cd->startup_state); +	} +} + +static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md, +		int max_slots) +{ +	int t; + +	if (md->num_prv_tch == 0) +		return; + +	for (t = 0; t < max_slots; t++) { +		input_mt_slot(md->input, t); +		input_mt_report_slot_state(md->input, +			MT_TOOL_FINGER, false); +	} +} + +static void cyttsp4_lift_all(struct cyttsp4_mt_data *md) +{ +	if (!md->si) +		return; + +	if (md->num_prv_tch != 0) { +		cyttsp4_report_slot_liftoff(md, +				md->si->si_ofs.tch_abs[CY_TCH_T].max); +		input_sync(md->input); +		md->num_prv_tch = 0; +	} +} + +static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md, +	int *axis, int size, int max, u8 *xy_data, int bofs) +{ +	int nbyte; +	int next; + +	for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { +		dev_vdbg(&md->input->dev, +			"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" +			" xy_data[%d]=%02X(%d) bofs=%d\n", +			__func__, *axis, *axis, size, max, xy_data, next, +			xy_data[next], xy_data[next], bofs); +		*axis = (*axis * 256) + (xy_data[next] >> bofs); +		next++; +	} + +	*axis &= max - 1; + +	dev_vdbg(&md->input->dev, +		"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" +		" xy_data[%d]=%02X(%d)\n", +		__func__, *axis, *axis, size, max, xy_data, next, +		xy_data[next], xy_data[next]); +} + +static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, +	struct cyttsp4_touch *touch, u8 *xy_data) +{ +	struct device *dev = &md->input->dev; +	struct cyttsp4_sysinfo *si = md->si; +	enum cyttsp4_tch_abs abs; +	int tmp; +	bool flipped; + +	for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { +		cyttsp4_get_touch_axis(md, &touch->abs[abs], +			si->si_ofs.tch_abs[abs].size, +			si->si_ofs.tch_abs[abs].max, +			xy_data + si->si_ofs.tch_abs[abs].ofs, +			si->si_ofs.tch_abs[abs].bofs); +		dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__, +			cyttsp4_tch_abs_string[abs], +			touch->abs[abs], touch->abs[abs]); +	} + +	if (md->pdata->flags & CY_FLAG_FLIP) { +		tmp = touch->abs[CY_TCH_X]; +		touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y]; +		touch->abs[CY_TCH_Y] = tmp; +		flipped = true; +	} else +		flipped = false; + +	if (md->pdata->flags & CY_FLAG_INV_X) { +		if (flipped) +			touch->abs[CY_TCH_X] = md->si->si_ofs.max_y - +				touch->abs[CY_TCH_X]; +		else +			touch->abs[CY_TCH_X] = md->si->si_ofs.max_x - +				touch->abs[CY_TCH_X]; +	} +	if (md->pdata->flags & CY_FLAG_INV_Y) { +		if (flipped) +			touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x - +				touch->abs[CY_TCH_Y]; +		else +			touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y - +				touch->abs[CY_TCH_Y]; +	} + +	dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", +		__func__, flipped ? "true" : "false", +		md->pdata->flags & CY_FLAG_INV_X ? "true" : "false", +		md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", +		touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], +		touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); +} + +static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids) +{ +	int t; + +	for (t = 0; t < max_slots; t++) { +		if (ids[t]) +			continue; +		input_mt_slot(input, t); +		input_mt_report_slot_state(input, MT_TOOL_FINGER, false); +	} + +	input_sync(input); +} + +static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) +{ +	struct device *dev = &md->input->dev; +	struct cyttsp4_sysinfo *si = md->si; +	struct cyttsp4_touch tch; +	int sig; +	int i, j, t = 0; +	int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)]; + +	memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); +	for (i = 0; i < num_cur_tch; i++) { +		cyttsp4_get_touch(md, &tch, si->xy_data + +			(i * si->si_ofs.tch_rec_size)); +		if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs +			[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) || +			(tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs +			[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) { +			dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", +				__func__, i, tch.abs[CY_TCH_T], +				md->pdata->frmwrk->abs[(CY_ABS_ID_OST * +				CY_NUM_ABS_SET) + CY_MAX_OST]); +			continue; +		} + +		/* use 0 based track id's */ +		sig = md->pdata->frmwrk->abs +			[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0]; +		if (sig != CY_IGNORE_VALUE) { +			t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs +				[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]; +			if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) { +				dev_dbg(dev, "%s: t=%d e=%d lift-off\n", +					__func__, t, tch.abs[CY_TCH_E]); +				goto cyttsp4_get_mt_touches_pr_tch; +			} +			input_mt_slot(md->input, t); +			input_mt_report_slot_state(md->input, MT_TOOL_FINGER, +					true); +			ids[t] = true; +		} + +		/* all devices: position and pressure fields */ +		for (j = 0; j <= CY_ABS_W_OST; j++) { +			sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * +				CY_NUM_ABS_SET) + 0]; +			if (sig != CY_IGNORE_VALUE) +				input_report_abs(md->input, sig, +					tch.abs[CY_TCH_X + j]); +		} +		if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { +			/* +			 * TMA400 size and orientation fields: +			 * if pressure is non-zero and major touch +			 * signal is zero, then set major and minor touch +			 * signals to minimum non-zero value +			 */ +			if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0) +				tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1; + +			/* Get the extended touch fields */ +			for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { +				sig = md->pdata->frmwrk->abs +					[((CY_ABS_MAJ_OST + j) * +					CY_NUM_ABS_SET) + 0]; +				if (sig != CY_IGNORE_VALUE) +					input_report_abs(md->input, sig, +						tch.abs[CY_TCH_MAJ + j]); +			} +		} + +cyttsp4_get_mt_touches_pr_tch: +		if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) +			dev_dbg(dev, +				"%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n", +				__func__, t, +				tch.abs[CY_TCH_X], +				tch.abs[CY_TCH_Y], +				tch.abs[CY_TCH_P], +				tch.abs[CY_TCH_MAJ], +				tch.abs[CY_TCH_MIN], +				tch.abs[CY_TCH_OR], +				tch.abs[CY_TCH_E]); +		else +			dev_dbg(dev, +				"%s: t=%d x=%d y=%d z=%d e=%d\n", __func__, +				t, +				tch.abs[CY_TCH_X], +				tch.abs[CY_TCH_Y], +				tch.abs[CY_TCH_P], +				tch.abs[CY_TCH_E]); +	} + +	cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids); + +	md->num_prv_tch = num_cur_tch; + +	return; +} + +/* read xy_data for all current touches */ +static int cyttsp4_xy_worker(struct cyttsp4 *cd) +{ +	struct cyttsp4_mt_data *md = &cd->md; +	struct device *dev = &md->input->dev; +	struct cyttsp4_sysinfo *si = md->si; +	u8 num_cur_tch; +	u8 hst_mode; +	u8 rep_len; +	u8 rep_stat; +	u8 tt_stat; +	int rc = 0; + +	/* +	 * Get event data from cyttsp4 device. +	 * The event data includes all data +	 * for all active touches. +	 * Event data also includes button data +	 */ +	/* +	 * Use 2 reads: +	 * 1st read to get mode + button bytes + touch count (core) +	 * 2nd read (optional) to get touch 1 - touch n data +	 */ +	hst_mode = si->xy_mode[CY_REG_BASE]; +	rep_len = si->xy_mode[si->si_ofs.rep_ofs]; +	rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1]; +	tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs]; +	dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__, +		"hst_mode=", hst_mode, "rep_len=", rep_len, +		"rep_stat=", rep_stat, "tt_stat=", tt_stat); + +	num_cur_tch = GET_NUM_TOUCHES(tt_stat); +	dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch); + +	if (rep_len == 0 && num_cur_tch > 0) { +		dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n", +			__func__, rep_len, num_cur_tch); +		goto cyttsp4_xy_worker_exit; +	} + +	/* read touches */ +	if (num_cur_tch > 0) { +		rc = cyttsp4_adap_read(cd, si->si_ofs.tt_stat_ofs + 1, +				num_cur_tch * si->si_ofs.tch_rec_size, +				si->xy_data); +		if (rc < 0) { +			dev_err(dev, "%s: read fail on touch regs r=%d\n", +				__func__, rc); +			goto cyttsp4_xy_worker_exit; +		} +	} + +	/* print xy data */ +	cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_data, num_cur_tch * +		si->si_ofs.tch_rec_size, "xy_data"); + +	/* check any error conditions */ +	if (IS_BAD_PKT(rep_stat)) { +		dev_dbg(dev, "%s: Invalid buffer detected\n", __func__); +		rc = 0; +		goto cyttsp4_xy_worker_exit; +	} + +	if (IS_LARGE_AREA(tt_stat)) +		dev_dbg(dev, "%s: Large area detected\n", __func__); + +	if (num_cur_tch > si->si_ofs.max_tchs) { +		dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%Zd)\n", +				__func__, num_cur_tch, si->si_ofs.max_tchs); +		num_cur_tch = si->si_ofs.max_tchs; +	} + +	/* extract xy_data for all currently reported touches */ +	dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__, +		num_cur_tch); +	if (num_cur_tch) +		cyttsp4_get_mt_touches(md, num_cur_tch); +	else +		cyttsp4_lift_all(md); + +	rc = 0; + +cyttsp4_xy_worker_exit: +	return rc; +} + +static int cyttsp4_mt_attention(struct cyttsp4 *cd) +{ +	struct device *dev = cd->dev; +	struct cyttsp4_mt_data *md = &cd->md; +	int rc = 0; + +	if (!md->si) +		return 0; + +	mutex_lock(&md->report_lock); +	if (!md->is_suspended) { +		/* core handles handshake */ +		rc = cyttsp4_xy_worker(cd); +	} else { +		dev_vdbg(dev, "%s: Ignoring report while suspended\n", +			__func__); +	} +	mutex_unlock(&md->report_lock); +	if (rc < 0) +		dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + +	return rc; +} + +static irqreturn_t cyttsp4_irq(int irq, void *handle) +{ +	struct cyttsp4 *cd = handle; +	struct device *dev = cd->dev; +	enum cyttsp4_mode cur_mode; +	u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs; +	u8 mode[3]; +	int rc; + +	/* +	 * Check whether this IRQ should be ignored (external) +	 * This should be the very first thing to check since +	 * ignore_irq may be set for a very short period of time +	 */ +	if (atomic_read(&cd->ignore_irq)) { +		dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); +		return IRQ_HANDLED; +	} + +	dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status); + +	mutex_lock(&cd->system_lock); + +	/* Just to debug */ +	if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) +		dev_vdbg(dev, "%s: Received IRQ while in sleep\n", __func__); + +	rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), mode); +	if (rc) { +		dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); +		goto cyttsp4_irq_exit; +	} +	dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__, +			mode[0], mode[1], mode[2]); + +	if (IS_BOOTLOADER(mode[0], mode[1])) { +		cur_mode = CY_MODE_BOOTLOADER; +		dev_vdbg(dev, "%s: bl running\n", __func__); +		if (cd->mode == CY_MODE_BOOTLOADER) { +			/* Signal bootloader heartbeat heard */ +			wake_up(&cd->wait_q); +			goto cyttsp4_irq_exit; +		} + +		/* switch to bootloader */ +		dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n", +			__func__, cd->mode, cur_mode); + +		/* catch operation->bl glitch */ +		if (cd->mode != CY_MODE_UNKNOWN) { +			/* Incase startup_state do not let startup_() */ +			cd->mode = CY_MODE_UNKNOWN; +			cyttsp4_queue_startup_(cd); +			goto cyttsp4_irq_exit; +		} + +		/* +		 * do not wake thread on this switch since +		 * it is possible to get an early heartbeat +		 * prior to performing the reset +		 */ +		cd->mode = cur_mode; + +		goto cyttsp4_irq_exit; +	} + +	switch (mode[0] & CY_HST_MODE) { +	case CY_HST_OPERATE: +		cur_mode = CY_MODE_OPERATIONAL; +		dev_vdbg(dev, "%s: operational\n", __func__); +		break; +	case CY_HST_CAT: +		cur_mode = CY_MODE_CAT; +		dev_vdbg(dev, "%s: CaT\n", __func__); +		break; +	case CY_HST_SYSINFO: +		cur_mode = CY_MODE_SYSINFO; +		dev_vdbg(dev, "%s: sysinfo\n", __func__); +		break; +	default: +		cur_mode = CY_MODE_UNKNOWN; +		dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__, +			mode[0]); +		break; +	} + +	/* Check whether this IRQ should be ignored (internal) */ +	if (cd->int_status & CY_INT_IGNORE) { +		dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); +		goto cyttsp4_irq_exit; +	} + +	/* Check for wake up interrupt */ +	if (cd->int_status & CY_INT_AWAKE) { +		cd->int_status &= ~CY_INT_AWAKE; +		wake_up(&cd->wait_q); +		dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__); +		goto cyttsp4_irq_handshake; +	} + +	/* Expecting mode change interrupt */ +	if ((cd->int_status & CY_INT_MODE_CHANGE) +			&& (mode[0] & CY_HST_MODE_CHANGE) == 0) { +		cd->int_status &= ~CY_INT_MODE_CHANGE; +		dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n", +				__func__, cd->mode, cur_mode); +		cd->mode = cur_mode; +		wake_up(&cd->wait_q); +		goto cyttsp4_irq_handshake; +	} + +	/* compare current core mode to current device mode */ +	dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n", +			__func__, cd->mode, cur_mode); +	if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) { +		/* Unexpected mode change occurred */ +		dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode, +				cur_mode, cd->int_status); +		dev_dbg(dev, "%s: Unexpected mode change, startup\n", +				__func__); +		cyttsp4_queue_startup_(cd); +		goto cyttsp4_irq_exit; +	} + +	/* Expecting command complete interrupt */ +	dev_vdbg(dev, "%s: command byte:0x%x\n", __func__, mode[cmd_ofs]); +	if ((cd->int_status & CY_INT_EXEC_CMD) +			&& mode[cmd_ofs] & CY_CMD_COMPLETE) { +		cd->int_status &= ~CY_INT_EXEC_CMD; +		dev_vdbg(dev, "%s: Received command complete interrupt\n", +				__func__); +		wake_up(&cd->wait_q); +		/* +		 * It is possible to receive a single interrupt for +		 * command complete and touch/button status report. +		 * Continue processing for a possible status report. +		 */ +	} + +	/* This should be status report, read status regs */ +	if (cd->mode == CY_MODE_OPERATIONAL) { +		dev_vdbg(dev, "%s: Read status registers\n", __func__); +		rc = cyttsp4_load_status_regs(cd); +		if (rc < 0) +			dev_err(dev, "%s: fail read mode regs r=%d\n", +				__func__, rc); +	} + +	cyttsp4_mt_attention(cd); + +cyttsp4_irq_handshake: +	/* handshake the event */ +	dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n", +			__func__, mode[0], rc); +	rc = cyttsp4_handshake(cd, mode[0]); +	if (rc < 0) +		dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n", +				__func__, mode[0], rc); + +	/* +	 * a non-zero udelay period is required for using +	 * IRQF_TRIGGER_LOW in order to delay until the +	 * device completes isr deassert +	 */ +	udelay(cd->cpdata->level_irq_udelay); + +cyttsp4_irq_exit: +	mutex_unlock(&cd->system_lock); +	return IRQ_HANDLED; +} + +static void cyttsp4_start_wd_timer(struct cyttsp4 *cd) +{ +	if (!CY_WATCHDOG_TIMEOUT) +		return; + +	mod_timer(&cd->watchdog_timer, jiffies + +			msecs_to_jiffies(CY_WATCHDOG_TIMEOUT)); +} + +static void cyttsp4_stop_wd_timer(struct cyttsp4 *cd) +{ +	if (!CY_WATCHDOG_TIMEOUT) +		return; + +	/* +	 * Ensure we wait until the watchdog timer +	 * running on a different CPU finishes +	 */ +	del_timer_sync(&cd->watchdog_timer); +	cancel_work_sync(&cd->watchdog_work); +	del_timer_sync(&cd->watchdog_timer); +} + +static void cyttsp4_watchdog_timer(unsigned long handle) +{ +	struct cyttsp4 *cd = (struct cyttsp4 *)handle; + +	dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__); + +	schedule_work(&cd->watchdog_work); + +	return; +} + +static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr, +		int timeout_ms) +{ +	int t = msecs_to_jiffies(timeout_ms); +	bool with_timeout = (timeout_ms != 0); + +	mutex_lock(&cd->system_lock); +	if (!cd->exclusive_dev && cd->exclusive_waits == 0) { +		cd->exclusive_dev = ownptr; +		goto exit; +	} + +	cd->exclusive_waits++; +wait: +	mutex_unlock(&cd->system_lock); +	if (with_timeout) { +		t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); +		if (IS_TMO(t)) { +			dev_err(cd->dev, "%s: tmo waiting exclusive access\n", +				__func__); +			mutex_lock(&cd->system_lock); +			cd->exclusive_waits--; +			mutex_unlock(&cd->system_lock); +			return -ETIME; +		} +	} else { +		wait_event(cd->wait_q, !cd->exclusive_dev); +	} +	mutex_lock(&cd->system_lock); +	if (cd->exclusive_dev) +		goto wait; +	cd->exclusive_dev = ownptr; +	cd->exclusive_waits--; +exit: +	mutex_unlock(&cd->system_lock); + +	return 0; +} + +/* + * returns error if was not owned + */ +static int cyttsp4_release_exclusive(struct cyttsp4 *cd, void *ownptr) +{ +	mutex_lock(&cd->system_lock); +	if (cd->exclusive_dev != ownptr) { +		mutex_unlock(&cd->system_lock); +		return -EINVAL; +	} + +	dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n", +		__func__, cd->exclusive_dev); +	cd->exclusive_dev = NULL; +	wake_up(&cd->wait_q); +	mutex_unlock(&cd->system_lock); +	return 0; +} + +static int cyttsp4_wait_bl_heartbeat(struct cyttsp4 *cd) +{ +	long t; +	int rc = 0; + +	/* wait heartbeat */ +	dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__); +	t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER, +			msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT)); +	if (IS_TMO(t)) { +		dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n", +			__func__, cd->mode); +		rc = -ETIME; +	} + +	return rc; +} + +static int cyttsp4_wait_sysinfo_mode(struct cyttsp4 *cd) +{ +	long t; + +	dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__); + +	t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO, +			msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); +	if (IS_TMO(t)) { +		dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n", +			__func__, cd->mode); +		mutex_lock(&cd->system_lock); +		cd->int_status &= ~CY_INT_MODE_CHANGE; +		mutex_unlock(&cd->system_lock); +		return -ETIME; +	} + +	return 0; +} + +static int cyttsp4_reset_and_wait(struct cyttsp4 *cd) +{ +	int rc; + +	/* reset hardware */ +	mutex_lock(&cd->system_lock); +	dev_dbg(cd->dev, "%s: reset hw...\n", __func__); +	rc = cyttsp4_hw_reset(cd); +	cd->mode = CY_MODE_UNKNOWN; +	mutex_unlock(&cd->system_lock); +	if (rc < 0) { +		dev_err(cd->dev, "%s:Fail hw reset r=%d\n", __func__, rc); +		return rc; +	} + +	return cyttsp4_wait_bl_heartbeat(cd); +} + +/* + * returns err if refused or timeout; block until mode change complete + * bit is set (mode change interrupt) + */ +static int cyttsp4_set_mode(struct cyttsp4 *cd, int new_mode) +{ +	u8 new_dev_mode; +	u8 mode; +	long t; +	int rc; + +	switch (new_mode) { +	case CY_MODE_OPERATIONAL: +		new_dev_mode = CY_HST_OPERATE; +		break; +	case CY_MODE_SYSINFO: +		new_dev_mode = CY_HST_SYSINFO; +		break; +	case CY_MODE_CAT: +		new_dev_mode = CY_HST_CAT; +		break; +	default: +		dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n", +			__func__, new_mode, new_mode); +		return -EINVAL; +	} + +	/* change mode */ +	dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n", +			__func__, "have exclusive", cd->exclusive_dev, +			new_dev_mode, new_mode); + +	mutex_lock(&cd->system_lock); +	rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); +	if (rc < 0) { +		mutex_unlock(&cd->system_lock); +		dev_err(cd->dev, "%s: Fail read mode r=%d\n", +			__func__, rc); +		goto exit; +	} + +	/* Clear device mode bits and set to new mode */ +	mode &= ~CY_HST_MODE; +	mode |= new_dev_mode | CY_HST_MODE_CHANGE; + +	cd->int_status |= CY_INT_MODE_CHANGE; +	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode), &mode); +	mutex_unlock(&cd->system_lock); +	if (rc < 0) { +		dev_err(cd->dev, "%s: Fail write mode change r=%d\n", +				__func__, rc); +		goto exit; +	} + +	/* wait for mode change done interrupt */ +	t = wait_event_timeout(cd->wait_q, +			(cd->int_status & CY_INT_MODE_CHANGE) == 0, +			msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); +	dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n", +			__func__, t, cd->mode); + +	if (IS_TMO(t)) { +		dev_err(cd->dev, "%s: %s\n", __func__, +				"tmo waiting mode change"); +		mutex_lock(&cd->system_lock); +		cd->int_status &= ~CY_INT_MODE_CHANGE; +		mutex_unlock(&cd->system_lock); +		rc = -EINVAL; +	} + +exit: +	return rc; +} + +static void cyttsp4_watchdog_work(struct work_struct *work) +{ +	struct cyttsp4 *cd = +		container_of(work, struct cyttsp4, watchdog_work); +	u8 *mode; +	int retval; + +	mutex_lock(&cd->system_lock); +	retval = cyttsp4_load_status_regs(cd); +	if (retval < 0) { +		dev_err(cd->dev, +			"%s: failed to access device in watchdog timer r=%d\n", +			__func__, retval); +		cyttsp4_queue_startup_(cd); +		goto cyttsp4_timer_watchdog_exit_error; +	} +	mode = &cd->sysinfo.xy_mode[CY_REG_BASE]; +	if (IS_BOOTLOADER(mode[0], mode[1])) { +		dev_err(cd->dev, +			"%s: device found in bootloader mode when operational mode\n", +			__func__); +		cyttsp4_queue_startup_(cd); +		goto cyttsp4_timer_watchdog_exit_error; +	} + +	cyttsp4_start_wd_timer(cd); +cyttsp4_timer_watchdog_exit_error: +	mutex_unlock(&cd->system_lock); +	return; +} + +static int cyttsp4_core_sleep_(struct cyttsp4 *cd) +{ +	enum cyttsp4_sleep_state ss = SS_SLEEP_ON; +	enum cyttsp4_int_state int_status = CY_INT_IGNORE; +	int rc = 0; +	u8 mode[2]; + +	/* Already in sleep mode? */ +	mutex_lock(&cd->system_lock); +	if (cd->sleep_state == SS_SLEEP_ON) { +		mutex_unlock(&cd->system_lock); +		return 0; +	} +	cd->sleep_state = SS_SLEEPING; +	mutex_unlock(&cd->system_lock); + +	cyttsp4_stop_wd_timer(cd); + +	/* Wait until currently running IRQ handler exits and disable IRQ */ +	disable_irq(cd->irq); + +	dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__); +	mutex_lock(&cd->system_lock); +	rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); +	if (rc) { +		mutex_unlock(&cd->system_lock); +		dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); +		goto error; +	} + +	if (IS_BOOTLOADER(mode[0], mode[1])) { +		mutex_unlock(&cd->system_lock); +		dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__); +		rc = -EINVAL; +		goto error; +	} + +	mode[0] |= CY_HST_SLEEP; +	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode[0]), &mode[0]); +	mutex_unlock(&cd->system_lock); +	if (rc) { +		dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc); +		goto error; +	} +	dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); + +	if (cd->cpdata->power) { +		dev_dbg(cd->dev, "%s: Power down HW\n", __func__); +		rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq); +	} else { +		dev_dbg(cd->dev, "%s: No power function\n", __func__); +		rc = 0; +	} +	if (rc < 0) { +		dev_err(cd->dev, "%s: HW Power down fails r=%d\n", +				__func__, rc); +		goto error; +	} + +	/* Give time to FW to sleep */ +	msleep(50); + +	goto exit; + +error: +	ss = SS_SLEEP_OFF; +	int_status = CY_INT_NONE; +	cyttsp4_start_wd_timer(cd); + +exit: +	mutex_lock(&cd->system_lock); +	cd->sleep_state = ss; +	cd->int_status |= int_status; +	mutex_unlock(&cd->system_lock); +	enable_irq(cd->irq); +	return rc; +} + +static int cyttsp4_startup_(struct cyttsp4 *cd) +{ +	int retry = CY_CORE_STARTUP_RETRY_COUNT; +	int rc; + +	cyttsp4_stop_wd_timer(cd); + +reset: +	if (retry != CY_CORE_STARTUP_RETRY_COUNT) +		dev_dbg(cd->dev, "%s: Retry %d\n", __func__, +			CY_CORE_STARTUP_RETRY_COUNT - retry); + +	/* reset hardware and wait for heartbeat */ +	rc = cyttsp4_reset_and_wait(cd); +	if (rc < 0) { +		dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc); +		if (retry--) +			goto reset; +		goto exit; +	} + +	/* exit bl into sysinfo mode */ +	dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__); +	mutex_lock(&cd->system_lock); +	cd->int_status &= ~CY_INT_IGNORE; +	cd->int_status |= CY_INT_MODE_CHANGE; + +	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(ldr_exit), +			(u8 *)ldr_exit); +	mutex_unlock(&cd->system_lock); +	if (rc < 0) { +		dev_err(cd->dev, "%s: Fail write r=%d\n", __func__, rc); +		if (retry--) +			goto reset; +		goto exit; +	} + +	rc = cyttsp4_wait_sysinfo_mode(cd); +	if (rc < 0) { +		u8 buf[sizeof(ldr_err_app)]; +		int rc1; + +		/* Check for invalid/corrupted touch application */ +		rc1 = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(ldr_err_app), +				buf); +		if (rc1) { +			dev_err(cd->dev, "%s: Fail read r=%d\n", __func__, rc1); +		} else if (!memcmp(buf, ldr_err_app, sizeof(ldr_err_app))) { +			dev_err(cd->dev, "%s: Error launching touch application\n", +				__func__); +			mutex_lock(&cd->system_lock); +			cd->invalid_touch_app = true; +			mutex_unlock(&cd->system_lock); +			goto exit_no_wd; +		} + +		if (retry--) +			goto reset; +		goto exit; +	} + +	mutex_lock(&cd->system_lock); +	cd->invalid_touch_app = false; +	mutex_unlock(&cd->system_lock); + +	/* read sysinfo data */ +	dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__); +	rc = cyttsp4_get_sysinfo_regs(cd); +	if (rc < 0) { +		dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n", +			__func__, rc); +		if (retry--) +			goto reset; +		goto exit; +	} + +	rc = cyttsp4_set_mode(cd, CY_MODE_OPERATIONAL); +	if (rc < 0) { +		dev_err(cd->dev, "%s: failed to set mode to operational rc=%d\n", +			__func__, rc); +		if (retry--) +			goto reset; +		goto exit; +	} + +	cyttsp4_lift_all(&cd->md); + +	/* restore to sleep if was suspended */ +	mutex_lock(&cd->system_lock); +	if (cd->sleep_state == SS_SLEEP_ON) { +		cd->sleep_state = SS_SLEEP_OFF; +		mutex_unlock(&cd->system_lock); +		cyttsp4_core_sleep_(cd); +		goto exit_no_wd; +	} +	mutex_unlock(&cd->system_lock); + +exit: +	cyttsp4_start_wd_timer(cd); +exit_no_wd: +	return rc; +} + +static int cyttsp4_startup(struct cyttsp4 *cd) +{ +	int rc; + +	mutex_lock(&cd->system_lock); +	cd->startup_state = STARTUP_RUNNING; +	mutex_unlock(&cd->system_lock); + +	rc = cyttsp4_request_exclusive(cd, cd->dev, +			CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", +				__func__, cd->exclusive_dev, cd->dev); +		goto exit; +	} + +	rc = cyttsp4_startup_(cd); + +	if (cyttsp4_release_exclusive(cd, cd->dev) < 0) +		/* Don't return fail code, mode is already changed. */ +		dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); +	else +		dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + +exit: +	mutex_lock(&cd->system_lock); +	cd->startup_state = STARTUP_NONE; +	mutex_unlock(&cd->system_lock); + +	/* Wake the waiters for end of startup */ +	wake_up(&cd->wait_q); + +	return rc; +} + +static void cyttsp4_startup_work_function(struct work_struct *work) +{ +	struct cyttsp4 *cd =  container_of(work, struct cyttsp4, startup_work); +	int rc; + +	rc = cyttsp4_startup(cd); +	if (rc < 0) +		dev_err(cd->dev, "%s: Fail queued startup r=%d\n", +			__func__, rc); +} + +static void cyttsp4_free_si_ptrs(struct cyttsp4 *cd) +{ +	struct cyttsp4_sysinfo *si = &cd->sysinfo; + +	if (!si) +		return; + +	kfree(si->si_ptrs.cydata); +	kfree(si->si_ptrs.test); +	kfree(si->si_ptrs.pcfg); +	kfree(si->si_ptrs.opcfg); +	kfree(si->si_ptrs.ddata); +	kfree(si->si_ptrs.mdata); +	kfree(si->btn); +	kfree(si->xy_mode); +	kfree(si->xy_data); +	kfree(si->btn_rec_data); +} + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) +static int cyttsp4_core_sleep(struct cyttsp4 *cd) +{ +	int rc; + +	rc = cyttsp4_request_exclusive(cd, cd->dev, +			CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", +				__func__, cd->exclusive_dev, cd->dev); +		return 0; +	} + +	rc = cyttsp4_core_sleep_(cd); + +	if (cyttsp4_release_exclusive(cd, cd->dev) < 0) +		dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); +	else +		dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + +	return rc; +} + +static int cyttsp4_core_wake_(struct cyttsp4 *cd) +{ +	struct device *dev = cd->dev; +	int rc; +	u8 mode; +	int t; + +	/* Already woken? */ +	mutex_lock(&cd->system_lock); +	if (cd->sleep_state == SS_SLEEP_OFF) { +		mutex_unlock(&cd->system_lock); +		return 0; +	} +	cd->int_status &= ~CY_INT_IGNORE; +	cd->int_status |= CY_INT_AWAKE; +	cd->sleep_state = SS_WAKING; + +	if (cd->cpdata->power) { +		dev_dbg(dev, "%s: Power up HW\n", __func__); +		rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq); +	} else { +		dev_dbg(dev, "%s: No power function\n", __func__); +		rc = -ENOSYS; +	} +	if (rc < 0) { +		dev_err(dev, "%s: HW Power up fails r=%d\n", +				__func__, rc); + +		/* Initiate a read transaction to wake up */ +		cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); +	} else +		dev_vdbg(cd->dev, "%s: HW power up succeeds\n", +			__func__); +	mutex_unlock(&cd->system_lock); + +	t = wait_event_timeout(cd->wait_q, +			(cd->int_status & CY_INT_AWAKE) == 0, +			msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT)); +	if (IS_TMO(t)) { +		dev_err(dev, "%s: TMO waiting for wakeup\n", __func__); +		mutex_lock(&cd->system_lock); +		cd->int_status &= ~CY_INT_AWAKE; +		/* Try starting up */ +		cyttsp4_queue_startup_(cd); +		mutex_unlock(&cd->system_lock); +	} + +	mutex_lock(&cd->system_lock); +	cd->sleep_state = SS_SLEEP_OFF; +	mutex_unlock(&cd->system_lock); + +	cyttsp4_start_wd_timer(cd); + +	return 0; +} + +static int cyttsp4_core_wake(struct cyttsp4 *cd) +{ +	int rc; + +	rc = cyttsp4_request_exclusive(cd, cd->dev, +			CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); +	if (rc < 0) { +		dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", +				__func__, cd->exclusive_dev, cd->dev); +		return 0; +	} + +	rc = cyttsp4_core_wake_(cd); + +	if (cyttsp4_release_exclusive(cd, cd->dev) < 0) +		dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); +	else +		dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + +	return rc; +} + +static int cyttsp4_core_suspend(struct device *dev) +{ +	struct cyttsp4 *cd = dev_get_drvdata(dev); +	struct cyttsp4_mt_data *md = &cd->md; +	int rc; + +	md->is_suspended = true; + +	rc = cyttsp4_core_sleep(cd); +	if (rc < 0) { +		dev_err(dev, "%s: Error on sleep\n", __func__); +		return -EAGAIN; +	} +	return 0; +} + +static int cyttsp4_core_resume(struct device *dev) +{ +	struct cyttsp4 *cd = dev_get_drvdata(dev); +	struct cyttsp4_mt_data *md = &cd->md; +	int rc; + +	md->is_suspended = false; + +	rc = cyttsp4_core_wake(cd); +	if (rc < 0) { +		dev_err(dev, "%s: Error on wake\n", __func__); +		return -EAGAIN; +	} + +	return 0; +} +#endif + +const struct dev_pm_ops cyttsp4_pm_ops = { +	SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume) +	SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL) +}; +EXPORT_SYMBOL_GPL(cyttsp4_pm_ops); + +static int cyttsp4_mt_open(struct input_dev *input) +{ +	pm_runtime_get(input->dev.parent); +	return 0; +} + +static void cyttsp4_mt_close(struct input_dev *input) +{ +	struct cyttsp4_mt_data *md = input_get_drvdata(input); +	mutex_lock(&md->report_lock); +	if (!md->is_suspended) +		pm_runtime_put(input->dev.parent); +	mutex_unlock(&md->report_lock); +} + + +static int cyttsp4_setup_input_device(struct cyttsp4 *cd) +{ +	struct device *dev = cd->dev; +	struct cyttsp4_mt_data *md = &cd->md; +	int signal = CY_IGNORE_VALUE; +	int max_x, max_y, max_p, min, max; +	int max_x_tmp, max_y_tmp; +	int i; +	int rc; + +	dev_vdbg(dev, "%s: Initialize event signals\n", __func__); +	__set_bit(EV_ABS, md->input->evbit); +	__set_bit(EV_REL, md->input->evbit); +	__set_bit(EV_KEY, md->input->evbit); + +	max_x_tmp = md->si->si_ofs.max_x; +	max_y_tmp = md->si->si_ofs.max_y; + +	/* get maximum values from the sysinfo data */ +	if (md->pdata->flags & CY_FLAG_FLIP) { +		max_x = max_y_tmp - 1; +		max_y = max_x_tmp - 1; +	} else { +		max_x = max_x_tmp - 1; +		max_y = max_y_tmp - 1; +	} +	max_p = md->si->si_ofs.max_p; + +	/* set event signal capabilities */ +	for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { +		signal = md->pdata->frmwrk->abs +			[(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST]; +		if (signal != CY_IGNORE_VALUE) { +			__set_bit(signal, md->input->absbit); +			min = md->pdata->frmwrk->abs +				[(i * CY_NUM_ABS_SET) + CY_MIN_OST]; +			max = md->pdata->frmwrk->abs +				[(i * CY_NUM_ABS_SET) + CY_MAX_OST]; +			if (i == CY_ABS_ID_OST) { +				/* shift track ids down to start at 0 */ +				max = max - min; +				min = min - min; +			} else if (i == CY_ABS_X_OST) +				max = max_x; +			else if (i == CY_ABS_Y_OST) +				max = max_y; +			else if (i == CY_ABS_P_OST) +				max = max_p; +			input_set_abs_params(md->input, signal, min, max, +				md->pdata->frmwrk->abs +				[(i * CY_NUM_ABS_SET) + CY_FUZZ_OST], +				md->pdata->frmwrk->abs +				[(i * CY_NUM_ABS_SET) + CY_FLAT_OST]); +			dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", +				__func__, signal, min, max); +			if ((i == CY_ABS_ID_OST) && +				(md->si->si_ofs.tch_rec_size < +				CY_TMA4XX_TCH_REC_SIZE)) +				break; +		} +	} + +	input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max, +			INPUT_MT_DIRECT); +	rc = input_register_device(md->input); +	if (rc < 0) +		dev_err(dev, "%s: Error, failed register input device r=%d\n", +			__func__, rc); +	return rc; +} + +static int cyttsp4_mt_probe(struct cyttsp4 *cd) +{ +	struct device *dev = cd->dev; +	struct cyttsp4_mt_data *md = &cd->md; +	struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata; +	int rc = 0; + +	mutex_init(&md->report_lock); +	md->pdata = pdata; +	/* Create the input device and register it. */ +	dev_vdbg(dev, "%s: Create the input device and register it\n", +		__func__); +	md->input = input_allocate_device(); +	if (md->input == NULL) { +		dev_err(dev, "%s: Error, failed to allocate input device\n", +			__func__); +		rc = -ENOSYS; +		goto error_alloc_failed; +	} + +	md->input->name = pdata->inp_dev_name; +	scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev)); +	md->input->phys = md->phys; +	md->input->id.bustype = cd->bus_ops->bustype; +	md->input->dev.parent = dev; +	md->input->open = cyttsp4_mt_open; +	md->input->close = cyttsp4_mt_close; +	input_set_drvdata(md->input, md); + +	/* get sysinfo */ +	md->si = &cd->sysinfo; +	if (!md->si) { +		dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", +			__func__, md->si); +		goto error_get_sysinfo; +	} + +	rc = cyttsp4_setup_input_device(cd); +	if (rc) +		goto error_init_input; + +	return 0; + +error_init_input: +	input_free_device(md->input); +error_get_sysinfo: +	input_set_drvdata(md->input, NULL); +error_alloc_failed: +	dev_err(dev, "%s failed.\n", __func__); +	return rc; +} + +struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, +		struct device *dev, u16 irq, size_t xfer_buf_size) +{ +	struct cyttsp4 *cd; +	struct cyttsp4_platform_data *pdata = dev_get_platdata(dev); +	unsigned long irq_flags; +	int rc = 0; + +	if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { +		dev_err(dev, "%s: Missing platform data\n", __func__); +		rc = -ENODEV; +		goto error_no_pdata; +	} + +	cd = kzalloc(sizeof(*cd), GFP_KERNEL); +	if (!cd) { +		dev_err(dev, "%s: Error, kzalloc\n", __func__); +		rc = -ENOMEM; +		goto error_alloc_data; +	} + +	cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL); +	if (!cd->xfer_buf) { +		dev_err(dev, "%s: Error, kzalloc\n", __func__); +		rc = -ENOMEM; +		goto error_free_cd; +	} + +	/* Initialize device info */ +	cd->dev = dev; +	cd->pdata = pdata; +	cd->cpdata = pdata->core_pdata; +	cd->bus_ops = ops; + +	/* Initialize mutexes and spinlocks */ +	mutex_init(&cd->system_lock); +	mutex_init(&cd->adap_lock); + +	/* Initialize wait queue */ +	init_waitqueue_head(&cd->wait_q); + +	/* Initialize works */ +	INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function); +	INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work); + +	/* Initialize IRQ */ +	cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); +	if (cd->irq < 0) { +		rc = -EINVAL; +		goto error_free_xfer; +	} + +	dev_set_drvdata(dev, cd); + +	/* Call platform init function */ +	if (cd->cpdata->init) { +		dev_dbg(cd->dev, "%s: Init HW\n", __func__); +		rc = cd->cpdata->init(cd->cpdata, 1, cd->dev); +	} else { +		dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__); +		rc = 0; +	} +	if (rc < 0) +		dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); + +	dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq); +	if (cd->cpdata->level_irq_udelay > 0) +		/* use level triggered interrupts */ +		irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; +	else +		/* use edge triggered interrupts */ +		irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + +	rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags, +		dev_name(dev), cd); +	if (rc < 0) { +		dev_err(dev, "%s: Error, could not request irq\n", __func__); +		goto error_request_irq; +	} + +	/* Setup watchdog timer */ +	setup_timer(&cd->watchdog_timer, cyttsp4_watchdog_timer, +		(unsigned long)cd); + +	/* +	 * call startup directly to ensure that the device +	 * is tested before leaving the probe +	 */ +	rc = cyttsp4_startup(cd); + +	/* Do not fail probe if startup fails but the device is detected */ +	if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) { +		dev_err(cd->dev, "%s: Fail initial startup r=%d\n", +			__func__, rc); +		goto error_startup; +	} + +	rc = cyttsp4_mt_probe(cd); +	if (rc < 0) { +		dev_err(dev, "%s: Error, fail mt probe\n", __func__); +		goto error_startup; +	} + +	pm_runtime_enable(dev); + +	return cd; + +error_startup: +	cancel_work_sync(&cd->startup_work); +	cyttsp4_stop_wd_timer(cd); +	pm_runtime_disable(dev); +	cyttsp4_free_si_ptrs(cd); +	free_irq(cd->irq, cd); +error_request_irq: +	if (cd->cpdata->init) +		cd->cpdata->init(cd->cpdata, 0, dev); +error_free_xfer: +	kfree(cd->xfer_buf); +error_free_cd: +	kfree(cd); +error_alloc_data: +error_no_pdata: +	dev_err(dev, "%s failed.\n", __func__); +	return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(cyttsp4_probe); + +static void cyttsp4_mt_release(struct cyttsp4_mt_data *md) +{ +	input_unregister_device(md->input); +	input_set_drvdata(md->input, NULL); +} + +int cyttsp4_remove(struct cyttsp4 *cd) +{ +	struct device *dev = cd->dev; + +	cyttsp4_mt_release(&cd->md); + +	/* +	 * Suspend the device before freeing the startup_work and stopping +	 * the watchdog since sleep function restarts watchdog on failure +	 */ +	pm_runtime_suspend(dev); +	pm_runtime_disable(dev); + +	cancel_work_sync(&cd->startup_work); + +	cyttsp4_stop_wd_timer(cd); + +	free_irq(cd->irq, cd); +	if (cd->cpdata->init) +		cd->cpdata->init(cd->cpdata, 0, dev); +	cyttsp4_free_si_ptrs(cd); +	kfree(cd); +	return 0; +} +EXPORT_SYMBOL_GPL(cyttsp4_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h new file mode 100644 index 00000000000..8e0d4d490b2 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.h @@ -0,0 +1,472 @@ +/* + * cyttsp4_core.h + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#ifndef _LINUX_CYTTSP4_CORE_H +#define _LINUX_CYTTSP4_CORE_H + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/stringify.h> +#include <linux/types.h> +#include <linux/platform_data/cyttsp4.h> + +#define CY_REG_BASE			0x00 + +#define CY_POST_CODEL_WDG_RST		0x01 +#define CY_POST_CODEL_CFG_DATA_CRC_FAIL	0x02 +#define CY_POST_CODEL_PANEL_TEST_FAIL	0x04 + +#define CY_NUM_BTN_PER_REG		4 + +/* touch record system information offset masks and shifts */ +#define CY_BYTE_OFS_MASK		0x1F +#define CY_BOFS_MASK			0xE0 +#define CY_BOFS_SHIFT			5 + +#define CY_TMA1036_TCH_REC_SIZE		6 +#define CY_TMA4XX_TCH_REC_SIZE		9 +#define CY_TMA1036_MAX_TCH		0x0E +#define CY_TMA4XX_MAX_TCH		0x1E + +#define CY_NORMAL_ORIGIN		0	/* upper, left corner */ +#define CY_INVERT_ORIGIN		1	/* lower, right corner */ + +/* helpers */ +#define GET_NUM_TOUCHES(x)		((x) & 0x1F) +#define IS_LARGE_AREA(x)		((x) & 0x20) +#define IS_BAD_PKT(x)			((x) & 0x20) +#define IS_BOOTLOADER(hst_mode, reset_detect)	\ +		((hst_mode) & 0x01 || (reset_detect) != 0) +#define IS_TMO(t)			((t) == 0) + + +enum cyttsp_cmd_bits { +	CY_CMD_COMPLETE = (1 << 6), +}; + +/* Timeout in ms. */ +#define CY_WATCHDOG_TIMEOUT		1000 + +#define CY_MAX_PRINT_SIZE		512 +#ifdef VERBOSE_DEBUG +#define CY_MAX_PRBUF_SIZE		PIPE_BUF +#define CY_PR_TRUNCATED			" truncated..." +#endif + +enum cyttsp4_ic_grpnum { +	CY_IC_GRPNUM_RESERVED, +	CY_IC_GRPNUM_CMD_REGS, +	CY_IC_GRPNUM_TCH_REP, +	CY_IC_GRPNUM_DATA_REC, +	CY_IC_GRPNUM_TEST_REC, +	CY_IC_GRPNUM_PCFG_REC, +	CY_IC_GRPNUM_TCH_PARM_VAL, +	CY_IC_GRPNUM_TCH_PARM_SIZE, +	CY_IC_GRPNUM_RESERVED1, +	CY_IC_GRPNUM_RESERVED2, +	CY_IC_GRPNUM_OPCFG_REC, +	CY_IC_GRPNUM_DDATA_REC, +	CY_IC_GRPNUM_MDATA_REC, +	CY_IC_GRPNUM_TEST_REGS, +	CY_IC_GRPNUM_BTN_KEYS, +	CY_IC_GRPNUM_TTHE_REGS, +	CY_IC_GRPNUM_NUM +}; + +enum cyttsp4_int_state { +	CY_INT_NONE, +	CY_INT_IGNORE      = (1 << 0), +	CY_INT_MODE_CHANGE = (1 << 1), +	CY_INT_EXEC_CMD    = (1 << 2), +	CY_INT_AWAKE       = (1 << 3), +}; + +enum cyttsp4_mode { +	CY_MODE_UNKNOWN, +	CY_MODE_BOOTLOADER   = (1 << 1), +	CY_MODE_OPERATIONAL  = (1 << 2), +	CY_MODE_SYSINFO      = (1 << 3), +	CY_MODE_CAT          = (1 << 4), +	CY_MODE_STARTUP      = (1 << 5), +	CY_MODE_LOADER       = (1 << 6), +	CY_MODE_CHANGE_MODE  = (1 << 7), +	CY_MODE_CHANGED      = (1 << 8), +	CY_MODE_CMD_COMPLETE = (1 << 9), +}; + +enum cyttsp4_sleep_state { +	SS_SLEEP_OFF, +	SS_SLEEP_ON, +	SS_SLEEPING, +	SS_WAKING, +}; + +enum cyttsp4_startup_state { +	STARTUP_NONE, +	STARTUP_QUEUED, +	STARTUP_RUNNING, +}; + +#define CY_NUM_REVCTRL			8 +struct cyttsp4_cydata { +	u8 ttpidh; +	u8 ttpidl; +	u8 fw_ver_major; +	u8 fw_ver_minor; +	u8 revctrl[CY_NUM_REVCTRL]; +	u8 blver_major; +	u8 blver_minor; +	u8 jtag_si_id3; +	u8 jtag_si_id2; +	u8 jtag_si_id1; +	u8 jtag_si_id0; +	u8 mfgid_sz; +	u8 cyito_idh; +	u8 cyito_idl; +	u8 cyito_verh; +	u8 cyito_verl; +	u8 ttsp_ver_major; +	u8 ttsp_ver_minor; +	u8 device_info; +	u8 mfg_id[]; +} __packed; + +struct cyttsp4_test { +	u8 post_codeh; +	u8 post_codel; +} __packed; + +struct cyttsp4_pcfg { +	u8 electrodes_x; +	u8 electrodes_y; +	u8 len_xh; +	u8 len_xl; +	u8 len_yh; +	u8 len_yl; +	u8 res_xh; +	u8 res_xl; +	u8 res_yh; +	u8 res_yl; +	u8 max_zh; +	u8 max_zl; +	u8 panel_info0; +} __packed; + +struct cyttsp4_tch_rec_params { +	u8 loc; +	u8 size; +} __packed; + +#define CY_NUM_TCH_FIELDS		7 +#define CY_NUM_EXT_TCH_FIELDS		3 +struct cyttsp4_opcfg { +	u8 cmd_ofs; +	u8 rep_ofs; +	u8 rep_szh; +	u8 rep_szl; +	u8 num_btns; +	u8 tt_stat_ofs; +	u8 obj_cfg0; +	u8 max_tchs; +	u8 tch_rec_size; +	struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS]; +	u8 btn_rec_size;	/* btn record size (in bytes) */ +	u8 btn_diff_ofs;	/* btn data loc, diff counts  */ +	u8 btn_diff_size;	/* btn size of diff counts (in bits) */ +	struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS]; +} __packed; + +struct cyttsp4_sysinfo_ptr { +	struct cyttsp4_cydata *cydata; +	struct cyttsp4_test *test; +	struct cyttsp4_pcfg *pcfg; +	struct cyttsp4_opcfg *opcfg; +	struct cyttsp4_ddata *ddata; +	struct cyttsp4_mdata *mdata; +} __packed; + +struct cyttsp4_sysinfo_data { +	u8 hst_mode; +	u8 reserved; +	u8 map_szh; +	u8 map_szl; +	u8 cydata_ofsh; +	u8 cydata_ofsl; +	u8 test_ofsh; +	u8 test_ofsl; +	u8 pcfg_ofsh; +	u8 pcfg_ofsl; +	u8 opcfg_ofsh; +	u8 opcfg_ofsl; +	u8 ddata_ofsh; +	u8 ddata_ofsl; +	u8 mdata_ofsh; +	u8 mdata_ofsl; +} __packed; + +enum cyttsp4_tch_abs {	/* for ordering within the extracted touch data array */ +	CY_TCH_X,	/* X */ +	CY_TCH_Y,	/* Y */ +	CY_TCH_P,	/* P (Z) */ +	CY_TCH_T,	/* TOUCH ID */ +	CY_TCH_E,	/* EVENT ID */ +	CY_TCH_O,	/* OBJECT ID */ +	CY_TCH_W,	/* SIZE */ +	CY_TCH_MAJ,	/* TOUCH_MAJOR */ +	CY_TCH_MIN,	/* TOUCH_MINOR */ +	CY_TCH_OR,	/* ORIENTATION */ +	CY_TCH_NUM_ABS +}; + +static const char * const cyttsp4_tch_abs_string[] = { +	[CY_TCH_X]	= "X", +	[CY_TCH_Y]	= "Y", +	[CY_TCH_P]	= "P", +	[CY_TCH_T]	= "T", +	[CY_TCH_E]	= "E", +	[CY_TCH_O]	= "O", +	[CY_TCH_W]	= "W", +	[CY_TCH_MAJ]	= "MAJ", +	[CY_TCH_MIN]	= "MIN", +	[CY_TCH_OR]	= "OR", +	[CY_TCH_NUM_ABS] = "INVALID" +}; + +struct cyttsp4_touch { +	int abs[CY_TCH_NUM_ABS]; +}; + +struct cyttsp4_tch_abs_params { +	size_t ofs;	/* abs byte offset */ +	size_t size;	/* size in bits */ +	size_t max;	/* max value */ +	size_t bofs;	/* bit offset */ +}; + +struct cyttsp4_sysinfo_ofs { +	size_t chip_type; +	size_t cmd_ofs; +	size_t rep_ofs; +	size_t rep_sz; +	size_t num_btns; +	size_t num_btn_regs;	/* ceil(num_btns/4) */ +	size_t tt_stat_ofs; +	size_t tch_rec_size; +	size_t obj_cfg0; +	size_t max_tchs; +	size_t mode_size; +	size_t data_size; +	size_t map_sz; +	size_t max_x; +	size_t x_origin;	/* left or right corner */ +	size_t max_y; +	size_t y_origin;	/* upper or lower corner */ +	size_t max_p; +	size_t cydata_ofs; +	size_t test_ofs; +	size_t pcfg_ofs; +	size_t opcfg_ofs; +	size_t ddata_ofs; +	size_t mdata_ofs; +	size_t cydata_size; +	size_t test_size; +	size_t pcfg_size; +	size_t opcfg_size; +	size_t ddata_size; +	size_t mdata_size; +	size_t btn_keys_size; +	struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS]; +	size_t btn_rec_size; /* btn record size (in bytes) */ +	size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */ +	size_t btn_diff_size;/* btn size of diff counts (in bits) */ +}; + +enum cyttsp4_btn_state { +	CY_BTN_RELEASED, +	CY_BTN_PRESSED, +	CY_BTN_NUM_STATE +}; + +struct cyttsp4_btn { +	bool enabled; +	int state;	/* CY_BTN_PRESSED, CY_BTN_RELEASED */ +	int key_code; +}; + +struct cyttsp4_sysinfo { +	bool ready; +	struct cyttsp4_sysinfo_data si_data; +	struct cyttsp4_sysinfo_ptr si_ptrs; +	struct cyttsp4_sysinfo_ofs si_ofs; +	struct cyttsp4_btn *btn;	/* button states */ +	u8 *btn_rec_data;		/* button diff count data */ +	u8 *xy_mode;			/* operational mode and status regs */ +	u8 *xy_data;			/* operational touch regs */ +}; + +struct cyttsp4_mt_data { +	struct cyttsp4_mt_platform_data *pdata; +	struct cyttsp4_sysinfo *si; +	struct input_dev *input; +	struct mutex report_lock; +	bool is_suspended; +	char phys[NAME_MAX]; +	int num_prv_tch; +}; + +struct cyttsp4 { +	struct device *dev; +	struct mutex system_lock; +	struct mutex adap_lock; +	enum cyttsp4_mode mode; +	enum cyttsp4_sleep_state sleep_state; +	enum cyttsp4_startup_state startup_state; +	int int_status; +	wait_queue_head_t wait_q; +	int irq; +	struct work_struct startup_work; +	struct work_struct watchdog_work; +	struct timer_list watchdog_timer; +	struct cyttsp4_sysinfo sysinfo; +	void *exclusive_dev; +	int exclusive_waits; +	atomic_t ignore_irq; +	bool invalid_touch_app; +	struct cyttsp4_mt_data md; +	struct cyttsp4_platform_data *pdata; +	struct cyttsp4_core_platform_data *cpdata; +	const struct cyttsp4_bus_ops *bus_ops; +	u8 *xfer_buf; +#ifdef VERBOSE_DEBUG +	u8 pr_buf[CY_MAX_PRBUF_SIZE]; +#endif +}; + +struct cyttsp4_bus_ops { +	u16 bustype; +	int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, +			const void *values); +	int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, +			void *values); +}; + +enum cyttsp4_hst_mode_bits { +	CY_HST_TOGGLE      = (1 << 7), +	CY_HST_MODE_CHANGE = (1 << 3), +	CY_HST_MODE        = (7 << 4), +	CY_HST_OPERATE     = (0 << 4), +	CY_HST_SYSINFO     = (1 << 4), +	CY_HST_CAT         = (2 << 4), +	CY_HST_LOWPOW      = (1 << 2), +	CY_HST_SLEEP       = (1 << 1), +	CY_HST_RESET       = (1 << 0), +}; + +/* abs settings */ +#define CY_IGNORE_VALUE			0xFFFF + +/* abs signal capabilities offsets in the frameworks array */ +enum cyttsp4_sig_caps { +	CY_SIGNAL_OST, +	CY_MIN_OST, +	CY_MAX_OST, +	CY_FUZZ_OST, +	CY_FLAT_OST, +	CY_NUM_ABS_SET	/* number of signal capability fields */ +}; + +/* abs axis signal offsets in the framworks array  */ +enum cyttsp4_sig_ost { +	CY_ABS_X_OST, +	CY_ABS_Y_OST, +	CY_ABS_P_OST, +	CY_ABS_W_OST, +	CY_ABS_ID_OST, +	CY_ABS_MAJ_OST, +	CY_ABS_MIN_OST, +	CY_ABS_OR_OST, +	CY_NUM_ABS_OST	/* number of abs signals */ +}; + +enum cyttsp4_flags { +	CY_FLAG_NONE = 0x00, +	CY_FLAG_HOVER = 0x04, +	CY_FLAG_FLIP = 0x08, +	CY_FLAG_INV_X = 0x10, +	CY_FLAG_INV_Y = 0x20, +	CY_FLAG_VKEYS = 0x40, +}; + +enum cyttsp4_object_id { +	CY_OBJ_STANDARD_FINGER, +	CY_OBJ_LARGE_OBJECT, +	CY_OBJ_STYLUS, +	CY_OBJ_HOVER, +}; + +enum cyttsp4_event_id { +	CY_EV_NO_EVENT, +	CY_EV_TOUCHDOWN, +	CY_EV_MOVE,		/* significant displacement (> act dist) */ +	CY_EV_LIFTOFF,		/* record reports last position */ +}; + +/* x-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_X_MASK	0x7F + +/* y-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_Y_MASK	0x7F + +/* x-axis, 0:origin is on left side of panel, 1: right */ +#define CY_PCFG_ORIGIN_X_MASK		0x80 + +/* y-axis, 0:origin is on top side of panel, 1: bottom */ +#define CY_PCFG_ORIGIN_Y_MASK		0x80 + +static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u16 addr, int size, +		void *buf) +{ +	return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf); +} + +static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u16 addr, int size, +		const void *buf) +{ +	return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf); +} + +extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, +		struct device *dev, u16 irq, size_t xfer_buf_size); +extern int cyttsp4_remove(struct cyttsp4 *ts); +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr, +		u8 length, const void *values); +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr, +		u8 length, void *values); +extern const struct dev_pm_ops cyttsp4_pm_ops; + +#endif /* _LINUX_CYTTSP4_CORE_H */ diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c new file mode 100644 index 00000000000..8e2012c7905 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_i2c.c @@ -0,0 +1,90 @@ +/* + * cyttsp_i2c.c + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress  Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * Copyright (C) 2013 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp4_core.h" + +#include <linux/i2c.h> +#include <linux/input.h> + +#define CYTTSP4_I2C_DATA_SIZE	(3 * 256) + +static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = { +	.bustype	= BUS_I2C, +	.write		= cyttsp_i2c_write_block_data, +	.read           = cyttsp_i2c_read_block_data, +}; + +static int cyttsp4_i2c_probe(struct i2c_client *client, +				      const struct i2c_device_id *id) +{ +	struct cyttsp4 *ts; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		dev_err(&client->dev, "I2C functionality not Supported\n"); +		return -EIO; +	} + +	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; +} + +static int cyttsp4_i2c_remove(struct i2c_client *client) +{ +	struct cyttsp4 *ts = i2c_get_clientdata(client); + +	cyttsp4_remove(ts); + +	return 0; +} + +static const struct i2c_device_id cyttsp4_i2c_id[] = { +	{ CYTTSP4_I2C_NAME, 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id); + +static struct i2c_driver cyttsp4_i2c_driver = { +	.driver = { +		.name	= CYTTSP4_I2C_NAME, +		.owner	= THIS_MODULE, +		.pm	= &cyttsp4_pm_ops, +	}, +	.probe		= cyttsp4_i2c_probe, +	.remove		= cyttsp4_i2c_remove, +	.id_table	= cyttsp4_i2c_id, +}; + +module_i2c_driver(cyttsp4_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp4"); diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c new file mode 100644 index 00000000000..b19434cebbf --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_spi.c @@ -0,0 +1,200 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * Copyright (C) 2013 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp4_core.h" + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/spi/spi.h> + +#define CY_SPI_WR_OP		0x00 /* r/~w */ +#define CY_SPI_RD_OP		0x01 +#define CY_SPI_BITS_PER_WORD	8 +#define CY_SPI_A8_BIT		0x02 +#define CY_SPI_WR_HEADER_BYTES	2 +#define CY_SPI_RD_HEADER_BYTES	1 +#define CY_SPI_CMD_BYTES	2 +#define CY_SPI_SYNC_BYTE	0 +#define CY_SPI_SYNC_ACK		0x62 /* from TRM *A protocol */ +#define CY_SPI_DATA_SIZE	(2 * 256) + +#define CY_SPI_DATA_BUF_SIZE	(CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) + +static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, +			   u8 op, u16 reg, u8 *buf, int length) +{ +	struct spi_device *spi = to_spi_device(dev); +	struct spi_message msg; +	struct spi_transfer xfer[2]; +	u8 *wr_buf = &xfer_buf[0]; +	u8 rd_buf[CY_SPI_CMD_BYTES]; +	int retval; +	int i; + +	if (length > CY_SPI_DATA_SIZE) { +		dev_err(dev, "%s: length %d is too big.\n", +			__func__, length); +		return -EINVAL; +	} + +	memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); +	memset(rd_buf, 0, CY_SPI_CMD_BYTES); + +	wr_buf[0] = op + (((reg >> 8) & 0x1) ? CY_SPI_A8_BIT : 0); +	if (op == CY_SPI_WR_OP) { +		wr_buf[1] = reg & 0xFF; +		if (length > 0) +			memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); +	} + +	memset(xfer, 0, sizeof(xfer)); +	spi_message_init(&msg); + +	/* +	  We set both TX and RX buffers because Cypress TTSP +	  requires full duplex operation. +	*/ +	xfer[0].tx_buf = wr_buf; +	xfer[0].rx_buf = rd_buf; +	switch (op) { +	case CY_SPI_WR_OP: +		xfer[0].len = length + CY_SPI_CMD_BYTES; +		spi_message_add_tail(&xfer[0], &msg); +		break; + +	case CY_SPI_RD_OP: +		xfer[0].len = CY_SPI_RD_HEADER_BYTES; +		spi_message_add_tail(&xfer[0], &msg); + +		xfer[1].rx_buf = buf; +		xfer[1].len = length; +		spi_message_add_tail(&xfer[1], &msg); +		break; + +	default: +		dev_err(dev, "%s: bad operation code=%d\n", __func__, op); +		return -EINVAL; +	} + +	retval = spi_sync(spi, &msg); +	if (retval < 0) { +		dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n", +			__func__, retval, xfer[1].len, op); + +		/* +		 * do not return here since was a bad ACK sequence +		 * let the following ACK check handle any errors and +		 * allow silent retries +		 */ +	} + +	if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) { +		dev_dbg(dev, "%s: operation %d failed\n", __func__, op); + +		for (i = 0; i < CY_SPI_CMD_BYTES; i++) +			dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n", +				__func__, i, rd_buf[i]); +		for (i = 0; i < length; i++) +			dev_dbg(dev, "%s: test buf[%d]:0x%02x\n", +				__func__, i, buf[i]); + +		return -EIO; +	} + +	return 0; +} + +static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, +				      u16 addr, u8 length, void *data) +{ +	int rc; + +	rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0); +	if (rc) +		return rc; +	else +		return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data, +				length); +} + +static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, +				       u16 addr, u8 length, const void *data) +{ +	return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data, +			length); +} + +static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = { +	.bustype	= BUS_SPI, +	.write		= cyttsp_spi_write_block_data, +	.read		= cyttsp_spi_read_block_data, +}; + +static int cyttsp4_spi_probe(struct spi_device *spi) +{ +	struct cyttsp4 *ts; +	int error; + +	/* Set up SPI*/ +	spi->bits_per_word = CY_SPI_BITS_PER_WORD; +	spi->mode = SPI_MODE_0; +	error = spi_setup(spi); +	if (error < 0) { +		dev_err(&spi->dev, "%s: SPI setup error %d\n", +			__func__, error); +		return error; +	} + +	ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, +			  CY_SPI_DATA_BUF_SIZE); + +	return PTR_ERR_OR_ZERO(ts); +} + +static int cyttsp4_spi_remove(struct spi_device *spi) +{ +	struct cyttsp4 *ts = spi_get_drvdata(spi); +	cyttsp4_remove(ts); + +	return 0; +} + +static struct spi_driver cyttsp4_spi_driver = { +	.driver = { +		.name	= CYTTSP4_SPI_NAME, +		.owner	= THIS_MODULE, +		.pm	= &cyttsp4_pm_ops, +	}, +	.probe  = cyttsp4_spi_probe, +	.remove = cyttsp4_spi_remove, +}; + +module_spi_driver(cyttsp4_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp4"); diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100644 index 00000000000..eee656f77a2 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,641 @@ +/* + * Core Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +#include "cyttsp_core.h" + +/* Bootloader number of command keys */ +#define CY_NUM_BL_KEYS		8 + +/* helpers */ +#define GET_NUM_TOUCHES(x)		((x) & 0x0F) +#define IS_LARGE_AREA(x)		(((x) & 0x10) >> 4) +#define IS_BAD_PKT(x)			((x) & 0x20) +#define IS_VALID_APP(x)			((x) & 0x01) +#define IS_OPERATIONAL_ERR(x)		((x) & 0x3F) +#define GET_HSTMODE(reg)		(((reg) & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg)		(((reg) & 0x10) >> 4) + +#define CY_REG_BASE			0x00 +#define CY_REG_ACT_DIST			0x1E +#define CY_REG_ACT_INTRVL		0x1D +#define CY_REG_TCH_TMOUT		(CY_REG_ACT_INTRVL + 1) +#define CY_REG_LP_INTRVL		(CY_REG_TCH_TMOUT + 1) +#define CY_MAXZ				255 +#define CY_DELAY_DFLT			20 /* ms */ +#define CY_DELAY_MAX			500 +#define CY_ACT_DIST_DFLT		0xF8 +#define CY_HNDSHK_BIT			0x80 +/* device mode bits */ +#define CY_OPERATE_MODE			0x00 +#define CY_SYSINFO_MODE			0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE		0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE		0x02 +#define CY_LOW_POWER_MODE		0x04 + +/* Slots management */ +#define CY_MAX_FINGER			4 +#define CY_MAX_ID			16 + +static const u8 bl_command[] = { +	0x00,			/* file offset */ +	0xFF,			/* command */ +	0xA5,			/* exit bootloader command */ +	0, 1, 2, 3, 4, 5, 6, 7	/* default keys */ +}; + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, +				u8 length, void *buf) +{ +	int error; +	int tries; + +	for (tries = 0; tries < CY_NUM_RETRY; tries++) { +		error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command, +				length, buf); +		if (!error) +			return 0; + +		msleep(CY_DELAY_DFLT); +	} + +	return -EIO; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, +				 u8 length, void *buf) +{ +	int error; +	int tries; + +	for (tries = 0; tries < CY_NUM_RETRY; tries++) { +		error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command, +				length, buf); +		if (!error) +			return 0; + +		msleep(CY_DELAY_DFLT); +	} + +	return -EIO; +} + +static int ttsp_send_command(struct cyttsp *ts, u8 cmd) +{ +	return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); +} + +static int cyttsp_handshake(struct cyttsp *ts) +{ +	if (ts->pdata->use_hndshk) +		return ttsp_send_command(ts, +				ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); + +	return 0; +} + +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ +	memset(&ts->bl_data, 0, sizeof(ts->bl_data)); +	ts->bl_data.bl_status = 0x10; + +	return ttsp_read_block_data(ts, CY_REG_BASE, +				    sizeof(ts->bl_data), &ts->bl_data); +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ +	int error; +	u8 bl_cmd[sizeof(bl_command)]; + +	memcpy(bl_cmd, bl_command, sizeof(bl_command)); +	if (ts->pdata->bl_keys) +		memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], +			ts->pdata->bl_keys, CY_NUM_BL_KEYS); + +	error = ttsp_write_block_data(ts, CY_REG_BASE, +				      sizeof(bl_cmd), bl_cmd); +	if (error) +		return error; + +	/* wait for TTSP Device to complete the operation */ +	msleep(CY_DELAY_DFLT); + +	error = cyttsp_load_bl_regs(ts); +	if (error) +		return error; + +	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) +		return -EIO; + +	return 0; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ +	int error; + +	error = ttsp_send_command(ts, CY_OPERATE_MODE); +	if (error) +		return error; + +	/* wait for TTSP Device to complete switch to Operational mode */ +	error = ttsp_read_block_data(ts, CY_REG_BASE, +				     sizeof(ts->xy_data), &ts->xy_data); +	if (error) +		return error; + +	error = cyttsp_handshake(ts); +	if (error) +		return error; + +	return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ +	int error; + +	memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); + +	/* switch to sysinfo mode */ +	error = ttsp_send_command(ts, CY_SYSINFO_MODE); +	if (error) +		return error; + +	/* read sysinfo registers */ +	msleep(CY_DELAY_DFLT); +	error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), +				      &ts->sysinfo_data); +	if (error) +		return error; + +	error = cyttsp_handshake(ts); +	if (error) +		return error; + +	if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) +		return -EIO; + +	return 0; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ +	int retval = 0; + +	if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || +	    ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || +	    ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + +		u8 intrvl_ray[] = { +			ts->pdata->act_intrvl, +			ts->pdata->tch_tmout, +			ts->pdata->lp_intrvl +		}; + +		/* set intrvl registers */ +		retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, +					sizeof(intrvl_ray), intrvl_ray); +		msleep(CY_DELAY_DFLT); +	} + +	return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ +	unsigned long timeout; +	int retval; + +	/* wait for interrupt to set ready completion */ +	reinit_completion(&ts->bl_ready); +	ts->state = CY_BL_STATE; + +	enable_irq(ts->irq); + +	retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); +	if (retval) +		goto out; + +	timeout = wait_for_completion_timeout(&ts->bl_ready, +			msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); +	retval = timeout ? 0 : -EIO; + +out: +	ts->state = CY_IDLE_STATE; +	disable_irq(ts->irq); +	return retval; +} + +static int cyttsp_act_dist_setup(struct cyttsp *ts) +{ +	u8 act_dist_setup = ts->pdata->act_dist; + +	/* Init gesture; active distance setup */ +	return ttsp_write_block_data(ts, CY_REG_ACT_DIST, +				sizeof(act_dist_setup), &act_dist_setup); +} + +static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) +{ +	ids[0] = xy_data->touch12_id >> 4; +	ids[1] = xy_data->touch12_id & 0xF; +	ids[2] = xy_data->touch34_id >> 4; +	ids[3] = xy_data->touch34_id & 0xF; +} + +static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, +					       int idx) +{ +	switch (idx) { +	case 0: +		return &xy_data->tch1; +	case 1: +		return &xy_data->tch2; +	case 2: +		return &xy_data->tch3; +	case 3: +		return &xy_data->tch4; +	default: +		return NULL; +	} +} + +static void cyttsp_report_tchdata(struct cyttsp *ts) +{ +	struct cyttsp_xydata *xy_data = &ts->xy_data; +	struct input_dev *input = ts->input; +	int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); +	const struct cyttsp_tch *tch; +	int ids[CY_MAX_ID]; +	int i; +	DECLARE_BITMAP(used, CY_MAX_ID); + +	if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { +		/* terminate all active tracks */ +		num_tch = 0; +		dev_dbg(ts->dev, "%s: Large area detected\n", __func__); +	} else if (num_tch > CY_MAX_FINGER) { +		/* terminate all active tracks */ +		num_tch = 0; +		dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); +	} else if (IS_BAD_PKT(xy_data->tt_mode)) { +		/* terminate all active tracks */ +		num_tch = 0; +		dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); +	} + +	cyttsp_extract_track_ids(xy_data, ids); + +	bitmap_zero(used, CY_MAX_ID); + +	for (i = 0; i < num_tch; i++) { +		tch = cyttsp_get_tch(xy_data, i); + +		input_mt_slot(input, ids[i]); +		input_mt_report_slot_state(input, MT_TOOL_FINGER, true); +		input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); +		input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); +		input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); + +		__set_bit(ids[i], used); +	} + +	for (i = 0; i < CY_MAX_ID; i++) { +		if (test_bit(i, used)) +			continue; + +		input_mt_slot(input, i); +		input_mt_report_slot_state(input, MT_TOOL_FINGER, false); +	} + +	input_sync(input); +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ +	struct cyttsp *ts = handle; +	int error; + +	if (unlikely(ts->state == CY_BL_STATE)) { +		complete(&ts->bl_ready); +		goto out; +	} + +	/* Get touch data from CYTTSP device */ +	error = ttsp_read_block_data(ts, CY_REG_BASE, +				 sizeof(struct cyttsp_xydata), &ts->xy_data); +	if (error) +		goto out; + +	/* provide flow control handshake */ +	error = cyttsp_handshake(ts); +	if (error) +		goto out; + +	if (unlikely(ts->state == CY_IDLE_STATE)) +		goto out; + +	if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { +		/* +		 * TTSP device has reset back to bootloader mode. +		 * Restore to operational mode. +		 */ +		error = cyttsp_exit_bl_mode(ts); +		if (error) { +			dev_err(ts->dev, +				"Could not return to operational mode, err: %d\n", +				error); +			ts->state = CY_IDLE_STATE; +		} +	} else { +		cyttsp_report_tchdata(ts); +	} + +out: +	return IRQ_HANDLED; +} + +static int cyttsp_power_on(struct cyttsp *ts) +{ +	int error; + +	error = cyttsp_soft_reset(ts); +	if (error) +		return error; + +	error = cyttsp_load_bl_regs(ts); +	if (error) +		return error; + +	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && +	    IS_VALID_APP(ts->bl_data.bl_status)) { +		error = cyttsp_exit_bl_mode(ts); +		if (error) +			return error; +	} + +	if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || +	    IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { +		return -ENODEV; +	} + +	error = cyttsp_set_sysinfo_mode(ts); +	if (error) +		return error; + +	error = cyttsp_set_sysinfo_regs(ts); +	if (error) +		return error; + +	error = cyttsp_set_operational_mode(ts); +	if (error) +		return error; + +	/* init active distance */ +	error = cyttsp_act_dist_setup(ts); +	if (error) +		return error; + +	ts->state = CY_ACTIVE_STATE; + +	return 0; +} + +static int cyttsp_enable(struct cyttsp *ts) +{ +	int error; + +	/* +	 * The device firmware can wake on an I2C or SPI memory slave +	 * address match. So just reading a register is sufficient to +	 * wake up the device. The first read attempt will fail but it +	 * will wake it up making the second read attempt successful. +	 */ +	error = ttsp_read_block_data(ts, CY_REG_BASE, +				     sizeof(ts->xy_data), &ts->xy_data); +	if (error) +		return error; + +	if (GET_HSTMODE(ts->xy_data.hst_mode)) +		return -EIO; + +	enable_irq(ts->irq); + +	return 0; +} + +static int cyttsp_disable(struct cyttsp *ts) +{ +	int error; + +	error = ttsp_send_command(ts, CY_LOW_POWER_MODE); +	if (error) +		return error; + +	disable_irq(ts->irq); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyttsp_suspend(struct device *dev) +{ +	struct cyttsp *ts = dev_get_drvdata(dev); +	int retval = 0; + +	mutex_lock(&ts->input->mutex); + +	if (ts->input->users) { +		retval = cyttsp_disable(ts); +		if (retval == 0) +			ts->suspended = true; +	} + +	mutex_unlock(&ts->input->mutex); + +	return retval; +} + +static int cyttsp_resume(struct device *dev) +{ +	struct cyttsp *ts = dev_get_drvdata(dev); + +	mutex_lock(&ts->input->mutex); + +	if (ts->input->users) +		cyttsp_enable(ts); + +	ts->suspended = false; + +	mutex_unlock(&ts->input->mutex); + +	return 0; +} + +#endif + +SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); +EXPORT_SYMBOL_GPL(cyttsp_pm_ops); + +static int cyttsp_open(struct input_dev *dev) +{ +	struct cyttsp *ts = input_get_drvdata(dev); +	int retval = 0; + +	if (!ts->suspended) +		retval = cyttsp_enable(ts); + +	return retval; +} + +static void cyttsp_close(struct input_dev *dev) +{ +	struct cyttsp *ts = input_get_drvdata(dev); + +	if (!ts->suspended) +		cyttsp_disable(ts); +} + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, +			    struct device *dev, int irq, size_t xfer_buf_size) +{ +	const struct cyttsp_platform_data *pdata = dev_get_platdata(dev); +	struct cyttsp *ts; +	struct input_dev *input_dev; +	int error; + +	if (!pdata || !pdata->name || irq <= 0) { +		error = -EINVAL; +		goto err_out; +	} + +	ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!ts || !input_dev) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	ts->dev = dev; +	ts->input = input_dev; +	ts->pdata = dev_get_platdata(dev); +	ts->bus_ops = bus_ops; +	ts->irq = irq; + +	init_completion(&ts->bl_ready); +	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + +	if (pdata->init) { +		error = pdata->init(); +		if (error) { +			dev_err(ts->dev, "platform init failed, err: %d\n", +				error); +			goto err_free_mem; +		} +	} + +	input_dev->name = pdata->name; +	input_dev->phys = ts->phys; +	input_dev->id.bustype = bus_ops->bustype; +	input_dev->dev.parent = ts->dev; + +	input_dev->open = cyttsp_open; +	input_dev->close = cyttsp_close; + +	input_set_drvdata(input_dev, ts); + +	__set_bit(EV_ABS, input_dev->evbit); +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, +			     0, pdata->maxx, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, +			     0, pdata->maxy, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, +			     0, CY_MAXZ, 0, 0); + +	input_mt_init_slots(input_dev, CY_MAX_ID, 0); + +	error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, +				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +				     pdata->name, ts); +	if (error) { +		dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", +			ts->irq, error); +		goto err_platform_exit; +	} + +	disable_irq(ts->irq); + +	error = cyttsp_power_on(ts); +	if (error) +		goto err_free_irq; + +	error = input_register_device(input_dev); +	if (error) { +		dev_err(ts->dev, "failed to register input device: %d\n", +			error); +		goto err_free_irq; +	} + +	return ts; + +err_free_irq: +	free_irq(ts->irq, ts); +err_platform_exit: +	if (pdata->exit) +		pdata->exit(); +err_free_mem: +	input_free_device(input_dev); +	kfree(ts); +err_out: +	return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(cyttsp_probe); + +void cyttsp_remove(struct cyttsp *ts) +{ +	free_irq(ts->irq, ts); +	input_unregister_device(ts->input); +	if (ts->pdata->exit) +		ts->pdata->exit(); +	kfree(ts); +} +EXPORT_SYMBOL_GPL(cyttsp_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h new file mode 100644 index 00000000000..07074110a90 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,154 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/input/cyttsp.h> + +#define CY_NUM_RETRY		16 /* max number of retries for read ops */ + +struct cyttsp_tch { +	__be16 x, y; +	u8 z; +} __packed; + +/* TrueTouch Standard Product Gen3 interface definition */ +struct cyttsp_xydata { +	u8 hst_mode; +	u8 tt_mode; +	u8 tt_stat; +	struct cyttsp_tch tch1; +	u8 touch12_id; +	struct cyttsp_tch tch2; +	u8 gest_cnt; +	u8 gest_id; +	struct cyttsp_tch tch3; +	u8 touch34_id; +	struct cyttsp_tch tch4; +	u8 tt_undef[3]; +	u8 act_dist; +	u8 tt_reserved; +} __packed; + + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { +	u8 hst_mode; +	u8 mfg_stat; +	u8 mfg_cmd; +	u8 cid[3]; +	u8 tt_undef1; +	u8 uid[8]; +	u8 bl_verh; +	u8 bl_verl; +	u8 tts_verh; +	u8 tts_verl; +	u8 app_idh; +	u8 app_idl; +	u8 app_verh; +	u8 app_verl; +	u8 tt_undef[5]; +	u8 scn_typ; +	u8 act_intrvl; +	u8 tch_tmout; +	u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { +	u8 bl_file; +	u8 bl_status; +	u8 bl_error; +	u8 blver_hi; +	u8 blver_lo; +	u8 bld_blver_hi; +	u8 bld_blver_lo; +	u8 ttspver_hi; +	u8 ttspver_lo; +	u8 appid_hi; +	u8 appid_lo; +	u8 appver_hi; +	u8 appver_lo; +	u8 cid_0; +	u8 cid_1; +	u8 cid_2; +}; + +struct cyttsp; + +struct cyttsp_bus_ops { +	u16 bustype; +	int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, +			const void *values); +	int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, +			void *values); +}; + +enum cyttsp_state { +	CY_IDLE_STATE, +	CY_ACTIVE_STATE, +	CY_BL_STATE, +}; + +struct cyttsp { +	struct device *dev; +	int irq; +	struct input_dev *input; +	char phys[32]; +	const struct cyttsp_platform_data *pdata; +	const struct cyttsp_bus_ops *bus_ops; +	struct cyttsp_bootloader_data bl_data; +	struct cyttsp_sysinfo_data sysinfo_data; +	struct cyttsp_xydata xy_data; +	struct completion bl_ready; +	enum cyttsp_state state; +	bool suspended; + +	u8 xfer_buf[] ____cacheline_aligned; +}; + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, +			    struct device *dev, int irq, size_t xfer_buf_size); +void cyttsp_remove(struct cyttsp *ts); + +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr, +		u8 length, const void *values); +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr, +		u8 length, void *values); +extern const struct dev_pm_ops cyttsp_pm_ops; + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c new file mode 100644 index 00000000000..63104a86a9b --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -0,0 +1,90 @@ +/* + * cyttsp_i2c.c + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp_core.h" + +#include <linux/i2c.h> +#include <linux/input.h> + +#define CY_I2C_DATA_SIZE	128 + +static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { +	.bustype	= BUS_I2C, +	.write		= cyttsp_i2c_write_block_data, +	.read           = cyttsp_i2c_read_block_data, +}; + +static int cyttsp_i2c_probe(struct i2c_client *client, +				      const struct i2c_device_id *id) +{ +	struct cyttsp *ts; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		dev_err(&client->dev, "I2C functionality not Supported\n"); +		return -EIO; +	} + +	ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq, +			  CY_I2C_DATA_SIZE); + +	if (IS_ERR(ts)) +		return PTR_ERR(ts); + +	i2c_set_clientdata(client, ts); +	return 0; +} + +static int cyttsp_i2c_remove(struct i2c_client *client) +{ +	struct cyttsp *ts = i2c_get_clientdata(client); + +	cyttsp_remove(ts); + +	return 0; +} + +static const struct i2c_device_id cyttsp_i2c_id[] = { +	{ CY_I2C_NAME, 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); + +static struct i2c_driver cyttsp_i2c_driver = { +	.driver = { +		.name	= CY_I2C_NAME, +		.owner	= THIS_MODULE, +		.pm	= &cyttsp_pm_ops, +	}, +	.probe		= cyttsp_i2c_probe, +	.remove		= cyttsp_i2c_remove, +	.id_table	= cyttsp_i2c_id, +}; + +module_i2c_driver(cyttsp_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp"); diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c new file mode 100644 index 00000000000..ccefa56ca21 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c_common.c @@ -0,0 +1,95 @@ +/* + * cyttsp_i2c_common.c + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx and Txx4xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include <linux/device.h> +#include <linux/export.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> + +#include "cyttsp4_core.h" + +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, +				      u16 addr, u8 length, void *values) +{ +	struct i2c_client *client = to_i2c_client(dev); +	u8 client_addr = client->addr | ((addr >> 8) & 0x1); +	u8 addr_lo = addr & 0xFF; +	struct i2c_msg msgs[] = { +		{ +			.addr = client_addr, +			.flags = 0, +			.len = 1, +			.buf = &addr_lo, +		}, +		{ +			.addr = client_addr, +			.flags = I2C_M_RD, +			.len = length, +			.buf = values, +		}, +	}; +	int retval; + +	retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +	if (retval < 0) +		return retval; + +	return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(cyttsp_i2c_read_block_data); + +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, +				       u16 addr, u8 length, const void *values) +{ +	struct i2c_client *client = to_i2c_client(dev); +	u8 client_addr = client->addr | ((addr >> 8) & 0x1); +	u8 addr_lo = addr & 0xFF; +	struct i2c_msg msgs[] = { +		{ +			.addr = client_addr, +			.flags = 0, +			.len = length + 1, +			.buf = xfer_buf, +		}, +	}; +	int retval; + +	xfer_buf[0] = addr_lo; +	memcpy(&xfer_buf[1], values, length); + +	retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +	if (retval < 0) +		return retval; + +	return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(cyttsp_i2c_write_block_data); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c new file mode 100644 index 00000000000..4728bcb1916 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,197 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * Copyright (C) 2013 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com> + * + */ + +#include "cyttsp_core.h" + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/spi/spi.h> + +#define CY_SPI_WR_OP		0x00 /* r/~w */ +#define CY_SPI_RD_OP		0x01 +#define CY_SPI_CMD_BYTES	4 +#define CY_SPI_SYNC_BYTE	2 +#define CY_SPI_SYNC_ACK1	0x62 /* from protocol v.2 */ +#define CY_SPI_SYNC_ACK2	0x9D /* from protocol v.2 */ +#define CY_SPI_DATA_SIZE	128 +#define CY_SPI_DATA_BUF_SIZE	(CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD	8 + +static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, +			   u8 op, u16 reg, u8 *buf, int length) +{ +	struct spi_device *spi = to_spi_device(dev); +	struct spi_message msg; +	struct spi_transfer xfer[2]; +	u8 *wr_buf = &xfer_buf[0]; +	u8 *rd_buf = &xfer_buf[CY_SPI_DATA_BUF_SIZE]; +	int retval; +	int i; + +	if (length > CY_SPI_DATA_SIZE) { +		dev_err(dev, "%s: length %d is too big.\n", +			__func__, length); +		return -EINVAL; +	} + +	memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); +	memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + +	wr_buf[0] = 0x00; /* header byte 0 */ +	wr_buf[1] = 0xFF; /* header byte 1 */ +	wr_buf[2] = reg;  /* reg index */ +	wr_buf[3] = op;   /* r/~w */ +	if (op == CY_SPI_WR_OP) +		memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + +	memset(xfer, 0, sizeof(xfer)); +	spi_message_init(&msg); + +	/* +	  We set both TX and RX buffers because Cypress TTSP +	  requires full duplex operation. +	*/ +	xfer[0].tx_buf = wr_buf; +	xfer[0].rx_buf = rd_buf; +	switch (op) { +	case CY_SPI_WR_OP: +		xfer[0].len = length + CY_SPI_CMD_BYTES; +		spi_message_add_tail(&xfer[0], &msg); +		break; + +	case CY_SPI_RD_OP: +		xfer[0].len = CY_SPI_CMD_BYTES; +		spi_message_add_tail(&xfer[0], &msg); + +		xfer[1].rx_buf = buf; +		xfer[1].len = length; +		spi_message_add_tail(&xfer[1], &msg); +		break; + +	default: +		dev_err(dev, "%s: bad operation code=%d\n", __func__, op); +		return -EINVAL; +	} + +	retval = spi_sync(spi, &msg); +	if (retval < 0) { +		dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n", +			__func__, retval, xfer[1].len, op); + +		/* +		 * do not return here since was a bad ACK sequence +		 * let the following ACK check handle any errors and +		 * allow silent retries +		 */ +	} + +	if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || +	    rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { +		dev_dbg(dev, "%s: operation %d failed\n", __func__, op); + +		for (i = 0; i < CY_SPI_CMD_BYTES; i++) +			dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n", +				__func__, i, rd_buf[i]); +		for (i = 0; i < length; i++) +			dev_dbg(dev, "%s: test buf[%d]:0x%02x\n", +				__func__, i, buf[i]); + +		return -EIO; +	} + +	return 0; +} + +static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, +				      u16 addr, u8 length, void *data) +{ +	return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data, +			length); +} + +static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, +				       u16 addr, u8 length, const void *data) +{ +	return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data, +			length); +} + +static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { +	.bustype	= BUS_SPI, +	.write		= cyttsp_spi_write_block_data, +	.read		= cyttsp_spi_read_block_data, +}; + +static int cyttsp_spi_probe(struct spi_device *spi) +{ +	struct cyttsp *ts; +	int error; + +	/* Set up SPI*/ +	spi->bits_per_word = CY_SPI_BITS_PER_WORD; +	spi->mode = SPI_MODE_0; +	error = spi_setup(spi); +	if (error < 0) { +		dev_err(&spi->dev, "%s: SPI setup error %d\n", +			__func__, error); +		return error; +	} + +	ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, +			  CY_SPI_DATA_BUF_SIZE * 2); +	if (IS_ERR(ts)) +		return PTR_ERR(ts); + +	spi_set_drvdata(spi, ts); + +	return 0; +} + +static int cyttsp_spi_remove(struct spi_device *spi) +{ +	struct cyttsp *ts = spi_get_drvdata(spi); + +	cyttsp_remove(ts); + +	return 0; +} + +static struct spi_driver cyttsp_spi_driver = { +	.driver = { +		.name	= CY_SPI_NAME, +		.owner	= THIS_MODULE, +		.pm	= &cyttsp_pm_ops, +	}, +	.probe  = cyttsp_spi_probe, +	.remove = cyttsp_spi_remove, +}; + +module_spi_driver(cyttsp_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp"); diff --git a/drivers/input/touchscreen/da9034-ts.c b/drivers/input/touchscreen/da9034-ts.c index 2b72a5923c1..cf6f4b31db4 100644 --- a/drivers/input/touchscreen/da9034-ts.c +++ b/drivers/input/touchscreen/da9034-ts.c @@ -13,7 +13,6 @@  #include <linux/module.h>  #include <linux/kernel.h> -#include <linux/init.h>  #include <linux/delay.h>  #include <linux/platform_device.h>  #include <linux/input.h> @@ -297,15 +296,16 @@ static void da9034_touch_close(struct input_dev *dev)  } -static int __devinit da9034_touch_probe(struct platform_device *pdev) +static int da9034_touch_probe(struct platform_device *pdev)  { -	struct da9034_touch_pdata *pdata = pdev->dev.platform_data; +	struct da9034_touch_pdata *pdata = dev_get_platdata(&pdev->dev);  	struct da9034_touch *touch;  	struct input_dev *input_dev; -	int ret; +	int error; -	touch = kzalloc(sizeof(struct da9034_touch), GFP_KERNEL); -	if (touch == NULL) { +	touch = devm_kzalloc(&pdev->dev, sizeof(struct da9034_touch), +			     GFP_KERNEL); +	if (!touch) {  		dev_err(&pdev->dev, "failed to allocate driver data\n");  		return -ENOMEM;  	} @@ -316,18 +316,18 @@ static int __devinit da9034_touch_probe(struct platform_device *pdev)  		touch->interval_ms	= pdata->interval_ms;  		touch->x_inverted	= pdata->x_inverted;  		touch->y_inverted	= pdata->y_inverted; -	} else +	} else {  		/* fallback into default */  		touch->interval_ms	= 10; +	}  	INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);  	touch->notifier.notifier_call = da9034_touch_notifier; -	input_dev = input_allocate_device(); +	input_dev = devm_input_allocate_device(&pdev->dev);  	if (!input_dev) {  		dev_err(&pdev->dev, "failed to allocate input device\n"); -		ret = -ENOMEM; -		goto err_free_touch; +		return -ENOMEM;  	}  	input_dev->name		= pdev->name; @@ -347,26 +347,9 @@ static int __devinit da9034_touch_probe(struct platform_device *pdev)  	touch->input_dev = input_dev;  	input_set_drvdata(input_dev, touch); -	ret = input_register_device(input_dev); -	if (ret) -		goto err_free_input; - -	platform_set_drvdata(pdev, touch); -	return 0; - -err_free_input: -	input_free_device(input_dev); -err_free_touch: -	kfree(touch); -	return ret; -} - -static int __devexit da9034_touch_remove(struct platform_device *pdev) -{ -	struct da9034_touch *touch = platform_get_drvdata(pdev); - -	input_unregister_device(touch->input_dev); -	kfree(touch); +	error = input_register_device(input_dev); +	if (error) +		return error;  	return 0;  } @@ -377,20 +360,8 @@ static struct platform_driver da9034_touch_driver = {  		.owner	= THIS_MODULE,  	},  	.probe		= da9034_touch_probe, -	.remove		= __devexit_p(da9034_touch_remove),  }; - -static int __init da9034_touch_init(void) -{ -	return platform_driver_register(&da9034_touch_driver); -} -module_init(da9034_touch_init); - -static void __exit da9034_touch_exit(void) -{ -	platform_driver_unregister(&da9034_touch_driver); -} -module_exit(da9034_touch_exit); +module_platform_driver(da9034_touch_driver);  MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");  MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>"); diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c new file mode 100644 index 00000000000..ab64d58c3ac --- /dev/null +++ b/drivers/input/touchscreen/da9052_tsi.c @@ -0,0 +1,349 @@ +/* + * TSI driver for Dialog DA9052 + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen <dchen@diasemi.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. + * + */ +#include <linux/module.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> + +#include <linux/mfd/da9052/reg.h> +#include <linux/mfd/da9052/da9052.h> + +#define TSI_PEN_DOWN_STATUS 0x40 + +struct da9052_tsi { +	struct da9052 *da9052; +	struct input_dev *dev; +	struct delayed_work ts_pen_work; +	struct mutex mutex; +	bool stopped; +	bool adc_on; +}; + +static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on) +{ +	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on); +	tsi->adc_on = on; +} + +static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data) +{ +	struct da9052_tsi *tsi = data; + +	if (!tsi->stopped) { +		/* Mask PEN_DOWN event and unmask TSI_READY event */ +		da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN); +		da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); + +		da9052_ts_adc_toggle(tsi, true); + +		schedule_delayed_work(&tsi->ts_pen_work, HZ / 50); +	} + +	return IRQ_HANDLED; +} + +static void da9052_ts_read(struct da9052_tsi *tsi) +{ +	struct input_dev *input = tsi->dev; +	int ret; +	u16 x, y, z; +	u8 v; + +	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG); +	if (ret < 0) +		return; + +	x = (u16) ret; + +	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG); +	if (ret < 0) +		return; + +	y = (u16) ret; + +	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG); +	if (ret < 0) +		return; + +	z = (u16) ret; + +	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG); +	if (ret < 0) +		return; + +	v = (u8) ret; + +	x = ((x << 2) & 0x3fc) | (v & 0x3); +	y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2); +	z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4); + +	input_report_key(input, BTN_TOUCH, 1); +	input_report_abs(input, ABS_X, x); +	input_report_abs(input, ABS_Y, y); +	input_report_abs(input, ABS_PRESSURE, z); +	input_sync(input); +} + +static irqreturn_t da9052_ts_datardy_irq(int irq, void *data) +{ +	struct da9052_tsi *tsi = data; + +	da9052_ts_read(tsi); + +	return IRQ_HANDLED; +} + +static void da9052_ts_pen_work(struct work_struct *work) +{ +	struct da9052_tsi *tsi = container_of(work, struct da9052_tsi, +					      ts_pen_work.work); +	if (!tsi->stopped) { +		int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG); +		if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) { +			/* Pen is still DOWN (or read error) */ +			schedule_delayed_work(&tsi->ts_pen_work, HZ / 50); +		} else { +			struct input_dev *input = tsi->dev; + +			/* Pen UP */ +			da9052_ts_adc_toggle(tsi, false); + +			/* Report Pen UP */ +			input_report_key(input, BTN_TOUCH, 0); +			input_report_abs(input, ABS_PRESSURE, 0); +			input_sync(input); + +			/* +			 * FIXME: Fixes the unhandled irq issue when quick +			 * pen down and pen up events occurs +			 */ +			ret = da9052_reg_update(tsi->da9052, +						DA9052_EVENT_B_REG, 0xC0, 0xC0); +			if (ret < 0) +				return; + +			/* Mask TSI_READY event and unmask PEN_DOWN event */ +			da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); +			da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); +		} +	} +} + +static int da9052_ts_configure_gpio(struct da9052 *da9052) +{ +	int error; + +	error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0); +	if (error < 0) +		return error; + +	error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0); +	if (error < 0) +		return error; + +	error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0); +	if (error < 0) +		return error; + +	return 0; +} + +static int da9052_configure_tsi(struct da9052_tsi *tsi) +{ +	int error; + +	error = da9052_ts_configure_gpio(tsi->da9052); +	if (error) +		return error; + +	/* Measure TSI sample every 1ms */ +	error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG, +				  1 << 6, 1 << 6); +	if (error < 0) +		return error; + +	/* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */ +	error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0); +	if (error < 0) +		return error; + +	/* Supply TSIRef through LD09 */ +	error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59); +	if (error < 0) +		return error; + +	return 0; +} + +static int da9052_ts_input_open(struct input_dev *input_dev) +{ +	struct da9052_tsi *tsi = input_get_drvdata(input_dev); + +	tsi->stopped = false; +	mb(); + +	/* Unmask PEN_DOWN event */ +	da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); + +	/* Enable Pen Detect Circuit */ +	return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, +				 1 << 1, 1 << 1); +} + +static void da9052_ts_input_close(struct input_dev *input_dev) +{ +	struct da9052_tsi *tsi = input_get_drvdata(input_dev); + +	tsi->stopped = true; +	mb(); +	da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); +	cancel_delayed_work_sync(&tsi->ts_pen_work); + +	if (tsi->adc_on) { +		da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); +		da9052_ts_adc_toggle(tsi, false); + +		/* +		 * If ADC was on that means that pendwn IRQ was disabled +		 * twice and we need to enable it to keep enable/disable +		 * counter balanced. IRQ is still off though. +		 */ +		da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); +	} + +	/* Disable Pen Detect Circuit */ +	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0); +} + +static int da9052_ts_probe(struct platform_device *pdev) +{ +	struct da9052 *da9052; +	struct da9052_tsi *tsi; +	struct input_dev *input_dev; +	int error; + +	da9052 = dev_get_drvdata(pdev->dev.parent); +	if (!da9052) +		return -EINVAL; + +	tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!tsi || !input_dev) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	tsi->da9052 = da9052; +	tsi->dev = input_dev; +	tsi->stopped = true; +	INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work); + +	input_dev->id.version = 0x0101; +	input_dev->id.vendor = 0x15B6; +	input_dev->id.product = 0x9052; +	input_dev->name = "Dialog DA9052 TouchScreen Driver"; +	input_dev->dev.parent = &pdev->dev; +	input_dev->open = da9052_ts_input_open; +	input_dev->close = da9052_ts_input_close; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); + +	input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0); +	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0); + +	input_set_drvdata(input_dev, tsi); + +	/* Disable Pen Detect Circuit */ +	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0); + +	/* Disable ADC */ +	da9052_ts_adc_toggle(tsi, false); + +	error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN, +				"pendown-irq", da9052_ts_pendwn_irq, tsi); +	if (error) { +		dev_err(tsi->da9052->dev, +			"Failed to register PENDWN IRQ: %d\n", error); +		goto err_free_mem; +	} + +	error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY, +				"tsiready-irq", da9052_ts_datardy_irq, tsi); +	if (error) { +		dev_err(tsi->da9052->dev, +			"Failed to register TSIRDY IRQ :%d\n", error); +		goto err_free_pendwn_irq; +	} + +	/* Mask PEN_DOWN and TSI_READY events */ +	da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); +	da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); + +	error = da9052_configure_tsi(tsi); +	if (error) +		goto err_free_datardy_irq; + +	error = input_register_device(tsi->dev); +	if (error) +		goto err_free_datardy_irq; + +	platform_set_drvdata(pdev, tsi); + +	return 0; + +err_free_datardy_irq: +	da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi); +err_free_pendwn_irq: +	da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi); +err_free_mem: +	kfree(tsi); +	input_free_device(input_dev); + +	return error; +} + +static int  da9052_ts_remove(struct platform_device *pdev) +{ +	struct da9052_tsi *tsi = platform_get_drvdata(pdev); + +	da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19); + +	da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi); +	da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi); + +	input_unregister_device(tsi->dev); +	kfree(tsi); + +	return 0; +} + +static struct platform_driver da9052_tsi_driver = { +	.probe	= da9052_ts_probe, +	.remove	= da9052_ts_remove, +	.driver	= { +		.name	= "da9052-tsi", +		.owner	= THIS_MODULE, +	}, +}; + +module_platform_driver(da9052_tsi_driver); + +MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052"); +MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-tsi"); diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c index 455353908bd..86237a91087 100644 --- a/drivers/input/touchscreen/dynapro.c +++ b/drivers/input/touchscreen/dynapro.c @@ -24,7 +24,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Dynapro serial touchscreen driver" @@ -188,19 +187,4 @@ static struct serio_driver dynapro_drv = {  	.disconnect	= dynapro_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init dynapro_init(void) -{ -	return serio_register_driver(&dynapro_drv); -} - -static void __exit dynapro_exit(void) -{ -	serio_unregister_driver(&dynapro_drv); -} - -module_init(dynapro_init); -module_exit(dynapro_exit); +module_serio_driver(dynapro_drv); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c new file mode 100644 index 00000000000..d4f33992ad8 --- /dev/null +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -0,0 +1,1154 @@ +/* + * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> + * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support) + * Lothar Waßmann <LW@KARO-electronics.de> (DT support) + * + * 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. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +/* + * This is a driver for the EDT "Polytouch" family of touch controllers + * based on the FocalTech FT5x06 line of chips. + * + * Development of this driver has been sponsored by Glyn: + *    http://www.glyn.com/Products/Displays + */ + +#include <linux/module.h> +#include <linux/ratelimit.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/i2c.h> +#include <linux/uaccess.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/input/mt.h> +#include <linux/input/edt-ft5x06.h> + +#define MAX_SUPPORT_POINTS		5 + +#define WORK_REGISTER_THRESHOLD		0x00 +#define WORK_REGISTER_REPORT_RATE	0x08 +#define WORK_REGISTER_GAIN		0x30 +#define WORK_REGISTER_OFFSET		0x31 +#define WORK_REGISTER_NUM_X		0x33 +#define WORK_REGISTER_NUM_Y		0x34 + +#define M09_REGISTER_THRESHOLD		0x80 +#define M09_REGISTER_GAIN		0x92 +#define M09_REGISTER_OFFSET		0x93 +#define M09_REGISTER_NUM_X		0x94 +#define M09_REGISTER_NUM_Y		0x95 + +#define NO_REGISTER			0xff + +#define WORK_REGISTER_OPMODE		0x3c +#define FACTORY_REGISTER_OPMODE		0x01 + +#define TOUCH_EVENT_DOWN		0x00 +#define TOUCH_EVENT_UP			0x01 +#define TOUCH_EVENT_ON			0x02 +#define TOUCH_EVENT_RESERVED		0x03 + +#define EDT_NAME_LEN			23 +#define EDT_SWITCH_MODE_RETRIES		10 +#define EDT_SWITCH_MODE_DELAY		5 /* msec */ +#define EDT_RAW_DATA_RETRIES		100 +#define EDT_RAW_DATA_DELAY		1 /* msec */ + +enum edt_ver { +	M06, +	M09, +}; + +struct edt_reg_addr { +	int reg_threshold; +	int reg_report_rate; +	int reg_gain; +	int reg_offset; +	int reg_num_x; +	int reg_num_y; +}; + +struct edt_ft5x06_ts_data { +	struct i2c_client *client; +	struct input_dev *input; +	u16 num_x; +	u16 num_y; + +	int reset_pin; +	int irq_pin; +	int wake_pin; + +#if defined(CONFIG_DEBUG_FS) +	struct dentry *debug_dir; +	u8 *raw_buffer; +	size_t raw_bufsize; +#endif + +	struct mutex mutex; +	bool factory_mode; +	int threshold; +	int gain; +	int offset; +	int report_rate; + +	char name[EDT_NAME_LEN]; + +	struct edt_reg_addr reg_addr; +	enum edt_ver version; +}; + +static int edt_ft5x06_ts_readwrite(struct i2c_client *client, +				   u16 wr_len, u8 *wr_buf, +				   u16 rd_len, u8 *rd_buf) +{ +	struct i2c_msg wrmsg[2]; +	int i = 0; +	int ret; + +	if (wr_len) { +		wrmsg[i].addr  = client->addr; +		wrmsg[i].flags = 0; +		wrmsg[i].len = wr_len; +		wrmsg[i].buf = wr_buf; +		i++; +	} +	if (rd_len) { +		wrmsg[i].addr  = client->addr; +		wrmsg[i].flags = I2C_M_RD; +		wrmsg[i].len = rd_len; +		wrmsg[i].buf = rd_buf; +		i++; +	} + +	ret = i2c_transfer(client->adapter, wrmsg, i); +	if (ret < 0) +		return ret; +	if (ret != i) +		return -EIO; + +	return 0; +} + +static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, +				    u8 *buf, int buflen) +{ +	int i; +	u8 crc = 0; + +	for (i = 0; i < buflen - 1; i++) +		crc ^= buf[i]; + +	if (crc != buf[buflen-1]) { +		dev_err_ratelimited(&tsdata->client->dev, +				    "crc error: 0x%02x expected, got 0x%02x\n", +				    crc, buf[buflen-1]); +		return false; +	} + +	return true; +} + +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]; +	int i, type, x, y, id; +	int offset, tplen, datalen; +	int error; + +	switch (tsdata->version) { +	case M06: +		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 */ +		break; + +	case M09: +		cmd = 0x02; +		offset = 1; +		tplen = 6; +		datalen = 29; +		break; + +	default: +		goto out; +	} + +	memset(rdbuf, 0, sizeof(rdbuf)); + +	error = edt_ft5x06_ts_readwrite(tsdata->client, +					sizeof(cmd), &cmd, +					datalen, rdbuf); +	if (error) { +		dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", +				    error); +		goto out; +	} + +	/* M09 does not send header or CRC */ +	if (tsdata->version == M06) { +		if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || +			rdbuf[2] != datalen) { +			dev_err_ratelimited(dev, +					"Unexpected header: %02x%02x%02x!\n", +					rdbuf[0], rdbuf[1], rdbuf[2]); +			goto out; +		} + +		if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) +			goto out; +	} + +	for (i = 0; i < MAX_SUPPORT_POINTS; i++) { +		u8 *buf = &rdbuf[i * tplen + offset]; +		bool down; + +		type = buf[0] >> 6; +		/* ignore Reserved events */ +		if (type == TOUCH_EVENT_RESERVED) +			continue; + +		/* M06 sometimes sends bogus coordinates in TOUCH_DOWN */ +		if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN) +			continue; + +		x = ((buf[0] << 8) | buf[1]) & 0x0fff; +		y = ((buf[2] << 8) | buf[3]) & 0x0fff; +		id = (buf[2] >> 4) & 0x0f; +		down = type != TOUCH_EVENT_UP; + +		input_mt_slot(tsdata->input, id); +		input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); + +		if (!down) +			continue; + +		input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); +		input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); +	} + +	input_mt_report_pointer_emulation(tsdata->input, true); +	input_sync(tsdata->input); + +out: +	return IRQ_HANDLED; +} + +static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, +				     u8 addr, u8 value) +{ +	u8 wrbuf[4]; + +	switch (tsdata->version) { +	case M06: +		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; +		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; +		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; +		wrbuf[2] = value; +		wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; +		return edt_ft5x06_ts_readwrite(tsdata->client, 4, +					wrbuf, 0, NULL); +	case M09: +		wrbuf[0] = addr; +		wrbuf[1] = value; + +		return edt_ft5x06_ts_readwrite(tsdata->client, 2, +					wrbuf, 0, NULL); + +	default: +		return -EINVAL; +	} +} + +static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, +				    u8 addr) +{ +	u8 wrbuf[2], rdbuf[2]; +	int error; + +	switch (tsdata->version) { +	case M06: +		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; +		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; +		wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + +		error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, +						rdbuf); +		if (error) +			return error; + +		if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { +			dev_err(&tsdata->client->dev, +				"crc error: 0x%02x expected, got 0x%02x\n", +				wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], +				rdbuf[1]); +			return -EIO; +		} +		break; + +	case M09: +		wrbuf[0] = addr; +		error = edt_ft5x06_ts_readwrite(tsdata->client, 1, +						wrbuf, 1, rdbuf); +		if (error) +			return error; +		break; + +	default: +		return -EINVAL; +	} + +	return rdbuf[0]; +} + +struct edt_ft5x06_attribute { +	struct device_attribute dattr; +	size_t field_offset; +	u8 limit_low; +	u8 limit_high; +	u8 addr_m06; +	u8 addr_m09; +}; + +#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09,			\ +		_limit_low, _limit_high)				\ +	struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {	\ +		.dattr = __ATTR(_field, _mode,				\ +				edt_ft5x06_setting_show,		\ +				edt_ft5x06_setting_store),		\ +		.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ +		.addr_m06 = _addr_m06,					\ +		.addr_m09 = _addr_m09,					\ +		.limit_low = _limit_low,				\ +		.limit_high = _limit_high,				\ +	} + +static ssize_t edt_ft5x06_setting_show(struct device *dev, +				       struct device_attribute *dattr, +				       char *buf) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); +	struct edt_ft5x06_attribute *attr = +			container_of(dattr, struct edt_ft5x06_attribute, dattr); +	u8 *field = (u8 *)tsdata + attr->field_offset; +	int val; +	size_t count = 0; +	int error = 0; +	u8 addr; + +	mutex_lock(&tsdata->mutex); + +	if (tsdata->factory_mode) { +		error = -EIO; +		goto out; +	} + +	switch (tsdata->version) { +	case M06: +		addr = attr->addr_m06; +		break; + +	case M09: +		addr = attr->addr_m09; +		break; + +	default: +		error = -ENODEV; +		goto out; +	} + +	if (addr != NO_REGISTER) { +		val = edt_ft5x06_register_read(tsdata, addr); +		if (val < 0) { +			error = val; +			dev_err(&tsdata->client->dev, +				"Failed to fetch attribute %s, error %d\n", +				dattr->attr.name, error); +			goto out; +		} +	} else { +		val = *field; +	} + +	if (val != *field) { +		dev_warn(&tsdata->client->dev, +			 "%s: read (%d) and stored value (%d) differ\n", +			 dattr->attr.name, val, *field); +		*field = val; +	} + +	count = scnprintf(buf, PAGE_SIZE, "%d\n", val); +out: +	mutex_unlock(&tsdata->mutex); +	return error ?: count; +} + +static ssize_t edt_ft5x06_setting_store(struct device *dev, +					struct device_attribute *dattr, +					const char *buf, size_t count) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); +	struct edt_ft5x06_attribute *attr = +			container_of(dattr, struct edt_ft5x06_attribute, dattr); +	u8 *field = (u8 *)tsdata + attr->field_offset; +	unsigned int val; +	int error; +	u8 addr; + +	mutex_lock(&tsdata->mutex); + +	if (tsdata->factory_mode) { +		error = -EIO; +		goto out; +	} + +	error = kstrtouint(buf, 0, &val); +	if (error) +		goto out; + +	if (val < attr->limit_low || val > attr->limit_high) { +		error = -ERANGE; +		goto out; +	} + +	switch (tsdata->version) { +	case M06: +		addr = attr->addr_m06; +		break; + +	case M09: +		addr = attr->addr_m09; +		break; + +	default: +		error = -ENODEV; +		goto out; +	} + +	if (addr != NO_REGISTER) { +		error = edt_ft5x06_register_write(tsdata, addr, val); +		if (error) { +			dev_err(&tsdata->client->dev, +				"Failed to update attribute %s, error: %d\n", +				dattr->attr.name, error); +			goto out; +		} +	} +	*field = val; + +out: +	mutex_unlock(&tsdata->mutex); +	return error ?: count; +} + +static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, +		M09_REGISTER_GAIN, 0, 31); +static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, +		M09_REGISTER_OFFSET, 0, 31); +static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, +		M09_REGISTER_THRESHOLD, 20, 80); +static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, +		NO_REGISTER, 3, 14); + +static struct attribute *edt_ft5x06_attrs[] = { +	&edt_ft5x06_attr_gain.dattr.attr, +	&edt_ft5x06_attr_offset.dattr.attr, +	&edt_ft5x06_attr_threshold.dattr.attr, +	&edt_ft5x06_attr_report_rate.dattr.attr, +	NULL +}; + +static const struct attribute_group edt_ft5x06_attr_group = { +	.attrs = edt_ft5x06_attrs, +}; + +#ifdef CONFIG_DEBUG_FS +static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) +{ +	struct i2c_client *client = tsdata->client; +	int retries = EDT_SWITCH_MODE_RETRIES; +	int ret; +	int error; + +	disable_irq(client->irq); + +	if (!tsdata->raw_buffer) { +		tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y * +				      sizeof(u16); +		tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL); +		if (!tsdata->raw_buffer) { +			error = -ENOMEM; +			goto err_out; +		} +	} + +	/* mode register is 0x3c when in the work mode */ +	if (tsdata->version == M09) +		goto m09_out; + +	error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); +	if (error) { +		dev_err(&client->dev, +			"failed to switch to factory mode, error %d\n", error); +		goto err_out; +	} + +	tsdata->factory_mode = true; +	do { +		mdelay(EDT_SWITCH_MODE_DELAY); +		/* mode register is 0x01 when in factory mode */ +		ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE); +		if (ret == 0x03) +			break; +	} while (--retries > 0); + +	if (retries == 0) { +		dev_err(&client->dev, "not in factory mode after %dms.\n", +			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); +		error = -EIO; +		goto err_out; +	} + +	return 0; + +err_out: +	kfree(tsdata->raw_buffer); +	tsdata->raw_buffer = NULL; +	tsdata->factory_mode = false; +	enable_irq(client->irq); + +	return error; + +m09_out: +	dev_err(&client->dev, "No factory mode support for M09\n"); +	return -EINVAL; + +} + +static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) +{ +	struct i2c_client *client = tsdata->client; +	int retries = EDT_SWITCH_MODE_RETRIES; +	struct edt_reg_addr *reg_addr = &tsdata->reg_addr; +	int ret; +	int error; + +	/* mode register is 0x01 when in the factory mode */ +	error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1); +	if (error) { +		dev_err(&client->dev, +			"failed to switch to work mode, error: %d\n", error); +		return error; +	} + +	tsdata->factory_mode = false; + +	do { +		mdelay(EDT_SWITCH_MODE_DELAY); +		/* mode register is 0x01 when in factory mode */ +		ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE); +		if (ret == 0x01) +			break; +	} while (--retries > 0); + +	if (retries == 0) { +		dev_err(&client->dev, "not in work mode after %dms.\n", +			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); +		tsdata->factory_mode = true; +		return -EIO; +	} + +	kfree(tsdata->raw_buffer); +	tsdata->raw_buffer = NULL; + +	/* restore parameters */ +	edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, +				  tsdata->threshold); +	edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, +				  tsdata->gain); +	edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, +				  tsdata->offset); +	if (reg_addr->reg_report_rate) +		edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, +				  tsdata->report_rate); + +	enable_irq(client->irq); + +	return 0; +} + +static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) +{ +	struct edt_ft5x06_ts_data *tsdata = data; + +	*mode = tsdata->factory_mode; + +	return 0; +}; + +static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) +{ +	struct edt_ft5x06_ts_data *tsdata = data; +	int retval = 0; + +	if (mode > 1) +		return -ERANGE; + +	mutex_lock(&tsdata->mutex); + +	if (mode != tsdata->factory_mode) { +		retval = mode ? edt_ft5x06_factory_mode(tsdata) : +				edt_ft5x06_work_mode(tsdata); +	} + +	mutex_unlock(&tsdata->mutex); + +	return retval; +}; + +DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, +			edt_ft5x06_debugfs_mode_set, "%llu\n"); + +static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, +				char __user *buf, size_t count, loff_t *off) +{ +	struct edt_ft5x06_ts_data *tsdata = file->private_data; +	struct i2c_client *client = tsdata->client; +	int retries  = EDT_RAW_DATA_RETRIES; +	int val, i, error; +	size_t read = 0; +	int colbytes; +	char wrbuf[3]; +	u8 *rdbuf; + +	if (*off < 0 || *off >= tsdata->raw_bufsize) +		return 0; + +	mutex_lock(&tsdata->mutex); + +	if (!tsdata->factory_mode || !tsdata->raw_buffer) { +		error = -EIO; +		goto out; +	} + +	error = edt_ft5x06_register_write(tsdata, 0x08, 0x01); +	if (error) { +		dev_dbg(&client->dev, +			"failed to write 0x08 register, error %d\n", error); +		goto out; +	} + +	do { +		msleep(EDT_RAW_DATA_DELAY); +		val = edt_ft5x06_register_read(tsdata, 0x08); +		if (val < 1) +			break; +	} while (--retries > 0); + +	if (val < 0) { +		error = val; +		dev_dbg(&client->dev, +			"failed to read 0x08 register, error %d\n", error); +		goto out; +	} + +	if (retries == 0) { +		dev_dbg(&client->dev, +			"timed out waiting for register to settle\n"); +		error = -ETIMEDOUT; +		goto out; +	} + +	rdbuf = tsdata->raw_buffer; +	colbytes = tsdata->num_y * sizeof(u16); + +	wrbuf[0] = 0xf5; +	wrbuf[1] = 0x0e; +	for (i = 0; i < tsdata->num_x; i++) { +		wrbuf[2] = i;  /* column index */ +		error = edt_ft5x06_ts_readwrite(tsdata->client, +						sizeof(wrbuf), wrbuf, +						colbytes, rdbuf); +		if (error) +			goto out; + +		rdbuf += colbytes; +	} + +	read = min_t(size_t, count, tsdata->raw_bufsize - *off); +	if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) { +		error = -EFAULT; +		goto out; +	} + +	*off += read; +out: +	mutex_unlock(&tsdata->mutex); +	return error ?: read; +}; + +static const struct file_operations debugfs_raw_data_fops = { +	.open = simple_open, +	.read = edt_ft5x06_debugfs_raw_data_read, +}; + +static void +edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, +			      const char *debugfs_name) +{ +	tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); +	if (!tsdata->debug_dir) +		return; + +	debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); +	debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); + +	debugfs_create_file("mode", S_IRUSR | S_IWUSR, +			    tsdata->debug_dir, tsdata, &debugfs_mode_fops); +	debugfs_create_file("raw_data", S_IRUSR, +			    tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); +} + +static void +edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) +{ +	if (tsdata->debug_dir) +		debugfs_remove_recursive(tsdata->debug_dir); +	kfree(tsdata->raw_buffer); +} + +#else + +static inline void +edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, +			      const char *debugfs_name) +{ +} + +static inline void +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) +{ +	u8 rdbuf[EDT_NAME_LEN]; +	char *p; +	int error; +	char *model_name = tsdata->name; + +	/* see what we find if we assume it is a M06 * +	 * if we get less than EDT_NAME_LEN, we don't want +	 * to have garbage in there +	 */ +	memset(rdbuf, 0, sizeof(rdbuf)); +	error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", +					EDT_NAME_LEN - 1, rdbuf); +	if (error) +		return error; + +	/* if we find something consistent, stay with that assumption +	 * at least M09 won't send 3 bytes here +	 */ +	if (!(strnicmp(rdbuf + 1, "EP0", 3))) { +		tsdata->version = M06; + +		/* remove last '$' end marker */ +		rdbuf[EDT_NAME_LEN - 1] = '\0'; +		if (rdbuf[EDT_NAME_LEN - 2] == '$') +			rdbuf[EDT_NAME_LEN - 2] = '\0'; + +		/* look for Model/Version separator */ +		p = strchr(rdbuf, '*'); +		if (p) +			*p++ = '\0'; +		strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); +		strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); +	} else { +		/* since there are only two versions around (M06, M09) */ +		tsdata->version = M09; + +		error = edt_ft5x06_ts_readwrite(client, 1, "\xA6", +						2, rdbuf); +		if (error) +			return error; + +		strlcpy(fw_version, rdbuf, 2); + +		error = edt_ft5x06_ts_readwrite(client, 1, "\xA8", +						1, rdbuf); +		if (error) +			return error; + +		snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09", +			rdbuf[0] >> 4, rdbuf[0] & 0x0F); +	} + +	return 0; +} + +#define EDT_ATTR_CHECKSET(name, reg) \ +	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) + +#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) +{ +	struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + +	EDT_GET_PROP(threshold, reg_addr->reg_threshold); +	EDT_GET_PROP(gain, reg_addr->reg_gain); +	EDT_GET_PROP(offset, reg_addr->reg_offset); +} + +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; + +	/* 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); +} + +static void +edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) +{ +	struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + +	tsdata->threshold = edt_ft5x06_register_read(tsdata, +						     reg_addr->reg_threshold); +	tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); +	tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); +	if (reg_addr->reg_report_rate != NO_REGISTER) +		tsdata->report_rate = edt_ft5x06_register_read(tsdata, +						reg_addr->reg_report_rate); +	tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x); +	tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y); +} + +static void +edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) +{ +	struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + +	switch (tsdata->version) { +	case M06: +		reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD; +		reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; +		reg_addr->reg_gain = WORK_REGISTER_GAIN; +		reg_addr->reg_offset = WORK_REGISTER_OFFSET; +		reg_addr->reg_num_x = WORK_REGISTER_NUM_X; +		reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; +		break; + +	case M09: +		reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; +		reg_addr->reg_gain = M09_REGISTER_GAIN; +		reg_addr->reg_offset = M09_REGISTER_OFFSET; +		reg_addr->reg_num_x = M09_REGISTER_NUM_X; +		reg_addr->reg_num_y = M09_REGISTER_NUM_Y; +		break; +	} +} + +#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); +	struct edt_ft5x06_ts_data *tsdata; +	struct input_dev *input; +	int error; +	char fw_version[EDT_NAME_LEN]; + +	dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + +	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); +	if (!tsdata) { +		dev_err(&client->dev, "failed to allocate driver data.\n"); +		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; +	} + +	error = edt_ft5x06_ts_reset(client, tsdata); +	if (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; +		} +	} + +	input = devm_input_allocate_device(&client->dev); +	if (!input) { +		dev_err(&client->dev, "failed to allocate input device.\n"); +		return -ENOMEM; +	} + +	mutex_init(&tsdata->mutex); +	tsdata->client = client; +	tsdata->input = input; +	tsdata->factory_mode = false; + +	error = edt_ft5x06_ts_identify(client, tsdata, fw_version); +	if (error) { +		dev_err(&client->dev, "touchscreen probe failed\n"); +		return error; +	} + +	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_parameters(tsdata); + +	dev_dbg(&client->dev, +		"Model \"%s\", Rev. \"%s\", %dx%d sensors\n", +		tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + +	input->name = tsdata->name; +	input->id.bustype = BUS_I2C; +	input->dev.parent = &client->dev; + +	__set_bit(EV_SYN, input->evbit); +	__set_bit(EV_KEY, input->evbit); +	__set_bit(EV_ABS, input->evbit); +	__set_bit(BTN_TOUCH, input->keybit); +	input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); +	input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); +	input_set_abs_params(input, ABS_MT_POSITION_X, +			     0, tsdata->num_x * 64 - 1, 0, 0); +	input_set_abs_params(input, ABS_MT_POSITION_Y, +			     0, tsdata->num_y * 64 - 1, 0, 0); +	error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); +	if (error) { +		dev_err(&client->dev, "Unable to init MT slots.\n"); +		return error; +	} + +	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, +					client->name, tsdata); +	if (error) { +		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); +		return error; +	} + +	error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); +	if (error) +		return error; + +	error = input_register_device(input); +	if (error) +		goto err_remove_attrs; + +	edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); +	device_init_wakeup(&client->dev, 1); + +	dev_dbg(&client->dev, +		"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", +		client->irq, tsdata->wake_pin, tsdata->reset_pin); + +	return 0; + +err_remove_attrs: +	sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); +	return error; +} + +static int edt_ft5x06_ts_remove(struct i2c_client *client) +{ +	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + +	edt_ft5x06_ts_teardown_debugfs(tsdata); +	sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int edt_ft5x06_ts_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	if (device_may_wakeup(dev)) +		enable_irq_wake(client->irq); + +	return 0; +} + +static int edt_ft5x06_ts_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	if (device_may_wakeup(dev)) +		disable_irq_wake(client->irq); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, +			 edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); + +static const struct i2c_device_id edt_ft5x06_ts_id[] = { +	{ "edt-ft5x06", 0, }, +	{ /* 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", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); +#endif + +static struct i2c_driver edt_ft5x06_ts_driver = { +	.driver = { +		.owner = THIS_MODULE, +		.name = "edt_ft5x06", +		.of_match_table = of_match_ptr(edt_ft5x06_of_match), +		.pm = &edt_ft5x06_ts_pm_ops, +	}, +	.id_table = edt_ft5x06_ts_id, +	.probe    = edt_ft5x06_ts_probe, +	.remove   = edt_ft5x06_ts_remove, +}; + +module_i2c_driver(edt_ft5x06_ts_driver); + +MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>"); +MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 7a3a916f84a..b1884ddd7a8 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -35,11 +35,11 @@  #include <linux/input/eeti_ts.h>  #include <linux/slab.h> -static int flip_x; +static bool flip_x;  module_param(flip_x, bool, 0644);  MODULE_PARM_DESC(flip_x, "flip x coordinate"); -static int flip_y; +static bool flip_y;  module_param(flip_y, bool, 0644);  MODULE_PARM_DESC(flip_y, "flip y coordinate"); @@ -48,7 +48,7 @@ struct eeti_ts_priv {  	struct input_dev *input;  	struct work_struct work;  	struct mutex mutex; -	int irq, irq_active_high; +	int irq_gpio, irq, irq_active_high;  };  #define EETI_TS_BITDEPTH	(11) @@ -62,7 +62,7 @@ struct eeti_ts_priv {  static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)  { -	return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high; +	return gpio_get_value(priv->irq_gpio) == priv->irq_active_high;  }  static void eeti_ts_read(struct work_struct *work) @@ -154,10 +154,10 @@ static void eeti_ts_close(struct input_dev *dev)  	eeti_ts_stop(priv);  } -static int __devinit eeti_ts_probe(struct i2c_client *client, +static int eeti_ts_probe(struct i2c_client *client,  				   const struct i2c_device_id *idp)  { -	struct eeti_ts_platform_data *pdata; +	struct eeti_ts_platform_data *pdata = dev_get_platdata(&client->dev);  	struct eeti_ts_priv *priv;  	struct input_dev *input;  	unsigned int irq_flags; @@ -199,12 +199,14 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,  	priv->client = client;  	priv->input = input; -	priv->irq = client->irq; +	priv->irq_gpio = pdata->irq_gpio; +	priv->irq = gpio_to_irq(pdata->irq_gpio); -	pdata = client->dev.platform_data; +	err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name); +	if (err < 0) +		goto err1; -	if (pdata) -		priv->irq_active_high = pdata->irq_active_high; +	priv->irq_active_high = pdata->irq_active_high;  	irq_flags = priv->irq_active_high ?  		IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; @@ -215,13 +217,13 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,  	err = input_register_device(input);  	if (err) -		goto err1; +		goto err2;  	err = request_irq(priv->irq, eeti_ts_isr, irq_flags,  			  client->name, priv);  	if (err) {  		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); -		goto err2; +		goto err3;  	}  	/* @@ -233,9 +235,11 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,  	device_init_wakeup(&client->dev, 0);  	return 0; -err2: +err3:  	input_unregister_device(input);  	input = NULL; /* so we dont try to free it below */ +err2: +	gpio_free(pdata->irq_gpio);  err1:  	input_free_device(input);  	kfree(priv); @@ -243,7 +247,7 @@ err0:  	return err;  } -static int __devexit eeti_ts_remove(struct i2c_client *client) +static int eeti_ts_remove(struct i2c_client *client)  {  	struct eeti_ts_priv *priv = i2c_get_clientdata(client); @@ -260,9 +264,10 @@ static int __devexit eeti_ts_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM -static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int eeti_ts_suspend(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct eeti_ts_priv *priv = i2c_get_clientdata(client);  	struct input_dev *input_dev = priv->input; @@ -279,8 +284,9 @@ static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg)  	return 0;  } -static int eeti_ts_resume(struct i2c_client *client) +static int eeti_ts_resume(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct eeti_ts_priv *priv = i2c_get_clientdata(client);  	struct input_dev *input_dev = priv->input; @@ -296,11 +302,10 @@ static int eeti_ts_resume(struct i2c_client *client)  	return 0;  } -#else -#define eeti_ts_suspend NULL -#define eeti_ts_resume NULL  #endif +static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume); +  static const struct i2c_device_id eeti_ts_id[] = {  	{ "eeti_ts", 0 },  	{ } @@ -310,28 +315,15 @@ MODULE_DEVICE_TABLE(i2c, eeti_ts_id);  static struct i2c_driver eeti_ts_driver = {  	.driver = {  		.name = "eeti_ts", +		.pm = &eeti_ts_pm,  	},  	.probe = eeti_ts_probe, -	.remove = __devexit_p(eeti_ts_remove), -	.suspend = eeti_ts_suspend, -	.resume = eeti_ts_resume, +	.remove = eeti_ts_remove,  	.id_table = eeti_ts_id,  }; -static int __init eeti_ts_init(void) -{ -	return i2c_add_driver(&eeti_ts_driver); -} - -static void __exit eeti_ts_exit(void) -{ -	i2c_del_driver(&eeti_ts_driver); -} +module_i2c_driver(eeti_ts_driver);  MODULE_DESCRIPTION("EETI Touchscreen driver");  MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");  MODULE_LICENSE("GPL"); - -module_init(eeti_ts_init); -module_exit(eeti_ts_exit); - diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c new file mode 100644 index 00000000000..c8057847d71 --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts.c @@ -0,0 +1,285 @@ +/* + * Driver for EETI eGalax Multiple Touch Controller + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * based on max11801_ts.c + * + * 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. + */ + +/* EETI eGalax serial touch screen controller is a I2C based multiple + * touch screen controller, it supports 5 point multiple touch. */ + +/* TODO: +  - auto idle mode support +*/ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/input/mt.h> +#include <linux/of_gpio.h> + +/* + * Mouse Mode: some panel may configure the controller to mouse mode, + * which can only report one point at a given time. + * This driver will ignore events in this mode. + */ +#define REPORT_MODE_MOUSE		0x1 +/* + * Vendor Mode: this mode is used to transfer some vendor specific + * messages. + * This driver will ignore events in this mode. + */ +#define REPORT_MODE_VENDOR		0x3 +/* Multiple Touch Mode */ +#define REPORT_MODE_MTTOUCH		0x4 + +#define MAX_SUPPORT_POINTS		5 + +#define EVENT_VALID_OFFSET	7 +#define EVENT_VALID_MASK	(0x1 << EVENT_VALID_OFFSET) +#define EVENT_ID_OFFSET		2 +#define EVENT_ID_MASK		(0xf << EVENT_ID_OFFSET) +#define EVENT_IN_RANGE		(0x1 << 1) +#define EVENT_DOWN_UP		(0X1 << 0) + +#define MAX_I2C_DATA_LEN	10 + +#define EGALAX_MAX_X	32760 +#define EGALAX_MAX_Y	32760 +#define EGALAX_MAX_TRIES 100 + +struct egalax_ts { +	struct i2c_client		*client; +	struct input_dev		*input_dev; +}; + +static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) +{ +	struct egalax_ts *ts = dev_id; +	struct input_dev *input_dev = ts->input_dev; +	struct i2c_client *client = ts->client; +	u8 buf[MAX_I2C_DATA_LEN]; +	int id, ret, x, y, z; +	int tries = 0; +	bool down, valid; +	u8 state; + +	do { +		ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN); +	} while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES); + +	if (ret < 0) +		return IRQ_HANDLED; + +	if (buf[0] != REPORT_MODE_MTTOUCH) { +		/* ignore mouse events and vendor events */ +		return IRQ_HANDLED; +	} + +	state = buf[1]; +	x = (buf[3] << 8) | buf[2]; +	y = (buf[5] << 8) | buf[4]; +	z = (buf[7] << 8) | buf[6]; + +	valid = state & EVENT_VALID_MASK; +	id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; +	down = state & EVENT_DOWN_UP; + +	if (!valid || id > MAX_SUPPORT_POINTS) { +		dev_dbg(&client->dev, "point invalid\n"); +		return IRQ_HANDLED; +	} + +	input_mt_slot(input_dev, id); +	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down); + +	dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d", +		down ? "down" : "up", id, x, y, z); + +	if (down) { +		input_report_abs(input_dev, ABS_MT_POSITION_X, x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, y); +		input_report_abs(input_dev, ABS_MT_PRESSURE, z); +	} + +	input_mt_report_pointer_emulation(input_dev, true); +	input_sync(input_dev); + +	return IRQ_HANDLED; +} + +/* wake up controller by an falling edge of interrupt gpio.  */ +static int egalax_wake_up_device(struct i2c_client *client) +{ +	struct device_node *np = client->dev.of_node; +	int gpio; +	int ret; + +	if (!np) +		return -ENODEV; + +	gpio = of_get_named_gpio(np, "wakeup-gpios", 0); +	if (!gpio_is_valid(gpio)) +		return -ENODEV; + +	ret = gpio_request(gpio, "egalax_irq"); +	if (ret < 0) { +		dev_err(&client->dev, +			"request gpio failed, cannot wake up controller: %d\n", +			ret); +		return ret; +	} + +	/* wake up controller via an falling edge on IRQ gpio. */ +	gpio_direction_output(gpio, 0); +	gpio_set_value(gpio, 1); + +	/* controller should be waken up, return irq.  */ +	gpio_direction_input(gpio); +	gpio_free(gpio); + +	return 0; +} + +static int egalax_firmware_version(struct i2c_client *client) +{ +	static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; +	int ret; + +	ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int egalax_ts_probe(struct i2c_client *client, +			   const struct i2c_device_id *id) +{ +	struct egalax_ts *ts; +	struct input_dev *input_dev; +	int error; + +	ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL); +	if (!ts) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	input_dev = devm_input_allocate_device(&client->dev); +	if (!input_dev) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	ts->client = client; +	ts->input_dev = input_dev; + +	/* controller may be in sleep, wake it up. */ +	error = egalax_wake_up_device(client); +	if (error) { +		dev_err(&client->dev, "Failed to wake up the controller\n"); +		return error; +	} + +	error = egalax_firmware_version(client); +	if (error < 0) { +		dev_err(&client->dev, "Failed to read firmware version\n"); +		return error; +	} + +	input_dev->name = "EETI eGalax Touch Screen"; +	input_dev->id.bustype = BUS_I2C; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); + +	input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0); +	input_set_abs_params(input_dev, +			     ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); +	input_set_abs_params(input_dev, +			     ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0); +	input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0); + +	input_set_drvdata(input_dev, ts); + +	error = devm_request_threaded_irq(&client->dev, client->irq, NULL, +					  egalax_ts_interrupt, +					  IRQF_TRIGGER_LOW | IRQF_ONESHOT, +					  "egalax_ts", ts); +	if (error < 0) { +		dev_err(&client->dev, "Failed to register interrupt\n"); +		return error; +	} + +	error = input_register_device(ts->input_dev); +	if (error) +		return error; + +	i2c_set_clientdata(client, ts); +	return 0; +} + +static const struct i2c_device_id egalax_ts_id[] = { +	{ "egalax_ts", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, egalax_ts_id); + +#ifdef CONFIG_PM_SLEEP +static int egalax_ts_suspend(struct device *dev) +{ +	static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = { +		0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0 +	}; +	struct i2c_client *client = to_i2c_client(dev); +	int ret; + +	ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN); +	return ret > 0 ? 0 : ret; +} + +static int egalax_ts_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	return egalax_wake_up_device(client); +} +#endif + +static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); + +static const struct of_device_id egalax_ts_dt_ids[] = { +	{ .compatible = "eeti,egalax_ts" }, +	{ /* sentinel */ } +}; + +static struct i2c_driver egalax_ts_driver = { +	.driver = { +		.name	= "egalax_ts", +		.owner	= THIS_MODULE, +		.pm	= &egalax_ts_pm_ops, +		.of_match_table	= egalax_ts_dt_ids, +	}, +	.id_table	= egalax_ts_id, +	.probe		= egalax_ts_probe, +}; + +module_i2c_driver(egalax_ts_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index 486d31ba9c0..8051a4b704e 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -22,7 +22,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #include <linux/ctype.h>  #define DRIVER_DESC	"Elo serial touchscreen driver" @@ -405,19 +404,4 @@ static struct serio_driver elo_drv = {  	.disconnect	= elo_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init elo_init(void) -{ -	return serio_register_driver(&elo_drv); -} - -static void __exit elo_exit(void) -{ -	serio_unregister_driver(&elo_drv); -} - -module_init(elo_init); -module_exit(elo_exit); +module_serio_driver(elo_drv); diff --git a/drivers/input/touchscreen/fujitsu_ts.c b/drivers/input/touchscreen/fujitsu_ts.c index 80b21800355..d0e46a7e183 100644 --- a/drivers/input/touchscreen/fujitsu_ts.c +++ b/drivers/input/touchscreen/fujitsu_ts.c @@ -16,7 +16,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Fujitsu serial touchscreen driver" @@ -175,15 +174,4 @@ static struct serio_driver fujitsu_drv = {  	.disconnect	= fujitsu_disconnect,  }; -static int __init fujitsu_init(void) -{ -	return serio_register_driver(&fujitsu_drv); -} - -static void __exit fujitsu_exit(void) -{ -	serio_unregister_driver(&fujitsu_drv); -} - -module_init(fujitsu_init); -module_exit(fujitsu_exit); +module_serio_driver(fujitsu_drv); diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c index a54f90e02ab..e2ee6261527 100644 --- a/drivers/input/touchscreen/gunze.c +++ b/drivers/input/touchscreen/gunze.c @@ -32,7 +32,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Gunze AHL-51S touchscreen driver" @@ -186,19 +185,4 @@ static struct serio_driver gunze_drv = {  	.disconnect	= gunze_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init gunze_init(void) -{ -	return serio_register_driver(&gunze_drv); -} - -static void __exit gunze_exit(void) -{ -	serio_unregister_driver(&gunze_drv); -} - -module_init(gunze_init); -module_exit(gunze_exit); +module_serio_driver(gunze_drv); diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c deleted file mode 100644 index b4d7f63deff..00000000000 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - *  Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com - * - *  Sponsored by Transvirtual Technology. - * - *  Derived from the code in h3600_ts.[ch] by Charles Flynn - */ - -/* - * Driver for the h3600 Touch Screen and other Atmel controlled devices. - */ - -/* - * 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. - * - * 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 - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to <jsimmons@transvirtual.com>. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/input.h> -#include <linux/serio.h> -#include <linux/init.h> -#include <linux/delay.h> - -/* SA1100 serial defines */ -#include <mach/hardware.h> -#include <mach/irqs.h> - -#define DRIVER_DESC	"H3600 touchscreen driver" - -MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>"); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - -/* - * Definitions & global arrays. - */ - -/* The start and end of frame characters SOF and EOF */ -#define CHAR_SOF                0x02 -#define CHAR_EOF                0x03 -#define FRAME_OVERHEAD          3       /* CHAR_SOF,CHAR_EOF,LENGTH = 3 */ - -/* -        Atmel events and response IDs contained in frame. -        Programmer has no control over these numbers. -        TODO there are holes - specifically  1,7,0x0a -*/ -#define VERSION_ID              0       /* Get Version (request/respose) */ -#define KEYBD_ID                2       /* Keyboard (event) */ -#define TOUCHS_ID               3       /* Touch Screen (event)*/ -#define EEPROM_READ_ID          4       /* (request/response) */ -#define EEPROM_WRITE_ID         5       /* (request/response) */ -#define THERMAL_ID              6       /* (request/response) */ -#define NOTIFY_LED_ID           8       /* (request/response) */ -#define BATTERY_ID              9       /* (request/response) */ -#define SPI_READ_ID             0x0b    /* ( request/response) */ -#define SPI_WRITE_ID            0x0c    /* ( request/response) */ -#define FLITE_ID                0x0d    /* backlight ( request/response) */ -#define STX_ID                  0xa1    /* extension pack status (req/resp) */ - -#define MAX_ID                  14 - -#define H3600_MAX_LENGTH 16 -#define H3600_KEY 0xf - -#define H3600_SCANCODE_RECORD	1	 /* 1 -> record button */ -#define H3600_SCANCODE_CALENDAR 2	 /* 2 -> calendar */ -#define H3600_SCANCODE_CONTACTS 3	 /* 3 -> contact */ -#define H3600_SCANCODE_Q	4	 /* 4 -> Q button */ -#define	H3600_SCANCODE_START	5	 /* 5 -> start menu */ -#define	H3600_SCANCODE_UP	6	 /* 6 -> up */ -#define H3600_SCANCODE_RIGHT	7	 /* 7 -> right */ -#define H3600_SCANCODE_LEFT	8	 /* 8 -> left */ -#define H3600_SCANCODE_DOWN	9	 /* 9 -> down */ - -/* - * Per-touchscreen data. - */ -struct h3600_dev { -	struct input_dev *dev; -	struct serio *serio; -	unsigned char event;	/* event ID from packet */ -	unsigned char chksum; -	unsigned char len; -	unsigned char idx; -	unsigned char buf[H3600_MAX_LENGTH]; -	char phys[32]; -}; - -static irqreturn_t action_button_handler(int irq, void *dev_id) -{ -	int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1; -	struct input_dev *dev = dev_id; - -	input_report_key(dev, KEY_ENTER, down); -	input_sync(dev); - -	return IRQ_HANDLED; -} - -static irqreturn_t npower_button_handler(int irq, void *dev_id) -{ -	int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1; -	struct input_dev *dev = dev_id; - -	/* -	 * This interrupt is only called when we release the key. So we have -	 * to fake a key press. -	 */ -	input_report_key(dev, KEY_SUSPEND, 1); -	input_report_key(dev, KEY_SUSPEND, down); -	input_sync(dev); - -	return IRQ_HANDLED; -} - -#ifdef CONFIG_PM - -static int flite_brightness = 25; - -enum flite_pwr { -	FLITE_PWR_OFF = 0, -	FLITE_PWR_ON = 1 -}; - -/* - * h3600_flite_power: enables or disables power to frontlight, using last bright */ -unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr) -{ -	unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness; -	struct h3600_dev *ts = input_get_drvdata(dev); - -	/* Must be in this order */ -	serio_write(ts->serio, 1); -	serio_write(ts->serio, pwr); -	serio_write(ts->serio, brightness); - -	return 0; -} - -#endif - -/* - * This function translates the native event packets to linux input event - * packets. Some packets coming from serial are not touchscreen related. In - * this case we send them off to be processed elsewhere. - */ -static void h3600ts_process_packet(struct h3600_dev *ts) -{ -	struct input_dev *dev = ts->dev; -	static int touched = 0; -	int key, down = 0; - -	switch (ts->event) { -		/* -		   Buttons - returned as a single byte -			7 6 5 4 3 2 1 0 -			S x x x N N N N - -		   S       switch state ( 0=pressed 1=released) -		   x       Unused. -		   NNNN    switch number 0-15 - -		   Note: This is true for non interrupt generated key events. -		*/ -		case KEYBD_ID: -			down = (ts->buf[0] & 0x80) ? 0 : 1; - -			switch (ts->buf[0] & 0x7f) { -				case H3600_SCANCODE_RECORD: -					key = KEY_RECORD; -					break; -				case H3600_SCANCODE_CALENDAR: -					key = KEY_PROG1; -                                        break; -				case H3600_SCANCODE_CONTACTS: -					key = KEY_PROG2; -					break; -				case H3600_SCANCODE_Q: -					key = KEY_Q; -					break; -				case H3600_SCANCODE_START: -					key = KEY_PROG3; -					break; -				case H3600_SCANCODE_UP: -					key = KEY_UP; -					break; -				case H3600_SCANCODE_RIGHT: -					key = KEY_RIGHT; -					break; -				case H3600_SCANCODE_LEFT: -					key = KEY_LEFT; -					break; -				case H3600_SCANCODE_DOWN: -					key = KEY_DOWN; -					break; -				default: -					key = 0; -			} -			if (key) -				input_report_key(dev, key, down); -			break; -		/* -		 * Native touchscreen event data is formatted as shown below:- -		 * -		 *      +-------+-------+-------+-------+ -		 *      | Xmsb  | Xlsb  | Ymsb  | Ylsb  | -		 *      +-------+-------+-------+-------+ -		 *       byte 0    1       2       3 -		 */ -		case TOUCHS_ID: -			if (!touched) { -				input_report_key(dev, BTN_TOUCH, 1); -				touched = 1; -			} - -			if (ts->len) { -				unsigned short x, y; - -				x = ts->buf[0]; x <<= 8; x += ts->buf[1]; -				y = ts->buf[2]; y <<= 8; y += ts->buf[3]; - -				input_report_abs(dev, ABS_X, x); -				input_report_abs(dev, ABS_Y, y); -			} else { -				input_report_key(dev, BTN_TOUCH, 0); -				touched = 0; -			} -			break; -		default: -			/* Send a non input event elsewhere */ -			break; -	} - -	input_sync(dev); -} - -/* - * h3600ts_event() handles events from the input module. - */ -static int h3600ts_event(struct input_dev *dev, unsigned int type, -			 unsigned int code, int value) -{ -#if 0 -	struct h3600_dev *ts = input_get_drvdata(dev); - -	switch (type) { -		case EV_LED: { -		//	serio_write(ts->serio, SOME_CMD); -			return 0; -		} -	} -	return -1; -#endif -	return 0; -} - -/* -        Frame format -  byte    1       2               3              len + 4 -        +-------+---------------+---------------+--=------------+ -        |SOF    |id     |len    | len bytes     | Chksum        | -        +-------+---------------+---------------+--=------------+ -  bit   0     7  8    11 12   15 16 - -        +-------+---------------+-------+ -        |SOF    |id     |0      |Chksum | - Note Chksum does not include SOF -        +-------+---------------+-------+ -  bit   0     7  8    11 12   15 16 - -*/ - -static int state; - -/* decode States  */ -#define STATE_SOF       0       /* start of FRAME */ -#define STATE_ID        1       /* state where we decode the ID & len */ -#define STATE_DATA      2       /* state where we decode data */ -#define STATE_EOF       3       /* state where we decode checksum or EOF */ - -static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data, -                                     unsigned int flags) -{ -	struct h3600_dev *ts = serio_get_drvdata(serio); - -	/* -	 * We have a new frame coming in. -	 */ -	switch (state) { -		case STATE_SOF: -			if (data == CHAR_SOF) -				state = STATE_ID; -			break; -		case STATE_ID: -			ts->event = (data & 0xf0) >> 4; -			ts->len = (data & 0xf); -			ts->idx = 0; -			if (ts->event >= MAX_ID) { -				state = STATE_SOF; -				break; -			} -			ts->chksum = data; -			state = (ts->len > 0) ? STATE_DATA : STATE_EOF; -			break; -		case STATE_DATA: -			ts->chksum += data; -			ts->buf[ts->idx]= data; -			if (++ts->idx == ts->len) -				state = STATE_EOF; -			break; -		case STATE_EOF: -			state = STATE_SOF; -			if (data == CHAR_EOF || data == ts->chksum) -				h3600ts_process_packet(ts); -			break; -		default: -			printk("Error3\n"); -			break; -	} - -	return IRQ_HANDLED; -} - -/* - * h3600ts_connect() is the routine that is called when someone adds a - * new serio device that supports H3600 protocol and registers it as - * an input device. - */ -static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) -{ -	struct h3600_dev *ts; -	struct input_dev *input_dev; -	int err; - -	ts = kzalloc(sizeof(struct h3600_dev), GFP_KERNEL); -	input_dev = input_allocate_device(); -	if (!ts || !input_dev) { -		err = -ENOMEM; -		goto fail1; -	} - -	ts->serio = serio; -	ts->dev = input_dev; -	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", serio->phys); - -	input_dev->name = "H3600 TouchScreen"; -	input_dev->phys = ts->phys; -	input_dev->id.bustype = BUS_RS232; -	input_dev->id.vendor = SERIO_H3600; -	input_dev->id.product = 0x0666;  /* FIXME !!! We can ask the hardware */ -	input_dev->id.version = 0x0100; -	input_dev->dev.parent = &serio->dev; - -	input_set_drvdata(input_dev, ts); - -	input_dev->event = h3600ts_event; - -	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | -		BIT_MASK(EV_LED) | BIT_MASK(EV_PWR); -	input_dev->ledbit[0] = BIT_MASK(LED_SLEEP); -	input_set_abs_params(input_dev, ABS_X, 60, 985, 0, 0); -	input_set_abs_params(input_dev, ABS_Y, 35, 1024, 0, 0); - -	set_bit(KEY_RECORD, input_dev->keybit); -	set_bit(KEY_Q, input_dev->keybit); -	set_bit(KEY_PROG1, input_dev->keybit); -	set_bit(KEY_PROG2, input_dev->keybit); -	set_bit(KEY_PROG3, input_dev->keybit); -	set_bit(KEY_UP, input_dev->keybit); -	set_bit(KEY_RIGHT, input_dev->keybit); -	set_bit(KEY_LEFT, input_dev->keybit); -	set_bit(KEY_DOWN, input_dev->keybit); -	set_bit(KEY_ENTER, input_dev->keybit); -	set_bit(KEY_SUSPEND, input_dev->keybit); -	set_bit(BTN_TOUCH, input_dev->keybit); - -	/* Device specific stuff */ -	set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES); -	set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE); - -	if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler, -			IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) { -		printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n"); -		err = -EBUSY; -		goto fail2; -	} - -	if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler, -			IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) { -		printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n"); -		err = -EBUSY; -		goto fail3; -	} - -	serio_set_drvdata(serio, ts); - -	err = serio_open(serio, drv); -	if (err) -		return err; - -	//h3600_flite_control(1, 25);     /* default brightness */ -	input_register_device(ts->dev); - -	return 0; - -fail3:	free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev); -fail2:	free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev); -fail1:	serio_set_drvdata(serio, NULL); -	input_free_device(input_dev); -	kfree(ts); -	return err; -} - -/* - * h3600ts_disconnect() is the opposite of h3600ts_connect() - */ - -static void h3600ts_disconnect(struct serio *serio) -{ -	struct h3600_dev *ts = serio_get_drvdata(serio); - -	free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev); -	free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, &ts->dev); -	input_get_device(ts->dev); -	input_unregister_device(ts->dev); -	serio_close(serio); -	serio_set_drvdata(serio, NULL); -	input_put_device(ts->dev); -	kfree(ts); -} - -/* - * The serio driver structure. - */ - -static struct serio_device_id h3600ts_serio_ids[] = { -	{ -		.type	= SERIO_RS232, -		.proto	= SERIO_H3600, -		.id	= SERIO_ANY, -		.extra	= SERIO_ANY, -	}, -	{ 0 } -}; - -MODULE_DEVICE_TABLE(serio, h3600ts_serio_ids); - -static struct serio_driver h3600ts_drv = { -	.driver		= { -		.name	= "h3600ts", -	}, -	.description	= DRIVER_DESC, -	.id_table	= h3600ts_serio_ids, -	.interrupt	= h3600ts_interrupt, -	.connect	= h3600ts_connect, -	.disconnect	= h3600ts_disconnect, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -static int __init h3600ts_init(void) -{ -	return serio_register_driver(&h3600ts_drv); -} - -static void __exit h3600ts_exit(void) -{ -	serio_unregister_driver(&h3600ts_drv); -} - -module_init(h3600ts_init); -module_exit(h3600ts_exit); diff --git a/drivers/input/touchscreen/hampshire.c b/drivers/input/touchscreen/hampshire.c index 2da6cc31bb2..ecb1e0e0132 100644 --- a/drivers/input/touchscreen/hampshire.c +++ b/drivers/input/touchscreen/hampshire.c @@ -23,7 +23,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Hampshire serial touchscreen driver" @@ -187,19 +186,4 @@ static struct serio_driver hampshire_drv = {  	.disconnect	= hampshire_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init hampshire_init(void) -{ -	return serio_register_driver(&hampshire_drv); -} - -static void __exit hampshire_exit(void) -{ -	serio_unregister_driver(&hampshire_drv); -} - -module_init(hampshire_init); -module_exit(hampshire_exit); +module_serio_driver(hampshire_drv); diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c index dd4e8f020b9..85cf9bee801 100644 --- a/drivers/input/touchscreen/hp680_ts_input.c +++ b/drivers/input/touchscreen/hp680_ts_input.c @@ -93,7 +93,7 @@ static int __init hp680_ts_init(void)  	hp680_ts_dev->phys = "hp680_ts/input0";  	if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt, -			IRQF_DISABLED, MODNAME, 0) < 0) { +			0, MODNAME, NULL) < 0) {  		printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",  		       HP680_TS_IRQ);  		err = -EBUSY; diff --git a/drivers/input/touchscreen/htcpen.c b/drivers/input/touchscreen/htcpen.c index 62811de6f18..92e2243fb77 100644 --- a/drivers/input/touchscreen/htcpen.c +++ b/drivers/input/touchscreen/htcpen.c @@ -40,19 +40,13 @@ MODULE_LICENSE("GPL");  #define X_AXIS_MAX		2040  #define Y_AXIS_MAX		2040 -static int invert_x; +static bool invert_x;  module_param(invert_x, bool, 0644);  MODULE_PARM_DESC(invert_x, "If set, X axis is inverted"); -static int invert_y; +static bool invert_y;  module_param(invert_y, bool, 0644);  MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted"); -static struct pnp_device_id pnp_ids[] = { -	{ .id = "PNP0cc0" }, -	{ .id = "" } -}; -MODULE_DEVICE_TABLE(pnp, pnp_ids); -  static irqreturn_t htcpen_interrupt(int irq, void *handle)  {  	struct input_dev *htcpen_dev = handle; @@ -108,7 +102,7 @@ static void htcpen_close(struct input_dev *dev)  	synchronize_irq(HTCPEN_IRQ);  } -static int __devinit htcpen_isa_probe(struct device *dev, unsigned int id) +static int htcpen_isa_probe(struct device *dev, unsigned int id)  {  	struct input_dev *htcpen_dev;  	int err = -EBUSY; @@ -180,7 +174,7 @@ static int __devinit htcpen_isa_probe(struct device *dev, unsigned int id)  	return err;  } -static int __devexit htcpen_isa_remove(struct device *dev, unsigned int id) +static int htcpen_isa_remove(struct device *dev, unsigned int id)  {  	struct input_dev *htcpen_dev = dev_get_drvdata(dev); @@ -192,8 +186,6 @@ static int __devexit htcpen_isa_remove(struct device *dev, unsigned int id)  	release_region(HTCPEN_PORT_INIT, 1);  	release_region(HTCPEN_PORT_IRQ_CLEAR, 1); -	dev_set_drvdata(dev, NULL); -  	return 0;  } @@ -216,7 +208,7 @@ static int htcpen_isa_resume(struct device *dev, unsigned int n)  static struct isa_driver htcpen_isa_driver = {  	.probe		= htcpen_isa_probe, -	.remove		= __devexit_p(htcpen_isa_remove), +	.remove		= htcpen_isa_remove,  #ifdef CONFIG_PM  	.suspend	= htcpen_isa_suspend,  	.resume		= htcpen_isa_resume, @@ -227,7 +219,7 @@ static struct isa_driver htcpen_isa_driver = {  	}  }; -static struct dmi_system_id __initdata htcshift_dmi_table[] = { +static struct dmi_system_id htcshift_dmi_table[] __initdata = {  	{  		.ident = "Shift",  		.matches = { @@ -237,6 +229,7 @@ static struct dmi_system_id __initdata htcshift_dmi_table[] = {  	},  	{ }  }; +MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);  static int __init htcpen_isa_init(void)  { diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c new file mode 100644 index 00000000000..2a508913981 --- /dev/null +++ b/drivers/input/touchscreen/ili210x.c @@ -0,0 +1,360 @@ +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/input/ili210x.h> + +#define MAX_TOUCHES		2 +#define DEFAULT_POLL_PERIOD	20 + +/* Touchscreen commands */ +#define REG_TOUCHDATA		0x10 +#define REG_PANEL_INFO		0x20 +#define REG_FIRMWARE_VERSION	0x40 +#define REG_CALIBRATE		0xcc + +struct finger { +	u8 x_low; +	u8 x_high; +	u8 y_low; +	u8 y_high; +} __packed; + +struct touchdata { +	u8 status; +	struct finger finger[MAX_TOUCHES]; +} __packed; + +struct panel_info { +	struct finger finger_max; +	u8 xchannel_num; +	u8 ychannel_num; +} __packed; + +struct firmware_version { +	u8 id; +	u8 major; +	u8 minor; +} __packed; + +struct ili210x { +	struct i2c_client *client; +	struct input_dev *input; +	bool (*get_pendown_state)(void); +	unsigned int poll_period; +	struct delayed_work dwork; +}; + +static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, +			    size_t len) +{ +	struct i2c_msg msg[2] = { +		{ +			.addr	= client->addr, +			.flags	= 0, +			.len	= 1, +			.buf	= ®, +		}, +		{ +			.addr	= client->addr, +			.flags	= I2C_M_RD, +			.len	= len, +			.buf	= buf, +		} +	}; + +	if (i2c_transfer(client->adapter, msg, 2) != 2) { +		dev_err(&client->dev, "i2c transfer failed\n"); +		return -EIO; +	} + +	return 0; +} + +static void ili210x_report_events(struct input_dev *input, +				  const struct touchdata *touchdata) +{ +	int i; +	bool touch; +	unsigned int x, y; +	const struct finger *finger; + +	for (i = 0; i < MAX_TOUCHES; i++) { +		input_mt_slot(input, i); + +		finger = &touchdata->finger[i]; + +		touch = touchdata->status & (1 << i); +		input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); +		if (touch) { +			x = finger->x_low | (finger->x_high << 8); +			y = finger->y_low | (finger->y_high << 8); + +			input_report_abs(input, ABS_MT_POSITION_X, x); +			input_report_abs(input, ABS_MT_POSITION_Y, y); +		} +	} + +	input_mt_report_pointer_emulation(input, false); +	input_sync(input); +} + +static bool get_pendown_state(const struct ili210x *priv) +{ +	bool state = false; + +	if (priv->get_pendown_state) +		state = priv->get_pendown_state(); + +	return state; +} + +static void ili210x_work(struct work_struct *work) +{ +	struct ili210x *priv = container_of(work, struct ili210x, +					    dwork.work); +	struct i2c_client *client = priv->client; +	struct touchdata touchdata; +	int error; + +	error = ili210x_read_reg(client, REG_TOUCHDATA, +				 &touchdata, sizeof(touchdata)); +	if (error) { +		dev_err(&client->dev, +			"Unable to get touchdata, err = %d\n", error); +		return; +	} + +	ili210x_report_events(priv->input, &touchdata); + +	if ((touchdata.status & 0xf3) || get_pendown_state(priv)) +		schedule_delayed_work(&priv->dwork, +				      msecs_to_jiffies(priv->poll_period)); +} + +static irqreturn_t ili210x_irq(int irq, void *irq_data) +{ +	struct ili210x *priv = irq_data; + +	schedule_delayed_work(&priv->dwork, 0); + +	return IRQ_HANDLED; +} + +static ssize_t ili210x_calibrate(struct device *dev, +				 struct device_attribute *attr, +				 const char *buf, size_t count) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct ili210x *priv = i2c_get_clientdata(client); +	unsigned long calibrate; +	int rc; +	u8 cmd = REG_CALIBRATE; + +	if (kstrtoul(buf, 10, &calibrate)) +		return -EINVAL; + +	if (calibrate > 1) +		return -EINVAL; + +	if (calibrate) { +		rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); +		if (rc != sizeof(cmd)) +			return -EIO; +	} + +	return count; +} +static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); + +static struct attribute *ili210x_attributes[] = { +	&dev_attr_calibrate.attr, +	NULL, +}; + +static const struct attribute_group ili210x_attr_group = { +	.attrs = ili210x_attributes, +}; + +static int ili210x_i2c_probe(struct i2c_client *client, +				       const struct i2c_device_id *id) +{ +	struct device *dev = &client->dev; +	const struct ili210x_platform_data *pdata = dev_get_platdata(dev); +	struct ili210x *priv; +	struct input_dev *input; +	struct panel_info panel; +	struct firmware_version firmware; +	int xmax, ymax; +	int error; + +	dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + +	if (!pdata) { +		dev_err(dev, "No platform data!\n"); +		return -EINVAL; +	} + +	if (client->irq <= 0) { +		dev_err(dev, "No IRQ!\n"); +		return -EINVAL; +	} + +	/* Get firmware version */ +	error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, +				 &firmware, sizeof(firmware)); +	if (error) { +		dev_err(dev, "Failed to get firmware version, err: %d\n", +			error); +		return error; +	} + +	/* get panel info */ +	error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); +	if (error) { +		dev_err(dev, "Failed to get panel informations, err: %d\n", +			error); +		return error; +	} + +	xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); +	ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + +	priv = kzalloc(sizeof(*priv), GFP_KERNEL); +	input = input_allocate_device(); +	if (!priv || !input) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	priv->client = client; +	priv->input = input; +	priv->get_pendown_state = pdata->get_pendown_state; +	priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; +	INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + +	/* Setup input device */ +	input->name = "ILI210x Touchscreen"; +	input->id.bustype = BUS_I2C; +	input->dev.parent = dev; + +	__set_bit(EV_SYN, input->evbit); +	__set_bit(EV_KEY, input->evbit); +	__set_bit(EV_ABS, input->evbit); +	__set_bit(BTN_TOUCH, input->keybit); + +	/* Single touch */ +	input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); +	input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); + +	/* Multi touch */ +	input_mt_init_slots(input, MAX_TOUCHES, 0); +	input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); +	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + +	input_set_drvdata(input, priv); +	i2c_set_clientdata(client, priv); + +	error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, +			    client->name, priv); +	if (error) { +		dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", +			error); +		goto err_free_mem; +	} + +	error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); +	if (error) { +		dev_err(dev, "Unable to create sysfs attributes, err: %d\n", +			error); +		goto err_free_irq; +	} + +	error = input_register_device(priv->input); +	if (error) { +		dev_err(dev, "Cannot regiser input device, err: %d\n", error); +		goto err_remove_sysfs; +	} + +	device_init_wakeup(&client->dev, 1); + +	dev_dbg(dev, +		"ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", +		client->irq, firmware.id, firmware.major, firmware.minor); + +	return 0; + +err_remove_sysfs: +	sysfs_remove_group(&dev->kobj, &ili210x_attr_group); +err_free_irq: +	free_irq(client->irq, priv); +err_free_mem: +	input_free_device(input); +	kfree(priv); +	return error; +} + +static int ili210x_i2c_remove(struct i2c_client *client) +{ +	struct ili210x *priv = i2c_get_clientdata(client); + +	sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); +	free_irq(priv->client->irq, priv); +	cancel_delayed_work_sync(&priv->dwork); +	input_unregister_device(priv->input); +	kfree(priv); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ili210x_i2c_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	if (device_may_wakeup(&client->dev)) +		enable_irq_wake(client->irq); + +	return 0; +} + +static int ili210x_i2c_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	if (device_may_wakeup(&client->dev)) +		disable_irq_wake(client->irq); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, +			 ili210x_i2c_suspend, ili210x_i2c_resume); + +static const struct i2c_device_id ili210x_i2c_id[] = { +	{ "ili210x", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); + +static struct i2c_driver ili210x_ts_driver = { +	.driver = { +		.name = "ili210x_i2c", +		.owner = THIS_MODULE, +		.pm = &ili210x_i2c_pm, +	}, +	.id_table = ili210x_i2c_id, +	.probe = ili210x_i2c_probe, +	.remove = ili210x_i2c_remove, +}; + +module_i2c_driver(ili210x_ts_driver); + +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); +MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c index 192ade0a0fb..adb80b65a25 100644 --- a/drivers/input/touchscreen/inexio.c +++ b/drivers/input/touchscreen/inexio.c @@ -23,7 +23,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"iNexio serial touchscreen driver" @@ -189,19 +188,4 @@ static struct serio_driver inexio_drv = {  	.disconnect	= inexio_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init inexio_init(void) -{ -	return serio_register_driver(&inexio_drv); -} - -static void __exit inexio_exit(void) -{ -	serio_unregister_driver(&inexio_drv); -} - -module_init(inexio_init); -module_exit(inexio_exit); +module_serio_driver(inexio_drv); diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c index c0307b22d86..c38ca4a7e38 100644 --- a/drivers/input/touchscreen/intel-mid-touch.c +++ b/drivers/input/touchscreen/intel-mid-touch.c @@ -27,7 +27,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/input.h>  #include <linux/interrupt.h>  #include <linux/err.h> @@ -37,6 +36,7 @@  #include <linux/irq.h>  #include <linux/delay.h>  #include <asm/intel_scu_ipc.h> +#include <linux/device.h>  /* PMIC Interrupt registers */  #define PMIC_REG_ID1		0x00 /* PMIC ID1 register */ @@ -427,7 +427,7 @@ out:  }  /* Utility to read PMIC ID */ -static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev) +static int mrstouch_read_pmic_id(uint *vendor, uint *rev)  {  	int err;  	u8 r; @@ -446,17 +446,13 @@ static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)   * Parse ADC channels to find end of the channel configured by other ADC user   * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels   */ -static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev) +static int mrstouch_chan_parse(struct mrstouch_dev *tsdev)  { -	int err, i, found; +	int found = 0; +	int err, i;  	u8 r8; -	found = -1; -  	for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { -		if (found >= 0) -			break; -  		err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);  		if (err)  			return err; @@ -466,16 +462,15 @@ static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)  			break;  		}  	} -	if (found < 0) -		return 0;  	if (tsdev->vendor == PMIC_VENDOR_FS) { -		if (found && found > (MRSTOUCH_MAX_CHANNELS - 18)) +		if (found > MRSTOUCH_MAX_CHANNELS - 18)  			return -ENOSPC;  	} else { -		if (found && found > (MRSTOUCH_MAX_CHANNELS - 4)) +		if (found > MRSTOUCH_MAX_CHANNELS - 4)  			return -ENOSPC;  	} +  	return found;  } @@ -483,7 +478,7 @@ static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)  /*   * Writes touch screen channels to ADC address selection registers   */ -static int __devinit mrstouch_ts_chan_set(uint offset) +static int mrstouch_ts_chan_set(uint offset)  {  	u16 chan; @@ -499,7 +494,7 @@ static int __devinit mrstouch_ts_chan_set(uint offset)  }  /* Initialize ADC */ -static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev) +static int mrstouch_adc_init(struct mrstouch_dev *tsdev)  {  	int err, start;  	u8 ra, rm; @@ -542,7 +537,7 @@ static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)  	 * ADC power on, start, enable PENDET and set loop delay  	 * ADC loop delay is set to 4.5 ms approximately  	 * Loop delay more than this results in jitter in adc readings -	 * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET +	 * Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET  	 * interrupt generation sometimes.  	 */ @@ -573,7 +568,7 @@ static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)  /* Probe function for touch screen driver */ -static int __devinit mrstouch_probe(struct platform_device *pdev) +static int mrstouch_probe(struct platform_device *pdev)  {  	struct mrstouch_dev *tsdev;  	struct input_dev *input; @@ -586,12 +581,17 @@ static int __devinit mrstouch_probe(struct platform_device *pdev)  		return -EINVAL;  	} -	tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL); -	input = input_allocate_device(); -	if (!tsdev || !input) { +	tsdev = devm_kzalloc(&pdev->dev, sizeof(struct mrstouch_dev), +			     GFP_KERNEL); +	if (!tsdev) {  		dev_err(&pdev->dev, "unable to allocate memory\n"); -		err = -ENOMEM; -		goto err_free_mem; +		return -ENOMEM; +	} + +	input = devm_input_allocate_device(&pdev->dev); +	if (!input) { +		dev_err(&pdev->dev, "unable to allocate input device\n"); +		return -ENOMEM;  	}  	tsdev->dev = &pdev->dev; @@ -604,7 +604,7 @@ static int __devinit mrstouch_probe(struct platform_device *pdev)  	err = mrstouch_adc_init(tsdev);  	if (err) {  		dev_err(&pdev->dev, "ADC initialization failed\n"); -		goto err_free_mem; +		return err;  	}  	input->name = "mrst_touchscreen"; @@ -624,40 +624,20 @@ static int __devinit mrstouch_probe(struct platform_device *pdev)  	input_set_abs_params(tsdev->input, ABS_PRESSURE,  			     MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0); -	err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq, -				   0, "mrstouch", tsdev); +	err = devm_request_threaded_irq(&pdev->dev, tsdev->irq, NULL, +					mrstouch_pendet_irq, IRQF_ONESHOT, +					"mrstouch", tsdev);  	if (err) {  		dev_err(tsdev->dev, "unable to allocate irq\n"); -		goto err_free_mem; +		return err;  	}  	err = input_register_device(tsdev->input);  	if (err) {  		dev_err(tsdev->dev, "unable to register input device\n"); -		goto err_free_irq; +		return err;  	} -	platform_set_drvdata(pdev, tsdev); -	return 0; - -err_free_irq: -	free_irq(tsdev->irq, tsdev); -err_free_mem: -	input_free_device(input); -	kfree(tsdev); -	return err; -} - -static int __devexit mrstouch_remove(struct platform_device *pdev) -{ -	struct mrstouch_dev *tsdev = platform_get_drvdata(pdev); - -	free_irq(tsdev->irq, tsdev); -	input_unregister_device(tsdev->input); -	kfree(tsdev); - -	platform_set_drvdata(pdev, NULL); -  	return 0;  } @@ -667,20 +647,8 @@ static struct platform_driver mrstouch_driver = {  		.owner	= THIS_MODULE,  	},  	.probe		= mrstouch_probe, -	.remove		= __devexit_p(mrstouch_remove),  }; - -static int __init mrstouch_init(void) -{ -	return platform_driver_register(&mrstouch_driver); -} -module_init(mrstouch_init); - -static void __exit mrstouch_exit(void) -{ -	platform_driver_unregister(&mrstouch_driver); -} -module_exit(mrstouch_exit); +module_platform_driver(mrstouch_driver);  MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");  MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index 4b0a061811f..7324c5c0fb8 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -14,14 +14,15 @@   */  #include <linux/platform_device.h> -#include <linux/init.h>  #include <linux/input.h>  #include <linux/interrupt.h>  #include <linux/module.h>  #include <linux/slab.h> +#include <linux/io.h>  #include <mach/hardware.h>  #include <mach/jornada720.h> +#include <mach/irqs.h>  MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");  MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); @@ -97,7 +98,7 @@ static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static int __devinit jornada720_ts_probe(struct platform_device *pdev) +static int jornada720_ts_probe(struct platform_device *pdev)  {  	struct jornada_ts *jornada_ts;  	struct input_dev *input_dev; @@ -127,7 +128,7 @@ static int __devinit jornada720_ts_probe(struct platform_device *pdev)  	error = request_irq(IRQ_GPIO9,  			jornada720_ts_interrupt, -			IRQF_DISABLED | IRQF_TRIGGER_RISING, +			IRQF_TRIGGER_RISING,  			"HP7XX Touchscreen driver", pdev);  	if (error) {  		printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n"); @@ -143,18 +144,16 @@ static int __devinit jornada720_ts_probe(struct platform_device *pdev)   fail2:  	free_irq(IRQ_GPIO9, pdev);   fail1: -	platform_set_drvdata(pdev, NULL);  	input_free_device(input_dev);  	kfree(jornada_ts);  	return error;  } -static int __devexit jornada720_ts_remove(struct platform_device *pdev) +static int jornada720_ts_remove(struct platform_device *pdev)  {  	struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);  	free_irq(IRQ_GPIO9, pdev); -	platform_set_drvdata(pdev, NULL);  	input_unregister_device(jornada_ts->dev);  	kfree(jornada_ts); @@ -166,22 +165,10 @@ MODULE_ALIAS("platform:jornada_ts");  static struct platform_driver jornada720_ts_driver = {  	.probe		= jornada720_ts_probe, -	.remove		= __devexit_p(jornada720_ts_remove), +	.remove		= jornada720_ts_remove,  	.driver		= {  		.name	= "jornada_ts",  		.owner	= THIS_MODULE,  	},  }; - -static int __init jornada720_ts_init(void) -{ -	return platform_driver_register(&jornada720_ts_driver); -} - -static void __exit jornada720_ts_exit(void) -{ -	platform_driver_unregister(&jornada720_ts_driver); -} - -module_init(jornada720_ts_init); -module_exit(jornada720_ts_exit); +module_platform_driver(jornada720_ts_driver); diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c index dcf803f5a1f..bb47d3442a3 100644 --- a/drivers/input/touchscreen/lpc32xx_ts.c +++ b/drivers/input/touchscreen/lpc32xx_ts.c @@ -15,13 +15,13 @@   */  #include <linux/platform_device.h> -#include <linux/init.h>  #include <linux/input.h>  #include <linux/interrupt.h>  #include <linux/module.h>  #include <linux/clk.h>  #include <linux/io.h>  #include <linux/slab.h> +#include <linux/of.h>  /*   * Touchscreen controller register offsets @@ -202,7 +202,7 @@ static void lpc32xx_ts_close(struct input_dev *dev)  	lpc32xx_stop_tsc(tsc);  } -static int __devinit lpc32xx_ts_probe(struct platform_device *pdev) +static int lpc32xx_ts_probe(struct platform_device *pdev)  {  	struct lpc32xx_tsc *tsc;  	struct input_dev *input; @@ -276,7 +276,7 @@ static int __devinit lpc32xx_ts_probe(struct platform_device *pdev)  	input_set_drvdata(input, tsc);  	error = request_irq(tsc->irq, lpc32xx_ts_interrupt, -			    IRQF_DISABLED, pdev->name, tsc); +			    0, pdev->name, tsc);  	if (error) {  		dev_err(&pdev->dev, "failed requesting interrupt\n");  		goto err_put_clock; @@ -308,7 +308,7 @@ err_free_mem:  	return error;  } -static int __devexit lpc32xx_ts_remove(struct platform_device *pdev) +static int lpc32xx_ts_remove(struct platform_device *pdev)  {  	struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev);  	struct resource *res; @@ -383,27 +383,25 @@ static const struct dev_pm_ops lpc32xx_ts_pm_ops = {  #define LPC32XX_TS_PM_OPS NULL  #endif +#ifdef CONFIG_OF +static const struct of_device_id lpc32xx_tsc_of_match[] = { +	{ .compatible = "nxp,lpc3220-tsc", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_tsc_of_match); +#endif +  static struct platform_driver lpc32xx_ts_driver = {  	.probe		= lpc32xx_ts_probe, -	.remove		= __devexit_p(lpc32xx_ts_remove), +	.remove		= lpc32xx_ts_remove,  	.driver		= {  		.name	= MOD_NAME,  		.owner	= THIS_MODULE,  		.pm	= LPC32XX_TS_PM_OPS, +		.of_match_table = of_match_ptr(lpc32xx_tsc_of_match),  	},  }; - -static int __init lpc32xx_ts_init(void) -{ -	return platform_driver_register(&lpc32xx_ts_driver); -} -module_init(lpc32xx_ts_init); - -static void __exit lpc32xx_ts_exit(void) -{ -	platform_driver_unregister(&lpc32xx_ts_driver); -} -module_exit(lpc32xx_ts_exit); +module_platform_driver(lpc32xx_ts_driver);  MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");  MODULE_DESCRIPTION("LPC32XX TSC Driver"); diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index b6b8b1c7ece..0786010d7ed 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -25,7 +25,6 @@  #include <linux/module.h>  #include <linux/moduleparam.h>  #include <linux/kernel.h> -#include <linux/init.h>  #include <linux/delay.h>  #include <linux/irq.h>  #include <linux/interrupt.h> @@ -157,9 +156,9 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)  			x, y, p);  		/* are samples valid */ -		if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X || -		    (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y || -		    (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES) +		if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X || +		    (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y || +		    (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)  			goto up;  		/* coordinate is good */ @@ -219,7 +218,7 @@ static int wm97xx_acc_startup(struct wm97xx *wm)  		}  		wm->pen_irq = gpio_to_irq(irq); -		set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH); +		irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);  	} else /* pen irq not supported */  		pen_int = 0; @@ -302,19 +301,7 @@ static struct platform_driver mainstone_wm97xx_driver = {  		.name = "wm97xx-touch",  	},  }; - -static int __init mainstone_wm97xx_init(void) -{ -	return platform_driver_register(&mainstone_wm97xx_driver); -} - -static void __exit mainstone_wm97xx_exit(void) -{ -	platform_driver_unregister(&mainstone_wm97xx_driver); -} - -module_init(mainstone_wm97xx_init); -module_exit(mainstone_wm97xx_exit); +module_platform_driver(mainstone_wm97xx_driver);  /* Module information */  MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c new file mode 100644 index 00000000000..a68ec142ee9 --- /dev/null +++ b/drivers/input/touchscreen/max11801_ts.c @@ -0,0 +1,242 @@ +/* + * Driver for MAXI MAX11801 - A Resistive touch screen controller with + * i2c interface + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Zhang Jiejing <jiejing.zhang@freescale.com> + * + * Based on mcs5000_ts.c + * + * 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 driver aims to support the series of MAXI touch chips max11801 + * through max11803. The main difference between these 4 chips can be + * found in the table below: + * ----------------------------------------------------- + * | CHIP     |  AUTO MODE SUPPORT(FIFO) | INTERFACE    | + * |----------------------------------------------------| + * | max11800 |  YES                     |   SPI        | + * | max11801 |  YES                     |   I2C        | + * | max11802 |  NO                      |   SPI        | + * | max11803 |  NO                      |   I2C        | + * ------------------------------------------------------ + * + * Currently, this driver only supports max11801. + * + * Data Sheet: + * http://www.maxim-ic.com/datasheet/index.mvp/id/5943 + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/bitops.h> + +/* Register Address define */ +#define GENERNAL_STATUS_REG		0x00 +#define GENERNAL_CONF_REG		0x01 +#define MESURE_RES_CONF_REG		0x02 +#define MESURE_AVER_CONF_REG		0x03 +#define ADC_SAMPLE_TIME_CONF_REG	0x04 +#define PANEL_SETUPTIME_CONF_REG	0x05 +#define DELAY_CONVERSION_CONF_REG	0x06 +#define TOUCH_DETECT_PULLUP_CONF_REG	0x07 +#define AUTO_MODE_TIME_CONF_REG		0x08 /* only for max11800/max11801 */ +#define APERTURE_CONF_REG		0x09 /* only for max11800/max11801 */ +#define AUX_MESURE_CONF_REG		0x0a +#define OP_MODE_CONF_REG		0x0b + +/* FIFO is found only in max11800 and max11801 */ +#define FIFO_RD_CMD			(0x50 << 1) +#define MAX11801_FIFO_INT		(1 << 2) +#define MAX11801_FIFO_OVERFLOW		(1 << 3) + +#define XY_BUFSIZE			4 +#define XY_BUF_OFFSET			4 + +#define MAX11801_MAX_X			0xfff +#define MAX11801_MAX_Y			0xfff + +#define MEASURE_TAG_OFFSET		2 +#define MEASURE_TAG_MASK		(3 << MEASURE_TAG_OFFSET) +#define EVENT_TAG_OFFSET		0 +#define EVENT_TAG_MASK			(3 << EVENT_TAG_OFFSET) +#define MEASURE_X_TAG			(0 << MEASURE_TAG_OFFSET) +#define MEASURE_Y_TAG			(1 << MEASURE_TAG_OFFSET) + +/* These are the state of touch event state machine */ +enum { +	EVENT_INIT, +	EVENT_MIDDLE, +	EVENT_RELEASE, +	EVENT_FIFO_END +}; + +struct max11801_data { +	struct i2c_client		*client; +	struct input_dev		*input_dev; +}; + +static u8 read_register(struct i2c_client *client, int addr) +{ +	/* XXX: The chip ignores LSB of register address */ +	return i2c_smbus_read_byte_data(client, addr << 1); +} + +static int max11801_write_reg(struct i2c_client *client, int addr, int data) +{ +	/* XXX: The chip ignores LSB of register address */ +	return i2c_smbus_write_byte_data(client, addr << 1, data); +} + +static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id) +{ +	struct max11801_data *data = dev_id; +	struct i2c_client *client = data->client; +	int status, i, ret; +	u8 buf[XY_BUFSIZE]; +	int x = -1; +	int y = -1; + +	status = read_register(data->client, GENERNAL_STATUS_REG); + +	if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) { +		status = read_register(data->client, GENERNAL_STATUS_REG); + +		ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD, +						    XY_BUFSIZE, buf); + +		/* +		 * We should get 4 bytes buffer that contains X,Y +		 * and event tag +		 */ +		if (ret < XY_BUFSIZE) +			goto out; + +		for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) { +			if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG) +				x = (buf[i] << XY_BUF_OFFSET) + +				    (buf[i + 1] >> XY_BUF_OFFSET); +			else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG) +				y = (buf[i] << XY_BUF_OFFSET) + +				    (buf[i + 1] >> XY_BUF_OFFSET); +		} + +		if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK)) +			goto out; + +		switch (buf[1] & EVENT_TAG_MASK) { +		case EVENT_INIT: +			/* fall through */ +		case EVENT_MIDDLE: +			input_report_abs(data->input_dev, ABS_X, x); +			input_report_abs(data->input_dev, ABS_Y, y); +			input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1); +			input_sync(data->input_dev); +			break; + +		case EVENT_RELEASE: +			input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0); +			input_sync(data->input_dev); +			break; + +		case EVENT_FIFO_END: +			break; +		} +	} +out: +	return IRQ_HANDLED; +} + +static void max11801_ts_phy_init(struct max11801_data *data) +{ +	struct i2c_client *client = data->client; + +	/* Average X,Y, take 16 samples, average eight media sample */ +	max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff); +	/* X,Y panel setup time set to 20us */ +	max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11); +	/* Rough pullup time (2uS), Fine pullup time (10us)  */ +	max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10); +	/* Auto mode init period = 5ms , scan period = 5ms*/ +	max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa); +	/* Aperture X,Y set to +- 4LSB */ +	max11801_write_reg(client, APERTURE_CONF_REG, 0x33); +	/* Enable Power, enable Automode, enable Aperture, enable Average X,Y */ +	max11801_write_reg(client, OP_MODE_CONF_REG, 0x36); +} + +static int max11801_ts_probe(struct i2c_client *client, +				       const struct i2c_device_id *id) +{ +	struct max11801_data *data; +	struct input_dev *input_dev; +	int error; + +	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); +	input_dev = devm_input_allocate_device(&client->dev); +	if (!data || !input_dev) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	data->client = client; +	data->input_dev = input_dev; + +	input_dev->name = "max11801_ts"; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &client->dev; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); +	input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0); +	input_set_drvdata(input_dev, data); + +	max11801_ts_phy_init(data); + +	error = devm_request_threaded_irq(&client->dev, client->irq, NULL, +					  max11801_ts_interrupt, +					  IRQF_TRIGGER_LOW | IRQF_ONESHOT, +					  "max11801_ts", data); +	if (error) { +		dev_err(&client->dev, "Failed to register interrupt\n"); +		return error; +	} + +	error = input_register_device(data->input_dev); +	if (error) +		return error; + +	i2c_set_clientdata(client, data); +	return 0; +} + +static const struct i2c_device_id max11801_ts_id[] = { +	{"max11801", 0}, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, max11801_ts_id); + +static struct i2c_driver max11801_ts_driver = { +	.driver = { +		.name	= "max11801_ts", +		.owner	= THIS_MODULE, +	}, +	.id_table	= max11801_ts_id, +	.probe		= max11801_ts_probe, +}; + +module_i2c_driver(max11801_ts_driver); + +MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); +MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index c5bc62d85bb..d6f099c47f8 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -35,17 +35,18 @@ MODULE_PARM_DESC(sample_tolerance,  struct mc13783_ts_priv {  	struct input_dev *idev; -	struct mc13783 *mc13783; +	struct mc13xxx *mc13xxx;  	struct delayed_work work;  	struct workqueue_struct *workq;  	unsigned int sample[4]; +	struct mc13xxx_ts_platform_data *touch;  };  static irqreturn_t mc13783_ts_handler(int irq, void *data)  {  	struct mc13783_ts_priv *priv = data; -	mc13783_irq_ack(priv->mc13783, irq); +	mc13xxx_irq_ack(priv->mc13xxx, irq);  	/*  	 * Kick off reading coordinates. Note that if work happens already @@ -121,11 +122,13 @@ static void mc13783_ts_work(struct work_struct *work)  {  	struct mc13783_ts_priv *priv =  		container_of(work, struct mc13783_ts_priv, work.work); -	unsigned int mode = MC13783_ADC_MODE_TS; +	unsigned int mode = MC13XXX_ADC_MODE_TS;  	unsigned int channel = 12; -	if (mc13783_adc_do_conversion(priv->mc13783, -				mode, channel, priv->sample) == 0) +	if (mc13xxx_adc_do_conversion(priv->mc13xxx, +				mode, channel, +				priv->touch->ato, priv->touch->atox, +				priv->sample) == 0)  		mc13783_ts_report_sample(priv);  } @@ -134,21 +137,21 @@ static int mc13783_ts_open(struct input_dev *dev)  	struct mc13783_ts_priv *priv = input_get_drvdata(dev);  	int ret; -	mc13783_lock(priv->mc13783); +	mc13xxx_lock(priv->mc13xxx); -	mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TS); +	mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS); -	ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_TS, +	ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS,  		mc13783_ts_handler, MC13783_TS_NAME, priv);  	if (ret)  		goto out; -	ret = mc13783_reg_rmw(priv->mc13783, MC13783_ADC0, -			MC13783_ADC0_TSMOD_MASK, MC13783_ADC0_TSMOD0); +	ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0, +			MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0);  	if (ret) -		mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv); +		mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);  out: -	mc13783_unlock(priv->mc13783); +	mc13xxx_unlock(priv->mc13xxx);  	return ret;  } @@ -156,11 +159,11 @@ static void mc13783_ts_close(struct input_dev *dev)  {  	struct mc13783_ts_priv *priv = input_get_drvdata(dev); -	mc13783_lock(priv->mc13783); -	mc13783_reg_rmw(priv->mc13783, MC13783_ADC0, -			MC13783_ADC0_TSMOD_MASK, 0); -	mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv); -	mc13783_unlock(priv->mc13783); +	mc13xxx_lock(priv->mc13xxx); +	mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0, +			MC13XXX_ADC0_TSMOD_MASK, 0); +	mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv); +	mc13xxx_unlock(priv->mc13xxx);  	cancel_delayed_work_sync(&priv->work);  } @@ -177,8 +180,14 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)  		goto err_free_mem;  	INIT_DELAYED_WORK(&priv->work, mc13783_ts_work); -	priv->mc13783 = dev_get_drvdata(pdev->dev.parent); +	priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);  	priv->idev = idev; +	priv->touch = dev_get_platdata(&pdev->dev); +	if (!priv->touch) { +		dev_err(&pdev->dev, "missing platform data\n"); +		ret = -ENODEV; +		goto err_free_mem; +	}  	/*  	 * We need separate workqueue because mc13783_adc_do_conversion @@ -220,12 +229,10 @@ err_free_mem:  	return ret;  } -static int __devexit mc13783_ts_remove(struct platform_device *pdev) +static int mc13783_ts_remove(struct platform_device *pdev)  {  	struct mc13783_ts_priv *priv = platform_get_drvdata(pdev); -	platform_set_drvdata(pdev, NULL); -  	destroy_workqueue(priv->workq);  	input_unregister_device(priv->idev);  	kfree(priv); @@ -234,24 +241,14 @@ static int __devexit mc13783_ts_remove(struct platform_device *pdev)  }  static struct platform_driver mc13783_ts_driver = { -	.remove		= __devexit_p(mc13783_ts_remove), +	.remove		= mc13783_ts_remove,  	.driver		= {  		.owner	= THIS_MODULE,  		.name	= MC13783_TS_NAME,  	},  }; -static int __init mc13783_ts_init(void) -{ -	return platform_driver_probe(&mc13783_ts_driver, &mc13783_ts_probe); -} -module_init(mc13783_ts_init); - -static void __exit mc13783_ts_exit(void) -{ -	platform_driver_unregister(&mc13783_ts_driver); -} -module_exit(mc13783_ts_exit); +module_platform_driver_probe(mc13783_ts_driver, mc13783_ts_probe);  MODULE_DESCRIPTION("MC13783 input touchscreen driver");  MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 6ee9940aaf5..00510a9836b 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -14,7 +14,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/i2c.h>  #include <linux/i2c/mcs.h>  #include <linux/interrupt.h> @@ -162,10 +161,9 @@ static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) +static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data, +				 const struct mcs_platform_data *platform_data)  { -	const struct mcs_platform_data *platform_data = -		data->platform_data;  	struct i2c_client *client = data->client;  	/* Touch reset & sleep mode */ @@ -187,29 +185,33 @@ static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data)  			OP_MODE_ACTIVE | REPORT_RATE_80);  } -static int __devinit mcs5000_ts_probe(struct i2c_client *client, -		const struct i2c_device_id *id) +static int mcs5000_ts_probe(struct i2c_client *client, +			    const struct i2c_device_id *id)  { +	const struct mcs_platform_data *pdata;  	struct mcs5000_ts_data *data;  	struct input_dev *input_dev; -	int ret; +	int error; -	if (!client->dev.platform_data) +	pdata = dev_get_platdata(&client->dev); +	if (!pdata)  		return -EINVAL; -	data = kzalloc(sizeof(struct mcs5000_ts_data), GFP_KERNEL); -	input_dev = input_allocate_device(); -	if (!data || !input_dev) { +	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); +	if (!data) {  		dev_err(&client->dev, "Failed to allocate memory\n"); -		ret = -ENOMEM; -		goto err_free_mem; +		return -ENOMEM;  	}  	data->client = client; -	data->input_dev = input_dev; -	data->platform_data = client->dev.platform_data; -	input_dev->name = "MELPAS MCS-5000 Touchscreen"; +	input_dev = devm_input_allocate_device(&client->dev); +	if (!input_dev) { +		dev_err(&client->dev, "Failed to allocate input device\n"); +		return -ENOMEM; +	} + +	input_dev->name = "MELFAS MCS-5000 Touchscreen";  	input_dev->id.bustype = BUS_I2C;  	input_dev->dev.parent = &client->dev; @@ -220,68 +222,57 @@ static int __devinit mcs5000_ts_probe(struct i2c_client *client,  	input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);  	input_set_drvdata(input_dev, data); +	data->input_dev = input_dev; -	if (data->platform_data->cfg_pin) -		data->platform_data->cfg_pin(); - -	ret = request_threaded_irq(client->irq, NULL, mcs5000_ts_interrupt, -			IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mcs5000_ts", data); +	if (pdata->cfg_pin) +		pdata->cfg_pin(); -	if (ret < 0) { +	error = devm_request_threaded_irq(&client->dev, client->irq, +					  NULL, mcs5000_ts_interrupt, +					  IRQF_TRIGGER_LOW | IRQF_ONESHOT, +					  "mcs5000_ts", data); +	if (error) {  		dev_err(&client->dev, "Failed to register interrupt\n"); -		goto err_free_mem; +		return error;  	} -	ret = input_register_device(data->input_dev); -	if (ret < 0) -		goto err_free_irq; +	error = input_register_device(data->input_dev); +	if (error) { +		dev_err(&client->dev, "Failed to register input device\n"); +		return error; +	} -	mcs5000_ts_phys_init(data); +	mcs5000_ts_phys_init(data, pdata);  	i2c_set_clientdata(client, data);  	return 0; - -err_free_irq: -	free_irq(client->irq, data); -err_free_mem: -	input_free_device(input_dev); -	kfree(data); -	return ret; -} - -static int __devexit mcs5000_ts_remove(struct i2c_client *client) -{ -	struct mcs5000_ts_data *data = i2c_get_clientdata(client); - -	free_irq(client->irq, data); -	input_unregister_device(data->input_dev); -	kfree(data); - -	return 0;  }  #ifdef CONFIG_PM -static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg) +static int mcs5000_ts_suspend(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev); +  	/* Touch sleep mode */  	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);  	return 0;  } -static int mcs5000_ts_resume(struct i2c_client *client) +static int mcs5000_ts_resume(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct mcs5000_ts_data *data = i2c_get_clientdata(client); +	const struct mcs_platform_data *pdata = dev_get_platdata(dev); -	mcs5000_ts_phys_init(data); +	mcs5000_ts_phys_init(data, pdata);  	return 0;  } -#else -#define mcs5000_ts_suspend	NULL -#define mcs5000_ts_resume	NULL  #endif +static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); +  static const struct i2c_device_id mcs5000_ts_id[] = {  	{ "mcs5000_ts", 0 },  	{ } @@ -290,27 +281,14 @@ MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);  static struct i2c_driver mcs5000_ts_driver = {  	.probe		= mcs5000_ts_probe, -	.remove		= __devexit_p(mcs5000_ts_remove), -	.suspend	= mcs5000_ts_suspend, -	.resume		= mcs5000_ts_resume,  	.driver = {  		.name = "mcs5000_ts", +		.pm   = &mcs5000_ts_pm,  	},  	.id_table	= mcs5000_ts_id,  }; -static int __init mcs5000_ts_init(void) -{ -	return i2c_add_driver(&mcs5000_ts_driver); -} - -static void __exit mcs5000_ts_exit(void) -{ -	i2c_del_driver(&mcs5000_ts_driver); -} - -module_init(mcs5000_ts_init); -module_exit(mcs5000_ts_exit); +module_i2c_driver(mcs5000_ts_driver);  /* Module information */  MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c index defe5dd3627..c038db93e2c 100644 --- a/drivers/input/touchscreen/migor_ts.c +++ b/drivers/input/touchscreen/migor_ts.c @@ -23,6 +23,7 @@  #include <linux/kernel.h>  #include <linux/input.h>  #include <linux/interrupt.h> +#include <linux/pm.h>  #include <linux/slab.h>  #include <asm/io.h>  #include <linux/i2c.h> @@ -35,7 +36,6 @@  struct migor_ts_priv {  	struct i2c_client *client;  	struct input_dev *input; -	struct delayed_work work;  	int irq;  }; @@ -43,15 +43,24 @@ static const u_int8_t migor_ts_ena_seq[17] = { 0x33, 0x22, 0x11,  					       0x01, 0x06, 0x07, };  static const u_int8_t migor_ts_dis_seq[17] = { }; -static void migor_ts_poscheck(struct work_struct *work) +static irqreturn_t migor_ts_isr(int irq, void *dev_id)  { -	struct migor_ts_priv *priv = container_of(work, -						  struct migor_ts_priv, -						  work.work); +	struct migor_ts_priv *priv = dev_id;  	unsigned short xpos, ypos;  	unsigned char event;  	u_int8_t buf[16]; +	/* +	 * The touch screen controller chip is hooked up to the CPU +	 * using I2C and a single interrupt line. The interrupt line +	 * is pulled low whenever someone taps the screen. To deassert +	 * the interrupt line we need to acknowledge the interrupt by +	 * communicating with the controller over the slow i2c bus. +	 * +	 * Since I2C bus controller may sleep we are using threaded +	 * IRQ here. +	 */ +  	memset(buf, 0, sizeof(buf));  	/* Set Index 0 */ @@ -71,41 +80,25 @@ static void migor_ts_poscheck(struct work_struct *work)  	xpos = ((buf[11] & 0x03) << 8 | buf[10]);  	event = buf[12]; -	if (event == EVENT_PENDOWN || event == EVENT_REPEAT) { +	switch (event) { +	case EVENT_PENDOWN: +	case EVENT_REPEAT:  		input_report_key(priv->input, BTN_TOUCH, 1);  		input_report_abs(priv->input, ABS_X, ypos); /*X-Y swap*/  		input_report_abs(priv->input, ABS_Y, xpos);  		input_sync(priv->input); -	} else if (event == EVENT_PENUP) { +		break; + +	case EVENT_PENUP:  		input_report_key(priv->input, BTN_TOUCH, 0);  		input_sync(priv->input); +		break;  	} - out: -	enable_irq(priv->irq); -} - -static irqreturn_t migor_ts_isr(int irq, void *dev_id) -{ -	struct migor_ts_priv *priv = dev_id; - -	/* the touch screen controller chip is hooked up to the cpu -	 * using i2c and a single interrupt line. the interrupt line -	 * is pulled low whenever someone taps the screen. to deassert -	 * the interrupt line we need to acknowledge the interrupt by -	 * communicating with the controller over the slow i2c bus. -	 * -	 * we can't acknowledge from interrupt context since the i2c -	 * bus controller may sleep, so we just disable the interrupt -	 * here and handle the acknowledge using delayed work. -	 */ - -	disable_irq_nosync(irq); -	schedule_delayed_work(&priv->work, HZ / 20); + out:  	return IRQ_HANDLED;  } -  static int migor_ts_open(struct input_dev *dev)  {  	struct migor_ts_priv *priv = input_get_drvdata(dev); @@ -130,15 +123,6 @@ static void migor_ts_close(struct input_dev *dev)  	disable_irq(priv->irq); -	/* cancel pending work and wait for migor_ts_poscheck() to finish */ -	if (cancel_delayed_work_sync(&priv->work)) { -		/* -		 * if migor_ts_poscheck was canceled we need to enable IRQ -		 * here to balance disable done in migor_ts_isr. -		 */ -		enable_irq(priv->irq); -	} -  	/* disable controller */  	i2c_master_send(client, migor_ts_dis_seq, sizeof(migor_ts_dis_seq)); @@ -153,23 +137,20 @@ static int migor_ts_probe(struct i2c_client *client,  	int error;  	priv = kzalloc(sizeof(*priv), GFP_KERNEL); -	if (!priv) { -		dev_err(&client->dev, "failed to allocate driver data\n"); -		error = -ENOMEM; -		goto err0; -	} - -	dev_set_drvdata(&client->dev, priv); -  	input = input_allocate_device(); -	if (!input) { -		dev_err(&client->dev, "Failed to allocate input device.\n"); +	if (!priv || !input) { +		dev_err(&client->dev, "failed to allocate memory\n");  		error = -ENOMEM; -		goto err1; +		goto err_free_mem;  	} +	priv->client = client; +	priv->input = input; +	priv->irq = client->irq; +  	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); -	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + +	__set_bit(BTN_TOUCH, input->keybit);  	input_set_abs_params(input, ABS_X, 95, 955, 0, 0);  	input_set_abs_params(input, ABS_Y, 85, 935, 0, 0); @@ -183,39 +164,34 @@ static int migor_ts_probe(struct i2c_client *client,  	input_set_drvdata(input, priv); -	priv->client = client; -	priv->input = input; -	INIT_DELAYED_WORK(&priv->work, migor_ts_poscheck); -	priv->irq = client->irq; - -	error = input_register_device(input); -	if (error) -		goto err1; - -	error = request_irq(priv->irq, migor_ts_isr, IRQF_TRIGGER_LOW, -			    client->name, priv); +	error = request_threaded_irq(priv->irq, NULL, migor_ts_isr, +                                     IRQF_TRIGGER_LOW | IRQF_ONESHOT, +                                     client->name, priv);  	if (error) {  		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); -		goto err2; +		goto err_free_mem;  	} +	error = input_register_device(input); +	if (error) +		goto err_free_irq; + +	i2c_set_clientdata(client, priv);  	device_init_wakeup(&client->dev, 1); +  	return 0; - err2: -	input_unregister_device(input); -	input = NULL; /* so we dont try to free it below */ - err1: + err_free_irq: +	free_irq(priv->irq, priv); + err_free_mem:  	input_free_device(input);  	kfree(priv); - err0: -	dev_set_drvdata(&client->dev, NULL);  	return error;  }  static int migor_ts_remove(struct i2c_client *client)  { -	struct migor_ts_priv *priv = dev_get_drvdata(&client->dev); +	struct migor_ts_priv *priv = i2c_get_clientdata(client);  	free_irq(priv->irq, priv);  	input_unregister_device(priv->input); @@ -226,9 +202,10 @@ static int migor_ts_remove(struct i2c_client *client)  	return 0;  } -static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg) +static int migor_ts_suspend(struct device *dev)  { -	struct migor_ts_priv *priv = dev_get_drvdata(&client->dev); +	struct i2c_client *client = to_i2c_client(dev); +	struct migor_ts_priv *priv = i2c_get_clientdata(client);  	if (device_may_wakeup(&client->dev))  		enable_irq_wake(priv->irq); @@ -236,9 +213,10 @@ static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg)  	return 0;  } -static int migor_ts_resume(struct i2c_client *client) +static int migor_ts_resume(struct device *dev)  { -	struct migor_ts_priv *priv = dev_get_drvdata(&client->dev); +	struct i2c_client *client = to_i2c_client(dev); +	struct migor_ts_priv *priv = i2c_get_clientdata(client);  	if (device_may_wakeup(&client->dev))  		disable_irq_wake(priv->irq); @@ -246,6 +224,8 @@ static int migor_ts_resume(struct i2c_client *client)  	return 0;  } +static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume); +  static const struct i2c_device_id migor_ts_id[] = {  	{ "migor_ts", 0 },  	{ } @@ -255,27 +235,15 @@ MODULE_DEVICE_TABLE(i2c, migor_ts);  static struct i2c_driver migor_ts_driver = {  	.driver = {  		.name = "migor_ts", +		.pm = &migor_ts_pm,  	},  	.probe = migor_ts_probe,  	.remove = migor_ts_remove, -	.suspend = migor_ts_suspend, -	.resume = migor_ts_resume,  	.id_table = migor_ts_id,  }; -static int __init migor_ts_init(void) -{ -	return i2c_add_driver(&migor_ts_driver); -} - -static void __exit migor_ts_exit(void) -{ -	i2c_del_driver(&migor_ts_driver); -} +module_i2c_driver(migor_ts_driver);  MODULE_DESCRIPTION("MigoR Touchscreen driver");  MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");  MODULE_LICENSE("GPL"); - -module_init(migor_ts_init); -module_exit(migor_ts_exit); diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c new file mode 100644 index 00000000000..372bbf7658f --- /dev/null +++ b/drivers/input/touchscreen/mms114.c @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * 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/module.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/i2c.h> +#include <linux/i2c/mms114.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +/* Write only registers */ +#define MMS114_MODE_CONTROL		0x01 +#define MMS114_OPERATION_MODE_MASK	0xE +#define MMS114_ACTIVE			(1 << 1) + +#define MMS114_XY_RESOLUTION_H		0x02 +#define MMS114_X_RESOLUTION		0x03 +#define MMS114_Y_RESOLUTION		0x04 +#define MMS114_CONTACT_THRESHOLD	0x05 +#define MMS114_MOVING_THRESHOLD		0x06 + +/* Read only registers */ +#define MMS114_PACKET_SIZE		0x0F +#define MMS114_INFOMATION		0x10 +#define MMS114_TSP_REV			0xF0 + +/* Minimum delay time is 50us between stop and start signal of i2c */ +#define MMS114_I2C_DELAY		50 + +/* 200ms needs after power on */ +#define MMS114_POWERON_DELAY		200 + +/* Touchscreen absolute values */ +#define MMS114_MAX_AREA			0xff + +#define MMS114_MAX_TOUCH		10 +#define MMS114_PACKET_NUM		8 + +/* Touch type */ +#define MMS114_TYPE_NONE		0 +#define MMS114_TYPE_TOUCHSCREEN		1 +#define MMS114_TYPE_TOUCHKEY		2 + +struct mms114_data { +	struct i2c_client	*client; +	struct input_dev	*input_dev; +	struct regulator	*core_reg; +	struct regulator	*io_reg; +	const struct mms114_platform_data	*pdata; + +	/* Use cache data for mode control register(write only) */ +	u8			cache_mode_control; +}; + +struct mms114_touch { +	u8 id:4, reserved_bit4:1, type:2, pressed:1; +	u8 x_hi:4, y_hi:4; +	u8 x_lo; +	u8 y_lo; +	u8 width; +	u8 strength; +	u8 reserved[2]; +} __packed; + +static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, +			     unsigned int len, u8 *val) +{ +	struct i2c_client *client = data->client; +	struct i2c_msg xfer[2]; +	u8 buf = reg & 0xff; +	int error; + +	if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL) +		BUG(); + +	/* Write register: use repeated start */ +	xfer[0].addr = client->addr; +	xfer[0].flags = I2C_M_TEN | I2C_M_NOSTART; +	xfer[0].len = 1; +	xfer[0].buf = &buf; + +	/* Read data */ +	xfer[1].addr = client->addr; +	xfer[1].flags = I2C_M_RD; +	xfer[1].len = len; +	xfer[1].buf = val; + +	error = i2c_transfer(client->adapter, xfer, 2); +	if (error != 2) { +		dev_err(&client->dev, +			"%s: i2c transfer failed (%d)\n", __func__, error); +		return error < 0 ? error : -EIO; +	} +	udelay(MMS114_I2C_DELAY); + +	return 0; +} + +static int mms114_read_reg(struct mms114_data *data, unsigned int reg) +{ +	u8 val; +	int error; + +	if (reg == MMS114_MODE_CONTROL) +		return data->cache_mode_control; + +	error = __mms114_read_reg(data, reg, 1, &val); +	return error < 0 ? error : val; +} + +static int mms114_write_reg(struct mms114_data *data, unsigned int reg, +			    unsigned int val) +{ +	struct i2c_client *client = data->client; +	u8 buf[2]; +	int error; + +	buf[0] = reg & 0xff; +	buf[1] = val & 0xff; + +	error = i2c_master_send(client, buf, 2); +	if (error != 2) { +		dev_err(&client->dev, +			"%s: i2c send failed (%d)\n", __func__, error); +		return error < 0 ? error : -EIO; +	} +	udelay(MMS114_I2C_DELAY); + +	if (reg == MMS114_MODE_CONTROL) +		data->cache_mode_control = val; + +	return 0; +} + +static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch) +{ +	const struct mms114_platform_data *pdata = data->pdata; +	struct i2c_client *client = data->client; +	struct input_dev *input_dev = data->input_dev; +	unsigned int id; +	unsigned int x; +	unsigned int y; + +	if (touch->id > MMS114_MAX_TOUCH) { +		dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); +		return; +	} + +	if (touch->type != MMS114_TYPE_TOUCHSCREEN) { +		dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type); +		return; +	} + +	id = touch->id - 1; +	x = touch->x_lo | touch->x_hi << 8; +	y = touch->y_lo | touch->y_hi << 8; +	if (x > pdata->x_size || y > pdata->y_size) { +		dev_dbg(&client->dev, +			"Wrong touch coordinates (%d, %d)\n", x, y); +		return; +	} + +	if (pdata->x_invert) +		x = pdata->x_size - x; +	if (pdata->y_invert) +		y = pdata->y_size - y; + +	dev_dbg(&client->dev, +		"id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n", +		id, touch->type, touch->pressed, +		x, y, touch->width, touch->strength); + +	input_mt_slot(input_dev, id); +	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed); + +	if (touch->pressed) { +		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width); +		input_report_abs(input_dev, ABS_MT_POSITION_X, x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, y); +		input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength); +	} +} + +static irqreturn_t mms114_interrupt(int irq, void *dev_id) +{ +	struct mms114_data *data = dev_id; +	struct input_dev *input_dev = data->input_dev; +	struct mms114_touch touch[MMS114_MAX_TOUCH]; +	int packet_size; +	int touch_size; +	int index; +	int error; + +	mutex_lock(&input_dev->mutex); +	if (!input_dev->users) { +		mutex_unlock(&input_dev->mutex); +		goto out; +	} +	mutex_unlock(&input_dev->mutex); + +	packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); +	if (packet_size <= 0) +		goto out; + +	touch_size = packet_size / MMS114_PACKET_NUM; + +	error = __mms114_read_reg(data, MMS114_INFOMATION, packet_size, +			(u8 *)touch); +	if (error < 0) +		goto out; + +	for (index = 0; index < touch_size; index++) +		mms114_process_mt(data, touch + index); + +	input_mt_report_pointer_emulation(data->input_dev, true); +	input_sync(data->input_dev); + +out: +	return IRQ_HANDLED; +} + +static int mms114_set_active(struct mms114_data *data, bool active) +{ +	int val; + +	val = mms114_read_reg(data, MMS114_MODE_CONTROL); +	if (val < 0) +		return val; + +	val &= ~MMS114_OPERATION_MODE_MASK; + +	/* If active is false, sleep mode */ +	if (active) +		val |= MMS114_ACTIVE; + +	return mms114_write_reg(data, MMS114_MODE_CONTROL, val); +} + +static int mms114_get_version(struct mms114_data *data) +{ +	struct device *dev = &data->client->dev; +	u8 buf[6]; +	int error; + +	error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); +	if (error < 0) +		return error; + +	dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", +		 buf[0], buf[1], buf[3]); + +	return 0; +} + +static int mms114_setup_regs(struct mms114_data *data) +{ +	const struct mms114_platform_data *pdata = data->pdata; +	int val; +	int error; + +	error = mms114_get_version(data); +	if (error < 0) +		return error; + +	error = mms114_set_active(data, true); +	if (error < 0) +		return error; + +	val = (pdata->x_size >> 8) & 0xf; +	val |= ((pdata->y_size >> 8) & 0xf) << 4; +	error = mms114_write_reg(data, MMS114_XY_RESOLUTION_H, val); +	if (error < 0) +		return error; + +	val = pdata->x_size & 0xff; +	error = mms114_write_reg(data, MMS114_X_RESOLUTION, val); +	if (error < 0) +		return error; + +	val = pdata->y_size & 0xff; +	error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val); +	if (error < 0) +		return error; + +	if (pdata->contact_threshold) { +		error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD, +				pdata->contact_threshold); +		if (error < 0) +			return error; +	} + +	if (pdata->moving_threshold) { +		error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD, +				pdata->moving_threshold); +		if (error < 0) +			return error; +	} + +	return 0; +} + +static int mms114_start(struct mms114_data *data) +{ +	struct i2c_client *client = data->client; +	int error; + +	error = regulator_enable(data->core_reg); +	if (error) { +		dev_err(&client->dev, "Failed to enable avdd: %d\n", error); +		return error; +	} + +	error = regulator_enable(data->io_reg); +	if (error) { +		dev_err(&client->dev, "Failed to enable vdd: %d\n", error); +		regulator_disable(data->core_reg); +		return error; +	} + +	mdelay(MMS114_POWERON_DELAY); + +	error = mms114_setup_regs(data); +	if (error < 0) { +		regulator_disable(data->io_reg); +		regulator_disable(data->core_reg); +		return error; +	} + +	if (data->pdata->cfg_pin) +		data->pdata->cfg_pin(true); + +	enable_irq(client->irq); + +	return 0; +} + +static void mms114_stop(struct mms114_data *data) +{ +	struct i2c_client *client = data->client; +	int error; + +	disable_irq(client->irq); + +	if (data->pdata->cfg_pin) +		data->pdata->cfg_pin(false); + +	error = regulator_disable(data->io_reg); +	if (error) +		dev_warn(&client->dev, "Failed to disable vdd: %d\n", error); + +	error = regulator_disable(data->core_reg); +	if (error) +		dev_warn(&client->dev, "Failed to disable avdd: %d\n", error); +} + +static int mms114_input_open(struct input_dev *dev) +{ +	struct mms114_data *data = input_get_drvdata(dev); + +	return mms114_start(data); +} + +static void mms114_input_close(struct input_dev *dev) +{ +	struct mms114_data *data = input_get_drvdata(dev); + +	mms114_stop(data); +} + +#ifdef CONFIG_OF +static struct mms114_platform_data *mms114_parse_dt(struct device *dev) +{ +	struct mms114_platform_data *pdata; +	struct device_node *np = dev->of_node; + +	if (!np) +		return NULL; + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) { +		dev_err(dev, "failed to allocate platform data\n"); +		return NULL; +	} + +	if (of_property_read_u32(np, "x-size", &pdata->x_size)) { +		dev_err(dev, "failed to get x-size property\n"); +		return NULL; +	}; + +	if (of_property_read_u32(np, "y-size", &pdata->y_size)) { +		dev_err(dev, "failed to get y-size property\n"); +		return NULL; +	}; + +	of_property_read_u32(np, "contact-threshold", +				&pdata->contact_threshold); +	of_property_read_u32(np, "moving-threshold", +				&pdata->moving_threshold); + +	if (of_find_property(np, "x-invert", NULL)) +		pdata->x_invert = true; +	if (of_find_property(np, "y-invert", NULL)) +		pdata->y_invert = true; + +	return pdata; +} +#else +static inline struct mms114_platform_data *mms114_parse_dt(struct device *dev) +{ +	return NULL; +} +#endif + +static int mms114_probe(struct i2c_client *client, +				  const struct i2c_device_id *id) +{ +	const struct mms114_platform_data *pdata; +	struct mms114_data *data; +	struct input_dev *input_dev; +	int error; + +	pdata = dev_get_platdata(&client->dev); +	if (!pdata) +		pdata = mms114_parse_dt(&client->dev); + +	if (!pdata) { +		dev_err(&client->dev, "Need platform data\n"); +		return -EINVAL; +	} + +	if (!i2c_check_functionality(client->adapter, +				I2C_FUNC_PROTOCOL_MANGLING)) { +		dev_err(&client->dev, +			"Need i2c bus that supports protocol mangling\n"); +		return -ENODEV; +	} + +	data = devm_kzalloc(&client->dev, sizeof(struct mms114_data), +			    GFP_KERNEL); +	input_dev = devm_input_allocate_device(&client->dev); +	if (!data || !input_dev) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	data->client = client; +	data->input_dev = input_dev; +	data->pdata = pdata; + +	input_dev->name = "MELFAS MMS114 Touchscreen"; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &client->dev; +	input_dev->open = mms114_input_open; +	input_dev->close = mms114_input_close; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); +	input_set_abs_params(input_dev, ABS_X, 0, data->pdata->x_size, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0); + +	/* For multi touch */ +	input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0); +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, +			     0, MMS114_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, +			     0, data->pdata->x_size, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, +			     0, data->pdata->y_size, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + +	input_set_drvdata(input_dev, data); +	i2c_set_clientdata(client, data); + +	data->core_reg = devm_regulator_get(&client->dev, "avdd"); +	if (IS_ERR(data->core_reg)) { +		error = PTR_ERR(data->core_reg); +		dev_err(&client->dev, +			"Unable to get the Core regulator (%d)\n", error); +		return error; +	} + +	data->io_reg = devm_regulator_get(&client->dev, "vdd"); +	if (IS_ERR(data->io_reg)) { +		error = PTR_ERR(data->io_reg); +		dev_err(&client->dev, +			"Unable to get the IO regulator (%d)\n", error); +		return error; +	} + +	error = devm_request_threaded_irq(&client->dev, client->irq, NULL, +			mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +			dev_name(&client->dev), data); +	if (error) { +		dev_err(&client->dev, "Failed to register interrupt\n"); +		return error; +	} +	disable_irq(client->irq); + +	error = input_register_device(data->input_dev); +	if (error) { +		dev_err(&client->dev, "Failed to register input device\n"); +		return error; +	} + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mms114_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct mms114_data *data = i2c_get_clientdata(client); +	struct input_dev *input_dev = data->input_dev; +	int id; + +	/* Release all touch */ +	for (id = 0; id < MMS114_MAX_TOUCH; id++) { +		input_mt_slot(input_dev, id); +		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); +	} + +	input_mt_report_pointer_emulation(input_dev, true); +	input_sync(input_dev); + +	mutex_lock(&input_dev->mutex); +	if (input_dev->users) +		mms114_stop(data); +	mutex_unlock(&input_dev->mutex); + +	return 0; +} + +static int mms114_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct mms114_data *data = i2c_get_clientdata(client); +	struct input_dev *input_dev = data->input_dev; +	int error; + +	mutex_lock(&input_dev->mutex); +	if (input_dev->users) { +		error = mms114_start(data); +		if (error < 0) { +			mutex_unlock(&input_dev->mutex); +			return error; +		} +	} +	mutex_unlock(&input_dev->mutex); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); + +static const struct i2c_device_id mms114_id[] = { +	{ "mms114", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, mms114_id); + +#ifdef CONFIG_OF +static const struct of_device_id mms114_dt_match[] = { +	{ .compatible = "melfas,mms114" }, +	{ } +}; +#endif + +static struct i2c_driver mms114_driver = { +	.driver = { +		.name	= "mms114", +		.owner	= THIS_MODULE, +		.pm	= &mms114_pm_ops, +		.of_match_table = of_match_ptr(mms114_dt_match), +	}, +	.probe		= mms114_probe, +	.id_table	= mms114_id, +}; + +module_i2c_driver(mms114_driver); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c index 9077228418b..9b5552a2616 100644 --- a/drivers/input/touchscreen/mtouch.c +++ b/drivers/input/touchscreen/mtouch.c @@ -21,7 +21,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"MicroTouch serial touchscreen driver" @@ -202,19 +201,4 @@ static struct serio_driver mtouch_drv = {  	.disconnect	= mtouch_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init mtouch_init(void) -{ -	return serio_register_driver(&mtouch_drv); -} - -static void __exit mtouch_exit(void) -{ -	serio_unregister_driver(&mtouch_drv); -} - -module_init(mtouch_init); -module_exit(mtouch_exit); +module_serio_driver(mtouch_drv); diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c new file mode 100644 index 00000000000..f8f9b84230b --- /dev/null +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -0,0 +1,45 @@ +/* + *  Generic DT helper functions for touchscreen devices + * + *  Copyright (c) 2014 Sebastian Reichel <sre@kernel.org> + * + *  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/of.h> +#include <linux/input.h> +#include <linux/input/touchscreen.h> + +/** + * touchscreen_parse_of_params - parse common touchscreen DT properties + * @dev: device that should be parsed + * + * This function parses common DT properties for touchscreens and setups the + * input device accordingly. The function keeps previously setuped default + * values if no value is specified via DT. + */ +void touchscreen_parse_of_params(struct input_dev *dev) +{ +	struct device_node *np = dev->dev.parent->of_node; +	struct input_absinfo *absinfo; + +	input_alloc_absinfo(dev); +	if (!dev->absinfo) +		return; + +	absinfo = &dev->absinfo[ABS_X]; +	of_property_read_u32(np, "touchscreen-size-x", &absinfo->maximum); +	of_property_read_u32(np, "touchscreen-fuzz-x", &absinfo->fuzz); + +	absinfo = &dev->absinfo[ABS_Y]; +	of_property_read_u32(np, "touchscreen-size-y", &absinfo->maximum); +	of_property_read_u32(np, "touchscreen-fuzz-y", &absinfo->fuzz); + +	absinfo = &dev->absinfo[ABS_PRESSURE]; +	of_property_read_u32(np, "touchscreen-max-pressure", &absinfo->maximum); +	of_property_read_u32(np, "touchscreen-fuzz-pressure", &absinfo->fuzz); +} +EXPORT_SYMBOL(touchscreen_parse_of_params); diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c index ea6ef16e59b..cff2376817e 100644 --- a/drivers/input/touchscreen/pcap_ts.c +++ b/drivers/input/touchscreen/pcap_ts.c @@ -11,7 +11,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/fs.h>  #include <linux/string.h>  #include <linux/slab.h> @@ -137,7 +136,7 @@ static void pcap_ts_close(struct input_dev *dev)  				pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);  } -static int __devinit pcap_ts_probe(struct platform_device *pdev) +static int pcap_ts_probe(struct platform_device *pdev)  {  	struct input_dev *input_dev;  	struct pcap_ts *pcap_ts; @@ -202,7 +201,7 @@ fail:  	return err;  } -static int __devexit pcap_ts_remove(struct platform_device *pdev) +static int pcap_ts_remove(struct platform_device *pdev)  {  	struct pcap_ts *pcap_ts = platform_get_drvdata(pdev); @@ -245,26 +244,14 @@ static const struct dev_pm_ops pcap_ts_pm_ops = {  static struct platform_driver pcap_ts_driver = {  	.probe		= pcap_ts_probe, -	.remove		= __devexit_p(pcap_ts_remove), +	.remove		= pcap_ts_remove,  	.driver		= {  		.name	= "pcap-ts",  		.owner	= THIS_MODULE,  		.pm	= PCAP_TS_PM_OPS,  	},  }; - -static int __init pcap_ts_init(void) -{ -	return platform_driver_register(&pcap_ts_driver); -} - -static void __exit pcap_ts_exit(void) -{ -	platform_driver_unregister(&pcap_ts_driver); -} - -module_init(pcap_ts_init); -module_exit(pcap_ts_exit); +module_platform_driver(pcap_ts_driver);  MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");  MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c index c7f9cebebbb..417d8737926 100644 --- a/drivers/input/touchscreen/penmount.c +++ b/drivers/input/touchscreen/penmount.c @@ -2,6 +2,7 @@   * Penmount serial touchscreen driver   *   * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com> + * Copyright (c) 2011 John Sung <penmount.touch@gmail.com>   *   * Based on ELO driver (drivers/input/touchscreen/elo.c)   * Copyright (c) 2004 Vojtech Pavlik @@ -18,12 +19,13 @@  #include <linux/module.h>  #include <linux/slab.h>  #include <linux/input.h> +#include <linux/input/mt.h>  #include <linux/serio.h> -#include <linux/init.h> -#define DRIVER_DESC	"Penmount serial touchscreen driver" +#define DRIVER_DESC	"PenMount serial touchscreen driver"  MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>"); +MODULE_AUTHOR("John Sung <penmount.touch@gmail.com>");  MODULE_DESCRIPTION(DRIVER_DESC);  MODULE_LICENSE("GPL"); @@ -31,7 +33,19 @@ MODULE_LICENSE("GPL");   * Definitions & global arrays.   */ -#define	PM_MAX_LENGTH	5 +#define	PM_MAX_LENGTH	6 +#define	PM_MAX_MTSLOT	16 +#define	PM_3000_MTSLOT	2 +#define	PM_6250_MTSLOT	12 + +/* + * Multi-touch slot + */ + +struct mt_slot { +	unsigned short x, y; +	bool active; /* is the touch valid? */ +};  /*   * Per-touchscreen data. @@ -43,25 +57,119 @@ struct pm {  	int idx;  	unsigned char data[PM_MAX_LENGTH];  	char phys[32]; +	unsigned char packetsize; +	unsigned char maxcontacts; +	struct mt_slot slots[PM_MAX_MTSLOT]; +	void (*parse_packet)(struct pm *);  }; -static irqreturn_t pm_interrupt(struct serio *serio, -		unsigned char data, unsigned int flags) +/* + * pm_mtevent() sends mt events and also emulates pointer movement + */ + +static void pm_mtevent(struct pm *pm, struct input_dev *input) +{ +	int i; + +	for (i = 0; i < pm->maxcontacts; ++i) { +		input_mt_slot(input, i); +		input_mt_report_slot_state(input, MT_TOOL_FINGER, +				pm->slots[i].active); +		if (pm->slots[i].active) { +			input_event(input, EV_ABS, ABS_MT_POSITION_X, pm->slots[i].x); +			input_event(input, EV_ABS, ABS_MT_POSITION_Y, pm->slots[i].y); +		} +	} + +	input_mt_report_pointer_emulation(input, true); +	input_sync(input); +} + +/* + * pm_checkpacket() checks if data packet is valid + */ + +static bool pm_checkpacket(unsigned char *packet) +{ +	int total = 0; +	int i; + +	for (i = 0; i < 5; i++) +		total += packet[i]; + +	return packet[5] == (unsigned char)~(total & 0xff); +} + +static void pm_parse_9000(struct pm *pm)  { -	struct pm *pm = serio_get_drvdata(serio);  	struct input_dev *dev = pm->dev; -	pm->data[pm->idx] = data; +	if ((pm->data[0] & 0x80) && pm->packetsize == ++pm->idx) { +		input_report_abs(dev, ABS_X, pm->data[1] * 128 + pm->data[2]); +		input_report_abs(dev, ABS_Y, pm->data[3] * 128 + pm->data[4]); +		input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40)); +		input_sync(dev); +		pm->idx = 0; +	} +} -	if (pm->data[0] & 0x80) { -		if (PM_MAX_LENGTH == ++pm->idx) { -			input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]); -			input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]); -			input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40)); +static void pm_parse_6000(struct pm *pm) +{ +	struct input_dev *dev = pm->dev; + +	if ((pm->data[0] & 0xbf) == 0x30 && pm->packetsize == ++pm->idx) { +		if (pm_checkpacket(pm->data)) { +			input_report_abs(dev, ABS_X, +					pm->data[2] * 256 + pm->data[1]); +			input_report_abs(dev, ABS_Y, +					pm->data[4] * 256 + pm->data[3]); +			input_report_key(dev, BTN_TOUCH, pm->data[0] & 0x40);  			input_sync(dev); -			pm->idx = 0;  		} +		pm->idx = 0;  	} +} + +static void pm_parse_3000(struct pm *pm) +{ +	struct input_dev *dev = pm->dev; + +	if ((pm->data[0] & 0xce) == 0x40 && pm->packetsize == ++pm->idx) { +		if (pm_checkpacket(pm->data)) { +			int slotnum = pm->data[0] & 0x0f; +			pm->slots[slotnum].active = pm->data[0] & 0x30; +			pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1]; +			pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3]; +			pm_mtevent(pm, dev); +		} +		pm->idx = 0; +	} +} + +static void pm_parse_6250(struct pm *pm) +{ +	struct input_dev *dev = pm->dev; + +	if ((pm->data[0] & 0xb0) == 0x30 && pm->packetsize == ++pm->idx) { +		if (pm_checkpacket(pm->data)) { +			int slotnum = pm->data[0] & 0x0f; +			pm->slots[slotnum].active = pm->data[0] & 0x40; +			pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1]; +			pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3]; +			pm_mtevent(pm, dev); +		} +		pm->idx = 0; +	} +} + +static irqreturn_t pm_interrupt(struct serio *serio, +		unsigned char data, unsigned int flags) +{ +	struct pm *pm = serio_get_drvdata(serio); + +	pm->data[pm->idx] = data; + +	pm->parse_packet(pm);  	return IRQ_HANDLED;  } @@ -74,17 +182,17 @@ static void pm_disconnect(struct serio *serio)  {  	struct pm *pm = serio_get_drvdata(serio); -	input_get_device(pm->dev); -	input_unregister_device(pm->dev);  	serio_close(serio); -	serio_set_drvdata(serio, NULL); -	input_put_device(pm->dev); + +	input_unregister_device(pm->dev);  	kfree(pm); + +	serio_set_drvdata(serio, NULL);  }  /*   * pm_connect() is the routine that is called when someone adds a - * new serio device that supports Gunze protocol and registers it as + * new serio device that supports PenMount protocol and registers it as   * an input device.   */ @@ -92,6 +200,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)  {  	struct pm *pm;  	struct input_dev *input_dev; +	int max_x, max_y;  	int err;  	pm = kzalloc(sizeof(struct pm), GFP_KERNEL); @@ -104,8 +213,9 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)  	pm->serio = serio;  	pm->dev = input_dev;  	snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys); +	pm->maxcontacts = 1; -	input_dev->name = "Penmount Serial TouchScreen"; +	input_dev->name = "PenMount Serial TouchScreen";  	input_dev->phys = pm->phys;  	input_dev->id.bustype = BUS_RS232;  	input_dev->id.vendor = SERIO_PENMOUNT; @@ -113,10 +223,52 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)  	input_dev->id.version = 0x0100;  	input_dev->dev.parent = &serio->dev; -        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); -        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); -        input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0); -        input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0); +	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + +	switch (serio->id.id) { +	default: +	case 0: +		pm->packetsize = 5; +		pm->parse_packet = pm_parse_9000; +		input_dev->id.product = 0x9000; +		max_x = max_y = 0x3ff; +		break; + +	case 1: +		pm->packetsize = 6; +		pm->parse_packet = pm_parse_6000; +		input_dev->id.product = 0x6000; +		max_x = max_y = 0x3ff; +		break; + +	case 2: +		pm->packetsize = 6; +		pm->parse_packet = pm_parse_3000; +		input_dev->id.product = 0x3000; +		max_x = max_y = 0x7ff; +		pm->maxcontacts = PM_3000_MTSLOT; +		break; + +	case 3: +		pm->packetsize = 6; +		pm->parse_packet = pm_parse_6250; +		input_dev->id.product = 0x6250; +		max_x = max_y = 0x3ff; +		pm->maxcontacts = PM_6250_MTSLOT; +		break; +	} + +	input_set_abs_params(pm->dev, ABS_X, 0, max_x, 0, 0); +	input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0); + +	if (pm->maxcontacts > 1) { +		input_mt_init_slots(pm->dev, pm->maxcontacts, 0); +		input_set_abs_params(pm->dev, +				     ABS_MT_POSITION_X, 0, max_x, 0, 0); +		input_set_abs_params(pm->dev, +				     ABS_MT_POSITION_Y, 0, max_y, 0, 0); +	}  	serio_set_drvdata(serio, pm); @@ -155,7 +307,7 @@ MODULE_DEVICE_TABLE(serio, pm_serio_ids);  static struct serio_driver pm_drv = {  	.driver		= { -		.name	= "penmountlpc", +		.name	= "serio-penmount",  	},  	.description	= DRIVER_DESC,  	.id_table	= pm_serio_ids, @@ -164,19 +316,4 @@ static struct serio_driver pm_drv = {  	.disconnect	= pm_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init pm_init(void) -{ -	return serio_register_driver(&pm_drv); -} - -static void __exit pm_exit(void) -{ -	serio_unregister_driver(&pm_drv); -} - -module_init(pm_init); -module_exit(pm_exit); +module_serio_driver(pm_drv); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c new file mode 100644 index 00000000000..19c6c0fdc94 --- /dev/null +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -0,0 +1,441 @@ +/* + * Driver for Pixcir I2C touchscreen controllers. + * + * Copyright (C) 2010-2011 Pixcir, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/pixcir_ts.h> +#include <linux/gpio.h> + +struct pixcir_i2c_ts_data { +	struct i2c_client *client; +	struct input_dev *input; +	const struct pixcir_ts_platform_data *chip; +	bool running; +}; + +static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) +{ +	struct pixcir_i2c_ts_data *tsdata = data; +	u8 rdbuf[10], wrbuf[1] = { 0 }; +	u8 touch; +	int ret; + +	ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf)); +	if (ret != sizeof(wrbuf)) { +		dev_err(&tsdata->client->dev, +			"%s: i2c_master_send failed(), ret=%d\n", +			__func__, ret); +		return; +	} + +	ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf)); +	if (ret != sizeof(rdbuf)) { +		dev_err(&tsdata->client->dev, +			"%s: i2c_master_recv failed(), ret=%d\n", +			__func__, ret); +		return; +	} + +	touch = rdbuf[0]; +	if (touch) { +		u16 posx1 = (rdbuf[3] << 8) | rdbuf[2]; +		u16 posy1 = (rdbuf[5] << 8) | rdbuf[4]; +		u16 posx2 = (rdbuf[7] << 8) | rdbuf[6]; +		u16 posy2 = (rdbuf[9] << 8) | rdbuf[8]; + +		input_report_key(tsdata->input, BTN_TOUCH, 1); +		input_report_abs(tsdata->input, ABS_X, posx1); +		input_report_abs(tsdata->input, ABS_Y, posy1); + +		input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1); +		input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1); +		input_mt_sync(tsdata->input); + +		if (touch == 2) { +			input_report_abs(tsdata->input, +					 ABS_MT_POSITION_X, posx2); +			input_report_abs(tsdata->input, +					 ABS_MT_POSITION_Y, posy2); +			input_mt_sync(tsdata->input); +		} +	} else { +		input_report_key(tsdata->input, BTN_TOUCH, 0); +	} + +	input_sync(tsdata->input); +} + +static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) +{ +	struct pixcir_i2c_ts_data *tsdata = dev_id; +	const struct pixcir_ts_platform_data *pdata = tsdata->chip; + +	while (tsdata->running) { +		pixcir_ts_poscheck(tsdata); + +		if (gpio_get_value(pdata->gpio_attb)) +			break; + +		msleep(20); +	} + +	return IRQ_HANDLED; +} + +static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, +				 enum pixcir_power_mode mode) +{ +	struct device *dev = &ts->client->dev; +	int ret; + +	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", +			__func__, PIXCIR_REG_POWER_MODE, ret); +		return ret; +	} + +	ret &= ~PIXCIR_POWER_MODE_MASK; +	ret |= mode; + +	/* Always AUTO_IDLE */ +	ret |= PIXCIR_POWER_ALLOW_IDLE; + +	ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret); +	if (ret < 0) { +		dev_err(dev, "%s: can't write reg 0x%x : %d\n", +			__func__, PIXCIR_REG_POWER_MODE, ret); +		return ret; +	} + +	return 0; +} + +/* + * Set the interrupt mode for the device i.e. ATTB line behaviour + * + * @polarity : 1 for active high, 0 for active low. + */ +static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts, +			       enum pixcir_int_mode mode, bool polarity) +{ +	struct device *dev = &ts->client->dev; +	int ret; + +	ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE); +	if (ret < 0) { +		dev_err(dev, "%s: can't read reg 0x%x : %d\n", +			__func__, PIXCIR_REG_INT_MODE, ret); +		return ret; +	} + +	ret &= ~PIXCIR_INT_MODE_MASK; +	ret |= mode; + +	if (polarity) +		ret |= PIXCIR_INT_POL_HIGH; +	else +		ret &= ~PIXCIR_INT_POL_HIGH; + +	ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret); +	if (ret < 0) { +		dev_err(dev, "%s: can't write reg 0x%x : %d\n", +			__func__, PIXCIR_REG_INT_MODE, ret); +		return ret; +	} + +	return 0; +} + +/* + * Enable/disable interrupt generation + */ +static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable) +{ +	struct device *dev = &ts->client->dev; +	int ret; + +	ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE); +	if (ret < 0) { +		dev_err(dev, "%s: can't read reg 0x%x : %d\n", +			__func__, PIXCIR_REG_INT_MODE, ret); +		return ret; +	} + +	if (enable) +		ret |= PIXCIR_INT_ENABLE; +	else +		ret &= ~PIXCIR_INT_ENABLE; + +	ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret); +	if (ret < 0) { +		dev_err(dev, "%s: can't write reg 0x%x : %d\n", +			__func__, PIXCIR_REG_INT_MODE, ret); +		return ret; +	} + +	return 0; +} + +static int pixcir_start(struct pixcir_i2c_ts_data *ts) +{ +	struct device *dev = &ts->client->dev; +	int error; + +	/* LEVEL_TOUCH interrupt with active low polarity */ +	error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0); +	if (error) { +		dev_err(dev, "Failed to set interrupt mode: %d\n", error); +		return error; +	} + +	ts->running = true; +	mb();	/* Update status before IRQ can fire */ + +	/* enable interrupt generation */ +	error = pixcir_int_enable(ts, true); +	if (error) { +		dev_err(dev, "Failed to enable interrupt generation: %d\n", +			error); +		return error; +	} + +	return 0; +} + +static int pixcir_stop(struct pixcir_i2c_ts_data *ts) +{ +	int error; + +	/* Disable interrupt generation */ +	error = pixcir_int_enable(ts, false); +	if (error) { +		dev_err(&ts->client->dev, +			"Failed to disable interrupt generation: %d\n", +			error); +		return error; +	} + +	/* Exit ISR if running, no more report parsing */ +	ts->running = false; +	mb();	/* update status before we synchronize irq */ + +	/* Wait till running ISR is complete */ +	synchronize_irq(ts->client->irq); + +	return 0; +} + +static int pixcir_input_open(struct input_dev *dev) +{ +	struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev); + +	return pixcir_start(ts); +} + +static void pixcir_input_close(struct input_dev *dev) +{ +	struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev); + +	pixcir_stop(ts); +} + +#ifdef CONFIG_PM_SLEEP +static int pixcir_i2c_ts_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); +	struct input_dev *input = ts->input; +	int ret = 0; + +	mutex_lock(&input->mutex); + +	if (device_may_wakeup(&client->dev)) { +		if (!input->users) { +			ret = pixcir_start(ts); +			if (ret) { +				dev_err(dev, "Failed to start\n"); +				goto unlock; +			} +		} + +		enable_irq_wake(client->irq); +	} else if (input->users) { +		ret = pixcir_stop(ts); +	} + +unlock: +	mutex_unlock(&input->mutex); + +	return ret; +} + +static int pixcir_i2c_ts_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); +	struct input_dev *input = ts->input; +	int ret = 0; + +	mutex_lock(&input->mutex); + +	if (device_may_wakeup(&client->dev)) { +		disable_irq_wake(client->irq); + +		if (!input->users) { +			ret = pixcir_stop(ts); +			if (ret) { +				dev_err(dev, "Failed to stop\n"); +				goto unlock; +			} +		} +	} else if (input->users) { +		ret = pixcir_start(ts); +	} + +unlock: +	mutex_unlock(&input->mutex); + +	return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, +			 pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume); + +static int pixcir_i2c_ts_probe(struct i2c_client *client, +					 const struct i2c_device_id *id) +{ +	const struct pixcir_ts_platform_data *pdata = +			dev_get_platdata(&client->dev); +	struct device *dev = &client->dev; +	struct pixcir_i2c_ts_data *tsdata; +	struct input_dev *input; +	int error; + +	if (!pdata) { +		dev_err(&client->dev, "platform data not defined\n"); +		return -EINVAL; +	} + +	if (!gpio_is_valid(pdata->gpio_attb)) { +		dev_err(dev, "Invalid gpio_attb in pdata\n"); +		return -EINVAL; +	} + +	tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); +	if (!tsdata) +		return -ENOMEM; + +	input = devm_input_allocate_device(dev); +	if (!input) { +		dev_err(dev, "Failed to allocate input device\n"); +		return -ENOMEM; +	} + +	tsdata->client = client; +	tsdata->input = input; +	tsdata->chip = pdata; + +	input->name = client->name; +	input->id.bustype = BUS_I2C; +	input->open = pixcir_input_open; +	input->close = pixcir_input_close; +	input->dev.parent = &client->dev; + +	__set_bit(EV_KEY, input->evbit); +	__set_bit(EV_ABS, input->evbit); +	__set_bit(BTN_TOUCH, input->keybit); +	input_set_abs_params(input, ABS_X, 0, pdata->x_max, 0, 0); +	input_set_abs_params(input, ABS_Y, 0, pdata->y_max, 0, 0); +	input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); +	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); + +	input_set_drvdata(input, tsdata); + +	error = devm_gpio_request_one(dev, pdata->gpio_attb, +				      GPIOF_DIR_IN, "pixcir_i2c_attb"); +	if (error) { +		dev_err(dev, "Failed to request ATTB gpio\n"); +		return error; +	} + +	error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr, +					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +					  client->name, tsdata); +	if (error) { +		dev_err(dev, "failed to request irq %d\n", client->irq); +		return error; +	} + +	/* Always be in IDLE mode to save power, device supports auto wake */ +	error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE); +	if (error) { +		dev_err(dev, "Failed to set IDLE mode\n"); +		return error; +	} + +	/* Stop device till opened */ +	error = pixcir_stop(tsdata); +	if (error) +		return error; + +	error = input_register_device(input); +	if (error) +		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; +} + +static const struct i2c_device_id pixcir_i2c_ts_id[] = { +	{ "pixcir_ts", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id); + +static struct i2c_driver pixcir_i2c_ts_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "pixcir_ts", +		.pm	= &pixcir_dev_pm_ops, +	}, +	.probe		= pixcir_i2c_ts_probe, +	.remove		= pixcir_i2c_ts_remove, +	.id_table	= pixcir_i2c_ts_id, +}; + +module_i2c_driver(pixcir_i2c_ts_driver); + +MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>"); +MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c deleted file mode 100644 index 66b26ad3032..00000000000 --- a/drivers/input/touchscreen/qt602240_ts.c +++ /dev/null @@ -1,1401 +0,0 @@ -/* - * AT42QT602240/ATMXT224 Touchscreen driver - * - * Copyright (C) 2010 Samsung Electronics Co.Ltd - * Author: Joonyoung Shim <jy0922.shim@samsung.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. - * - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/firmware.h> -#include <linux/i2c.h> -#include <linux/i2c/qt602240_ts.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/slab.h> - -/* Version */ -#define QT602240_VER_20			20 -#define QT602240_VER_21			21 -#define QT602240_VER_22			22 - -/* Slave addresses */ -#define QT602240_APP_LOW		0x4a -#define QT602240_APP_HIGH		0x4b -#define QT602240_BOOT_LOW		0x24 -#define QT602240_BOOT_HIGH		0x25 - -/* Firmware */ -#define QT602240_FW_NAME		"qt602240.fw" - -/* Registers */ -#define QT602240_FAMILY_ID		0x00 -#define QT602240_VARIANT_ID		0x01 -#define QT602240_VERSION		0x02 -#define QT602240_BUILD			0x03 -#define QT602240_MATRIX_X_SIZE		0x04 -#define QT602240_MATRIX_Y_SIZE		0x05 -#define QT602240_OBJECT_NUM		0x06 -#define QT602240_OBJECT_START		0x07 - -#define QT602240_OBJECT_SIZE		6 - -/* Object types */ -#define QT602240_DEBUG_DIAGNOSTIC	37 -#define QT602240_GEN_MESSAGE		5 -#define QT602240_GEN_COMMAND		6 -#define QT602240_GEN_POWER		7 -#define QT602240_GEN_ACQUIRE		8 -#define QT602240_TOUCH_MULTI		9 -#define QT602240_TOUCH_KEYARRAY		15 -#define QT602240_TOUCH_PROXIMITY	23 -#define QT602240_PROCI_GRIPFACE		20 -#define QT602240_PROCG_NOISE		22 -#define QT602240_PROCI_ONETOUCH		24 -#define QT602240_PROCI_TWOTOUCH		27 -#define QT602240_SPT_COMMSCONFIG	18	/* firmware ver 21 over */ -#define QT602240_SPT_GPIOPWM		19 -#define QT602240_SPT_SELFTEST		25 -#define QT602240_SPT_CTECONFIG		28 -#define QT602240_SPT_USERDATA		38	/* firmware ver 21 over */ - -/* QT602240_GEN_COMMAND field */ -#define QT602240_COMMAND_RESET		0 -#define QT602240_COMMAND_BACKUPNV	1 -#define QT602240_COMMAND_CALIBRATE	2 -#define QT602240_COMMAND_REPORTALL	3 -#define QT602240_COMMAND_DIAGNOSTIC	5 - -/* QT602240_GEN_POWER field */ -#define QT602240_POWER_IDLEACQINT	0 -#define QT602240_POWER_ACTVACQINT	1 -#define QT602240_POWER_ACTV2IDLETO	2 - -/* QT602240_GEN_ACQUIRE field */ -#define QT602240_ACQUIRE_CHRGTIME	0 -#define QT602240_ACQUIRE_TCHDRIFT	2 -#define QT602240_ACQUIRE_DRIFTST	3 -#define QT602240_ACQUIRE_TCHAUTOCAL	4 -#define QT602240_ACQUIRE_SYNC		5 -#define QT602240_ACQUIRE_ATCHCALST	6 -#define QT602240_ACQUIRE_ATCHCALSTHR	7 - -/* QT602240_TOUCH_MULTI field */ -#define QT602240_TOUCH_CTRL		0 -#define QT602240_TOUCH_XORIGIN		1 -#define QT602240_TOUCH_YORIGIN		2 -#define QT602240_TOUCH_XSIZE		3 -#define QT602240_TOUCH_YSIZE		4 -#define QT602240_TOUCH_BLEN		6 -#define QT602240_TOUCH_TCHTHR		7 -#define QT602240_TOUCH_TCHDI		8 -#define QT602240_TOUCH_ORIENT		9 -#define QT602240_TOUCH_MOVHYSTI		11 -#define QT602240_TOUCH_MOVHYSTN		12 -#define QT602240_TOUCH_NUMTOUCH		14 -#define QT602240_TOUCH_MRGHYST		15 -#define QT602240_TOUCH_MRGTHR		16 -#define QT602240_TOUCH_AMPHYST		17 -#define QT602240_TOUCH_XRANGE_LSB	18 -#define QT602240_TOUCH_XRANGE_MSB	19 -#define QT602240_TOUCH_YRANGE_LSB	20 -#define QT602240_TOUCH_YRANGE_MSB	21 -#define QT602240_TOUCH_XLOCLIP		22 -#define QT602240_TOUCH_XHICLIP		23 -#define QT602240_TOUCH_YLOCLIP		24 -#define QT602240_TOUCH_YHICLIP		25 -#define QT602240_TOUCH_XEDGECTRL	26 -#define QT602240_TOUCH_XEDGEDIST	27 -#define QT602240_TOUCH_YEDGECTRL	28 -#define QT602240_TOUCH_YEDGEDIST	29 -#define QT602240_TOUCH_JUMPLIMIT	30	/* firmware ver 22 over */ - -/* QT602240_PROCI_GRIPFACE field */ -#define QT602240_GRIPFACE_CTRL		0 -#define QT602240_GRIPFACE_XLOGRIP	1 -#define QT602240_GRIPFACE_XHIGRIP	2 -#define QT602240_GRIPFACE_YLOGRIP	3 -#define QT602240_GRIPFACE_YHIGRIP	4 -#define QT602240_GRIPFACE_MAXTCHS	5 -#define QT602240_GRIPFACE_SZTHR1	7 -#define QT602240_GRIPFACE_SZTHR2	8 -#define QT602240_GRIPFACE_SHPTHR1	9 -#define QT602240_GRIPFACE_SHPTHR2	10 -#define QT602240_GRIPFACE_SUPEXTTO	11 - -/* QT602240_PROCI_NOISE field */ -#define QT602240_NOISE_CTRL		0 -#define QT602240_NOISE_OUTFLEN		1 -#define QT602240_NOISE_GCAFUL_LSB	3 -#define QT602240_NOISE_GCAFUL_MSB	4 -#define QT602240_NOISE_GCAFLL_LSB	5 -#define QT602240_NOISE_GCAFLL_MSB	6 -#define QT602240_NOISE_ACTVGCAFVALID	7 -#define QT602240_NOISE_NOISETHR		8 -#define QT602240_NOISE_FREQHOPSCALE	10 -#define QT602240_NOISE_FREQ0		11 -#define QT602240_NOISE_FREQ1		12 -#define QT602240_NOISE_FREQ2		13 -#define QT602240_NOISE_FREQ3		14 -#define QT602240_NOISE_FREQ4		15 -#define QT602240_NOISE_IDLEGCAFVALID	16 - -/* QT602240_SPT_COMMSCONFIG */ -#define QT602240_COMMS_CTRL		0 -#define QT602240_COMMS_CMD		1 - -/* QT602240_SPT_CTECONFIG field */ -#define QT602240_CTE_CTRL		0 -#define QT602240_CTE_CMD		1 -#define QT602240_CTE_MODE		2 -#define QT602240_CTE_IDLEGCAFDEPTH	3 -#define QT602240_CTE_ACTVGCAFDEPTH	4 -#define QT602240_CTE_VOLTAGE		5	/* firmware ver 21 over */ - -#define QT602240_VOLTAGE_DEFAULT	2700000 -#define QT602240_VOLTAGE_STEP		10000 - -/* Define for QT602240_GEN_COMMAND */ -#define QT602240_BOOT_VALUE		0xa5 -#define QT602240_BACKUP_VALUE		0x55 -#define QT602240_BACKUP_TIME		25	/* msec */ -#define QT602240_RESET_TIME		65	/* msec */ - -#define QT602240_FWRESET_TIME		175	/* msec */ - -/* Command to unlock bootloader */ -#define QT602240_UNLOCK_CMD_MSB		0xaa -#define QT602240_UNLOCK_CMD_LSB		0xdc - -/* Bootloader mode status */ -#define QT602240_WAITING_BOOTLOAD_CMD	0xc0	/* valid 7 6 bit only */ -#define QT602240_WAITING_FRAME_DATA	0x80	/* valid 7 6 bit only */ -#define QT602240_FRAME_CRC_CHECK	0x02 -#define QT602240_FRAME_CRC_FAIL		0x03 -#define QT602240_FRAME_CRC_PASS		0x04 -#define QT602240_APP_CRC_FAIL		0x40	/* valid 7 8 bit only */ -#define QT602240_BOOT_STATUS_MASK	0x3f - -/* Touch status */ -#define QT602240_SUPPRESS		(1 << 1) -#define QT602240_AMP			(1 << 2) -#define QT602240_VECTOR			(1 << 3) -#define QT602240_MOVE			(1 << 4) -#define QT602240_RELEASE		(1 << 5) -#define QT602240_PRESS			(1 << 6) -#define QT602240_DETECT			(1 << 7) - -/* Touchscreen absolute values */ -#define QT602240_MAX_XC			0x3ff -#define QT602240_MAX_YC			0x3ff -#define QT602240_MAX_AREA		0xff - -#define QT602240_MAX_FINGER		10 - -/* Initial register values recommended from chip vendor */ -static const u8 init_vals_ver_20[] = { -	/* QT602240_GEN_COMMAND(6) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_GEN_POWER(7) */ -	0x20, 0xff, 0x32, -	/* QT602240_GEN_ACQUIRE(8) */ -	0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, -	/* QT602240_TOUCH_MULTI(9) */ -	0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, -	0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, -	/* QT602240_TOUCH_KEYARRAY(15) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, -	/* QT602240_SPT_GPIOPWM(19) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, -	/* QT602240_PROCI_GRIPFACE(20) */ -	0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, -	0x1e, 0x00, -	/* QT602240_PROCG_NOISE(22) */ -	0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, -	0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, -	/* QT602240_TOUCH_PROXIMITY(23) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, -	/* QT602240_PROCI_ONETOUCH(24) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_SPT_SELFTEST(25) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, -	/* QT602240_PROCI_TWOTOUCH(27) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_SPT_CTECONFIG(28) */ -	0x00, 0x00, 0x00, 0x04, 0x08, -}; - -static const u8 init_vals_ver_21[] = { -	/* QT602240_GEN_COMMAND(6) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_GEN_POWER(7) */ -	0x20, 0xff, 0x32, -	/* QT602240_GEN_ACQUIRE(8) */ -	0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, -	/* QT602240_TOUCH_MULTI(9) */ -	0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, -	0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_TOUCH_KEYARRAY(15) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, -	/* QT602240_SPT_GPIOPWM(19) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_PROCI_GRIPFACE(20) */ -	0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, -	0x0f, 0x0a, -	/* QT602240_PROCG_NOISE(22) */ -	0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, -	0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, -	/* QT602240_TOUCH_PROXIMITY(23) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, -	/* QT602240_PROCI_ONETOUCH(24) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_SPT_SELFTEST(25) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, -	/* QT602240_PROCI_TWOTOUCH(27) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_SPT_CTECONFIG(28) */ -	0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - -static const u8 init_vals_ver_22[] = { -	/* QT602240_GEN_COMMAND(6) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_GEN_POWER(7) */ -	0x20, 0xff, 0x32, -	/* QT602240_GEN_ACQUIRE(8) */ -	0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, -	/* QT602240_TOUCH_MULTI(9) */ -	0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, -	0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, -	/* QT602240_TOUCH_KEYARRAY(15) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, -	/* QT602240_SPT_GPIOPWM(19) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_PROCI_GRIPFACE(20) */ -	0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, -	0x0f, 0x0a, -	/* QT602240_PROCG_NOISE(22) */ -	0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, -	0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, -	/* QT602240_TOUCH_PROXIMITY(23) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_PROCI_ONETOUCH(24) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_SPT_SELFTEST(25) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	0x00, 0x00, 0x00, 0x00, -	/* QT602240_PROCI_TWOTOUCH(27) */ -	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -	/* QT602240_SPT_CTECONFIG(28) */ -	0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - -struct qt602240_info { -	u8 family_id; -	u8 variant_id; -	u8 version; -	u8 build; -	u8 matrix_xsize; -	u8 matrix_ysize; -	u8 object_num; -}; - -struct qt602240_object { -	u8 type; -	u16 start_address; -	u8 size; -	u8 instances; -	u8 num_report_ids; - -	/* to map object and message */ -	u8 max_reportid; -}; - -struct qt602240_message { -	u8 reportid; -	u8 message[7]; -	u8 checksum; -}; - -struct qt602240_finger { -	int status; -	int x; -	int y; -	int area; -}; - -/* Each client has this additional data */ -struct qt602240_data { -	struct i2c_client *client; -	struct input_dev *input_dev; -	const struct qt602240_platform_data *pdata; -	struct qt602240_object *object_table; -	struct qt602240_info info; -	struct qt602240_finger finger[QT602240_MAX_FINGER]; -	unsigned int irq; -}; - -static bool qt602240_object_readable(unsigned int type) -{ -	switch (type) { -	case QT602240_GEN_MESSAGE: -	case QT602240_GEN_COMMAND: -	case QT602240_GEN_POWER: -	case QT602240_GEN_ACQUIRE: -	case QT602240_TOUCH_MULTI: -	case QT602240_TOUCH_KEYARRAY: -	case QT602240_TOUCH_PROXIMITY: -	case QT602240_PROCI_GRIPFACE: -	case QT602240_PROCG_NOISE: -	case QT602240_PROCI_ONETOUCH: -	case QT602240_PROCI_TWOTOUCH: -	case QT602240_SPT_COMMSCONFIG: -	case QT602240_SPT_GPIOPWM: -	case QT602240_SPT_SELFTEST: -	case QT602240_SPT_CTECONFIG: -	case QT602240_SPT_USERDATA: -		return true; -	default: -		return false; -	} -} - -static bool qt602240_object_writable(unsigned int type) -{ -	switch (type) { -	case QT602240_GEN_COMMAND: -	case QT602240_GEN_POWER: -	case QT602240_GEN_ACQUIRE: -	case QT602240_TOUCH_MULTI: -	case QT602240_TOUCH_KEYARRAY: -	case QT602240_TOUCH_PROXIMITY: -	case QT602240_PROCI_GRIPFACE: -	case QT602240_PROCG_NOISE: -	case QT602240_PROCI_ONETOUCH: -	case QT602240_PROCI_TWOTOUCH: -	case QT602240_SPT_GPIOPWM: -	case QT602240_SPT_SELFTEST: -	case QT602240_SPT_CTECONFIG: -		return true; -	default: -		return false; -	} -} - -static void qt602240_dump_message(struct device *dev, -				  struct qt602240_message *message) -{ -	dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); -	dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); -	dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); -	dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); -	dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); -	dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); -	dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); -	dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); -	dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); -} - -static int qt602240_check_bootloader(struct i2c_client *client, -				     unsigned int state) -{ -	u8 val; - -recheck: -	if (i2c_master_recv(client, &val, 1) != 1) { -		dev_err(&client->dev, "%s: i2c recv failed\n", __func__); -		return -EIO; -	} - -	switch (state) { -	case QT602240_WAITING_BOOTLOAD_CMD: -	case QT602240_WAITING_FRAME_DATA: -		val &= ~QT602240_BOOT_STATUS_MASK; -		break; -	case QT602240_FRAME_CRC_PASS: -		if (val == QT602240_FRAME_CRC_CHECK) -			goto recheck; -		break; -	default: -		return -EINVAL; -	} - -	if (val != state) { -		dev_err(&client->dev, "Unvalid bootloader mode state\n"); -		return -EINVAL; -	} - -	return 0; -} - -static int qt602240_unlock_bootloader(struct i2c_client *client) -{ -	u8 buf[2]; - -	buf[0] = QT602240_UNLOCK_CMD_LSB; -	buf[1] = QT602240_UNLOCK_CMD_MSB; - -	if (i2c_master_send(client, buf, 2) != 2) { -		dev_err(&client->dev, "%s: i2c send failed\n", __func__); -		return -EIO; -	} - -	return 0; -} - -static int qt602240_fw_write(struct i2c_client *client, -			     const u8 *data, unsigned int frame_size) -{ -	if (i2c_master_send(client, data, frame_size) != frame_size) { -		dev_err(&client->dev, "%s: i2c send failed\n", __func__); -		return -EIO; -	} - -	return 0; -} - -static int __qt602240_read_reg(struct i2c_client *client, -			       u16 reg, u16 len, void *val) -{ -	struct i2c_msg xfer[2]; -	u8 buf[2]; - -	buf[0] = reg & 0xff; -	buf[1] = (reg >> 8) & 0xff; - -	/* Write register */ -	xfer[0].addr = client->addr; -	xfer[0].flags = 0; -	xfer[0].len = 2; -	xfer[0].buf = buf; - -	/* Read data */ -	xfer[1].addr = client->addr; -	xfer[1].flags = I2C_M_RD; -	xfer[1].len = len; -	xfer[1].buf = val; - -	if (i2c_transfer(client->adapter, xfer, 2) != 2) { -		dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); -		return -EIO; -	} - -	return 0; -} - -static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) -{ -	return __qt602240_read_reg(client, reg, 1, val); -} - -static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) -{ -	u8 buf[3]; - -	buf[0] = reg & 0xff; -	buf[1] = (reg >> 8) & 0xff; -	buf[2] = val; - -	if (i2c_master_send(client, buf, 3) != 3) { -		dev_err(&client->dev, "%s: i2c send failed\n", __func__); -		return -EIO; -	} - -	return 0; -} - -static int qt602240_read_object_table(struct i2c_client *client, -				      u16 reg, u8 *object_buf) -{ -	return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, -				   object_buf); -} - -static struct qt602240_object * -qt602240_get_object(struct qt602240_data *data, u8 type) -{ -	struct qt602240_object *object; -	int i; - -	for (i = 0; i < data->info.object_num; i++) { -		object = data->object_table + i; -		if (object->type == type) -			return object; -	} - -	dev_err(&data->client->dev, "Invalid object type\n"); -	return NULL; -} - -static int qt602240_read_message(struct qt602240_data *data, -				 struct qt602240_message *message) -{ -	struct qt602240_object *object; -	u16 reg; - -	object = qt602240_get_object(data, QT602240_GEN_MESSAGE); -	if (!object) -		return -EINVAL; - -	reg = object->start_address; -	return __qt602240_read_reg(data->client, reg, -			sizeof(struct qt602240_message), message); -} - -static int qt602240_read_object(struct qt602240_data *data, -				u8 type, u8 offset, u8 *val) -{ -	struct qt602240_object *object; -	u16 reg; - -	object = qt602240_get_object(data, type); -	if (!object) -		return -EINVAL; - -	reg = object->start_address; -	return __qt602240_read_reg(data->client, reg + offset, 1, val); -} - -static int qt602240_write_object(struct qt602240_data *data, -				 u8 type, u8 offset, u8 val) -{ -	struct qt602240_object *object; -	u16 reg; - -	object = qt602240_get_object(data, type); -	if (!object) -		return -EINVAL; - -	reg = object->start_address; -	return qt602240_write_reg(data->client, reg + offset, val); -} - -static void qt602240_input_report(struct qt602240_data *data, int single_id) -{ -	struct qt602240_finger *finger = data->finger; -	struct input_dev *input_dev = data->input_dev; -	int status = finger[single_id].status; -	int finger_num = 0; -	int id; - -	for (id = 0; id < QT602240_MAX_FINGER; id++) { -		if (!finger[id].status) -			continue; - -		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, -				finger[id].status != QT602240_RELEASE ? -				finger[id].area : 0); -		input_report_abs(input_dev, ABS_MT_POSITION_X, -				finger[id].x); -		input_report_abs(input_dev, ABS_MT_POSITION_Y, -				finger[id].y); -		input_mt_sync(input_dev); - -		if (finger[id].status == QT602240_RELEASE) -			finger[id].status = 0; -		else -			finger_num++; -	} - -	input_report_key(input_dev, BTN_TOUCH, finger_num > 0); - -	if (status != QT602240_RELEASE) { -		input_report_abs(input_dev, ABS_X, finger[single_id].x); -		input_report_abs(input_dev, ABS_Y, finger[single_id].y); -	} - -	input_sync(input_dev); -} - -static void qt602240_input_touchevent(struct qt602240_data *data, -				      struct qt602240_message *message, int id) -{ -	struct qt602240_finger *finger = data->finger; -	struct device *dev = &data->client->dev; -	u8 status = message->message[0]; -	int x; -	int y; -	int area; - -	/* Check the touch is present on the screen */ -	if (!(status & QT602240_DETECT)) { -		if (status & QT602240_RELEASE) { -			dev_dbg(dev, "[%d] released\n", id); - -			finger[id].status = QT602240_RELEASE; -			qt602240_input_report(data, id); -		} -		return; -	} - -	/* Check only AMP detection */ -	if (!(status & (QT602240_PRESS | QT602240_MOVE))) -		return; - -	x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); -	y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); -	area = message->message[4]; - -	dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, -		status & QT602240_MOVE ? "moved" : "pressed", -		x, y, area); - -	finger[id].status = status & QT602240_MOVE ? -				QT602240_MOVE : QT602240_PRESS; -	finger[id].x = x; -	finger[id].y = y; -	finger[id].area = area; - -	qt602240_input_report(data, id); -} - -static irqreturn_t qt602240_interrupt(int irq, void *dev_id) -{ -	struct qt602240_data *data = dev_id; -	struct qt602240_message message; -	struct qt602240_object *object; -	struct device *dev = &data->client->dev; -	int id; -	u8 reportid; -	u8 max_reportid; -	u8 min_reportid; - -	do { -		if (qt602240_read_message(data, &message)) { -			dev_err(dev, "Failed to read message\n"); -			goto end; -		} - -		reportid = message.reportid; - -		/* whether reportid is thing of QT602240_TOUCH_MULTI */ -		object = qt602240_get_object(data, QT602240_TOUCH_MULTI); -		if (!object) -			goto end; - -		max_reportid = object->max_reportid; -		min_reportid = max_reportid - object->num_report_ids + 1; -		id = reportid - min_reportid; - -		if (reportid >= min_reportid && reportid <= max_reportid) -			qt602240_input_touchevent(data, &message, id); -		else -			qt602240_dump_message(dev, &message); -	} while (reportid != 0xff); - -end: -	return IRQ_HANDLED; -} - -static int qt602240_check_reg_init(struct qt602240_data *data) -{ -	struct qt602240_object *object; -	struct device *dev = &data->client->dev; -	int index = 0; -	int i, j; -	u8 version = data->info.version; -	u8 *init_vals; - -	switch (version) { -	case QT602240_VER_20: -		init_vals = (u8 *)init_vals_ver_20; -		break; -	case QT602240_VER_21: -		init_vals = (u8 *)init_vals_ver_21; -		break; -	case QT602240_VER_22: -		init_vals = (u8 *)init_vals_ver_22; -		break; -	default: -		dev_err(dev, "Firmware version %d doesn't support\n", version); -		return -EINVAL; -	} - -	for (i = 0; i < data->info.object_num; i++) { -		object = data->object_table + i; - -		if (!qt602240_object_writable(object->type)) -			continue; - -		for (j = 0; j < object->size + 1; j++) -			qt602240_write_object(data, object->type, j, -					init_vals[index + j]); - -		index += object->size + 1; -	} - -	return 0; -} - -static int qt602240_check_matrix_size(struct qt602240_data *data) -{ -	const struct qt602240_platform_data *pdata = data->pdata; -	struct device *dev = &data->client->dev; -	int mode = -1; -	int error; -	u8 val; - -	dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); -	dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); - -	switch (pdata->x_line) { -	case 0 ... 15: -		if (pdata->y_line <= 14) -			mode = 0; -		break; -	case 16: -		if (pdata->y_line <= 12) -			mode = 1; -		if (pdata->y_line == 13 || pdata->y_line == 14) -			mode = 0; -		break; -	case 17: -		if (pdata->y_line <= 11) -			mode = 2; -		if (pdata->y_line == 12 || pdata->y_line == 13) -			mode = 1; -		break; -	case 18: -		if (pdata->y_line <= 10) -			mode = 3; -		if (pdata->y_line == 11 || pdata->y_line == 12) -			mode = 2; -		break; -	case 19: -		if (pdata->y_line <= 9) -			mode = 4; -		if (pdata->y_line == 10 || pdata->y_line == 11) -			mode = 3; -		break; -	case 20: -		mode = 4; -	} - -	if (mode < 0) { -		dev_err(dev, "Invalid X/Y lines\n"); -		return -EINVAL; -	} - -	error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, -				QT602240_CTE_MODE, &val); -	if (error) -		return error; - -	if (mode == val) -		return 0; - -	/* Change the CTE configuration */ -	qt602240_write_object(data, QT602240_SPT_CTECONFIG, -			QT602240_CTE_CTRL, 1); -	qt602240_write_object(data, QT602240_SPT_CTECONFIG, -			QT602240_CTE_MODE, mode); -	qt602240_write_object(data, QT602240_SPT_CTECONFIG, -			QT602240_CTE_CTRL, 0); - -	return 0; -} - -static int qt602240_make_highchg(struct qt602240_data *data) -{ -	struct device *dev = &data->client->dev; -	int count = 10; -	int error; -	u8 val; - -	/* Read dummy message to make high CHG pin */ -	do { -		error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); -		if (error) -			return error; -	} while ((val != 0xff) && --count); - -	if (!count) { -		dev_err(dev, "CHG pin isn't cleared\n"); -		return -EBUSY; -	} - -	return 0; -} - -static void qt602240_handle_pdata(struct qt602240_data *data) -{ -	const struct qt602240_platform_data *pdata = data->pdata; -	u8 voltage; - -	/* Set touchscreen lines */ -	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, -			pdata->x_line); -	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, -			pdata->y_line); - -	/* Set touchscreen orient */ -	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, -			pdata->orient); - -	/* Set touchscreen burst length */ -	qt602240_write_object(data, QT602240_TOUCH_MULTI, -			QT602240_TOUCH_BLEN, pdata->blen); - -	/* Set touchscreen threshold */ -	qt602240_write_object(data, QT602240_TOUCH_MULTI, -			QT602240_TOUCH_TCHTHR, pdata->threshold); - -	/* Set touchscreen resolution */ -	qt602240_write_object(data, QT602240_TOUCH_MULTI, -			QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); -	qt602240_write_object(data, QT602240_TOUCH_MULTI, -			QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); -	qt602240_write_object(data, QT602240_TOUCH_MULTI, -			QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); -	qt602240_write_object(data, QT602240_TOUCH_MULTI, -			QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); - -	/* Set touchscreen voltage */ -	if (data->info.version >= QT602240_VER_21 && pdata->voltage) { -		if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { -			voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / -				QT602240_VOLTAGE_STEP; -			voltage = 0xff - voltage + 1; -		} else -			voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / -				QT602240_VOLTAGE_STEP; - -		qt602240_write_object(data, QT602240_SPT_CTECONFIG, -				QT602240_CTE_VOLTAGE, voltage); -	} -} - -static int qt602240_get_info(struct qt602240_data *data) -{ -	struct i2c_client *client = data->client; -	struct qt602240_info *info = &data->info; -	int error; -	u8 val; - -	error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); -	if (error) -		return error; -	info->family_id = val; - -	error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); -	if (error) -		return error; -	info->variant_id = val; - -	error = qt602240_read_reg(client, QT602240_VERSION, &val); -	if (error) -		return error; -	info->version = val; - -	error = qt602240_read_reg(client, QT602240_BUILD, &val); -	if (error) -		return error; -	info->build = val; - -	error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); -	if (error) -		return error; -	info->object_num = val; - -	return 0; -} - -static int qt602240_get_object_table(struct qt602240_data *data) -{ -	int error; -	int i; -	u16 reg; -	u8 reportid = 0; -	u8 buf[QT602240_OBJECT_SIZE]; - -	for (i = 0; i < data->info.object_num; i++) { -		struct qt602240_object *object = data->object_table + i; - -		reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; -		error = qt602240_read_object_table(data->client, reg, buf); -		if (error) -			return error; - -		object->type = buf[0]; -		object->start_address = (buf[2] << 8) | buf[1]; -		object->size = buf[3]; -		object->instances = buf[4]; -		object->num_report_ids = buf[5]; - -		if (object->num_report_ids) { -			reportid += object->num_report_ids * -					(object->instances + 1); -			object->max_reportid = reportid; -		} -	} - -	return 0; -} - -static int qt602240_initialize(struct qt602240_data *data) -{ -	struct i2c_client *client = data->client; -	struct qt602240_info *info = &data->info; -	int error; -	u8 val; - -	error = qt602240_get_info(data); -	if (error) -		return error; - -	data->object_table = kcalloc(info->object_num, -				     sizeof(struct qt602240_data), -				     GFP_KERNEL); -	if (!data->object_table) { -		dev_err(&client->dev, "Failed to allocate memory\n"); -		return -ENOMEM; -	} - -	/* Get object table information */ -	error = qt602240_get_object_table(data); -	if (error) -		return error; - -	/* Check register init values */ -	error = qt602240_check_reg_init(data); -	if (error) -		return error; - -	/* Check X/Y matrix size */ -	error = qt602240_check_matrix_size(data); -	if (error) -		return error; - -	error = qt602240_make_highchg(data); -	if (error) -		return error; - -	qt602240_handle_pdata(data); - -	/* Backup to memory */ -	qt602240_write_object(data, QT602240_GEN_COMMAND, -			QT602240_COMMAND_BACKUPNV, -			QT602240_BACKUP_VALUE); -	msleep(QT602240_BACKUP_TIME); - -	/* Soft reset */ -	qt602240_write_object(data, QT602240_GEN_COMMAND, -			QT602240_COMMAND_RESET, 1); -	msleep(QT602240_RESET_TIME); - -	/* Update matrix size at info struct */ -	error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); -	if (error) -		return error; -	info->matrix_xsize = val; - -	error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); -	if (error) -		return error; -	info->matrix_ysize = val; - -	dev_info(&client->dev, -			"Family ID: %d Variant ID: %d Version: %d Build: %d\n", -			info->family_id, info->variant_id, info->version, -			info->build); - -	dev_info(&client->dev, -			"Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", -			info->matrix_xsize, info->matrix_ysize, -			info->object_num); - -	return 0; -} - -static ssize_t qt602240_object_show(struct device *dev, -				    struct device_attribute *attr, char *buf) -{ -	struct qt602240_data *data = dev_get_drvdata(dev); -	struct qt602240_object *object; -	int count = 0; -	int i, j; -	int error; -	u8 val; - -	for (i = 0; i < data->info.object_num; i++) { -		object = data->object_table + i; - -		count += sprintf(buf + count, -				"Object Table Element %d(Type %d)\n", -				i + 1, object->type); - -		if (!qt602240_object_readable(object->type)) { -			count += sprintf(buf + count, "\n"); -			continue; -		} - -		for (j = 0; j < object->size + 1; j++) { -			error = qt602240_read_object(data, -						object->type, j, &val); -			if (error) -				return error; - -			count += sprintf(buf + count, -					"  Byte %d: 0x%x (%d)\n", j, val, val); -		} - -		count += sprintf(buf + count, "\n"); -	} - -	return count; -} - -static int qt602240_load_fw(struct device *dev, const char *fn) -{ -	struct qt602240_data *data = dev_get_drvdata(dev); -	struct i2c_client *client = data->client; -	const struct firmware *fw = NULL; -	unsigned int frame_size; -	unsigned int pos = 0; -	int ret; - -	ret = request_firmware(&fw, fn, dev); -	if (ret) { -		dev_err(dev, "Unable to open firmware %s\n", fn); -		return ret; -	} - -	/* Change to the bootloader mode */ -	qt602240_write_object(data, QT602240_GEN_COMMAND, -			QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); -	msleep(QT602240_RESET_TIME); - -	/* Change to slave address of bootloader */ -	if (client->addr == QT602240_APP_LOW) -		client->addr = QT602240_BOOT_LOW; -	else -		client->addr = QT602240_BOOT_HIGH; - -	ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); -	if (ret) -		goto out; - -	/* Unlock bootloader */ -	qt602240_unlock_bootloader(client); - -	while (pos < fw->size) { -		ret = qt602240_check_bootloader(client, -						QT602240_WAITING_FRAME_DATA); -		if (ret) -			goto out; - -		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); - -		/* We should add 2 at frame size as the the firmware data is not -		 * included the CRC bytes. -		 */ -		frame_size += 2; - -		/* Write one frame to device */ -		qt602240_fw_write(client, fw->data + pos, frame_size); - -		ret = qt602240_check_bootloader(client, -						QT602240_FRAME_CRC_PASS); -		if (ret) -			goto out; - -		pos += frame_size; - -		dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); -	} - -out: -	release_firmware(fw); - -	/* Change to slave address of application */ -	if (client->addr == QT602240_BOOT_LOW) -		client->addr = QT602240_APP_LOW; -	else -		client->addr = QT602240_APP_HIGH; - -	return ret; -} - -static ssize_t qt602240_update_fw_store(struct device *dev, -					struct device_attribute *attr, -					const char *buf, size_t count) -{ -	struct qt602240_data *data = dev_get_drvdata(dev); -	unsigned int version; -	int error; - -	if (sscanf(buf, "%u", &version) != 1) { -		dev_err(dev, "Invalid values\n"); -		return -EINVAL; -	} - -	if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { -		dev_err(dev, "FW update supported starting with version 21\n"); -		return -EINVAL; -	} - -	disable_irq(data->irq); - -	error = qt602240_load_fw(dev, QT602240_FW_NAME); -	if (error) { -		dev_err(dev, "The firmware update failed(%d)\n", error); -		count = error; -	} else { -		dev_dbg(dev, "The firmware update succeeded\n"); - -		/* Wait for reset */ -		msleep(QT602240_FWRESET_TIME); - -		kfree(data->object_table); -		data->object_table = NULL; - -		qt602240_initialize(data); -	} - -	enable_irq(data->irq); - -	return count; -} - -static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); -static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); - -static struct attribute *qt602240_attrs[] = { -	&dev_attr_object.attr, -	&dev_attr_update_fw.attr, -	NULL -}; - -static const struct attribute_group qt602240_attr_group = { -	.attrs = qt602240_attrs, -}; - -static void qt602240_start(struct qt602240_data *data) -{ -	/* Touch enable */ -	qt602240_write_object(data, -			QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); -} - -static void qt602240_stop(struct qt602240_data *data) -{ -	/* Touch disable */ -	qt602240_write_object(data, -			QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); -} - -static int qt602240_input_open(struct input_dev *dev) -{ -	struct qt602240_data *data = input_get_drvdata(dev); - -	qt602240_start(data); - -	return 0; -} - -static void qt602240_input_close(struct input_dev *dev) -{ -	struct qt602240_data *data = input_get_drvdata(dev); - -	qt602240_stop(data); -} - -static int __devinit qt602240_probe(struct i2c_client *client, -		const struct i2c_device_id *id) -{ -	struct qt602240_data *data; -	struct input_dev *input_dev; -	int error; - -	if (!client->dev.platform_data) -		return -EINVAL; - -	data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); -	input_dev = input_allocate_device(); -	if (!data || !input_dev) { -		dev_err(&client->dev, "Failed to allocate memory\n"); -		error = -ENOMEM; -		goto err_free_mem; -	} - -	input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; -	input_dev->id.bustype = BUS_I2C; -	input_dev->dev.parent = &client->dev; -	input_dev->open = qt602240_input_open; -	input_dev->close = qt602240_input_close; - -	__set_bit(EV_ABS, input_dev->evbit); -	__set_bit(EV_KEY, input_dev->evbit); -	__set_bit(BTN_TOUCH, input_dev->keybit); - -	/* For single touch */ -	input_set_abs_params(input_dev, ABS_X, -			     0, QT602240_MAX_XC, 0, 0); -	input_set_abs_params(input_dev, ABS_Y, -			     0, QT602240_MAX_YC, 0, 0); - -	/* For multi touch */ -	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, -			     0, QT602240_MAX_AREA, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_POSITION_X, -			     0, QT602240_MAX_XC, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, -			     0, QT602240_MAX_YC, 0, 0); - -	input_set_drvdata(input_dev, data); - -	data->client = client; -	data->input_dev = input_dev; -	data->pdata = client->dev.platform_data; -	data->irq = client->irq; - -	i2c_set_clientdata(client, data); - -	error = qt602240_initialize(data); -	if (error) -		goto err_free_object; - -	error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, -			IRQF_TRIGGER_FALLING, client->dev.driver->name, data); -	if (error) { -		dev_err(&client->dev, "Failed to register interrupt\n"); -		goto err_free_object; -	} - -	error = input_register_device(input_dev); -	if (error) -		goto err_free_irq; - -	error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); -	if (error) -		goto err_unregister_device; - -	return 0; - -err_unregister_device: -	input_unregister_device(input_dev); -	input_dev = NULL; -err_free_irq: -	free_irq(client->irq, data); -err_free_object: -	kfree(data->object_table); -err_free_mem: -	input_free_device(input_dev); -	kfree(data); -	return error; -} - -static int __devexit qt602240_remove(struct i2c_client *client) -{ -	struct qt602240_data *data = i2c_get_clientdata(client); - -	sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); -	free_irq(data->irq, data); -	input_unregister_device(data->input_dev); -	kfree(data->object_table); -	kfree(data); - -	return 0; -} - -#ifdef CONFIG_PM -static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg) -{ -	struct qt602240_data *data = i2c_get_clientdata(client); -	struct input_dev *input_dev = data->input_dev; - -	mutex_lock(&input_dev->mutex); - -	if (input_dev->users) -		qt602240_stop(data); - -	mutex_unlock(&input_dev->mutex); - -	return 0; -} - -static int qt602240_resume(struct i2c_client *client) -{ -	struct qt602240_data *data = i2c_get_clientdata(client); -	struct input_dev *input_dev = data->input_dev; - -	/* Soft reset */ -	qt602240_write_object(data, QT602240_GEN_COMMAND, -			QT602240_COMMAND_RESET, 1); - -	msleep(QT602240_RESET_TIME); - -	mutex_lock(&input_dev->mutex); - -	if (input_dev->users) -		qt602240_start(data); - -	mutex_unlock(&input_dev->mutex); - -	return 0; -} -#else -#define qt602240_suspend	NULL -#define qt602240_resume		NULL -#endif - -static const struct i2c_device_id qt602240_id[] = { -	{ "qt602240_ts", 0 }, -	{ } -}; -MODULE_DEVICE_TABLE(i2c, qt602240_id); - -static struct i2c_driver qt602240_driver = { -	.driver = { -		.name	= "qt602240_ts", -		.owner	= THIS_MODULE, -	}, -	.probe		= qt602240_probe, -	.remove		= __devexit_p(qt602240_remove), -	.suspend	= qt602240_suspend, -	.resume		= qt602240_resume, -	.id_table	= qt602240_id, -}; - -static int __init qt602240_init(void) -{ -	return i2c_add_driver(&qt602240_driver); -} - -static void __exit qt602240_exit(void) -{ -	i2c_del_driver(&qt602240_driver); -} - -module_init(qt602240_init); -module_exit(qt602240_exit); - -/* Module information */ -MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); -MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 8feb7f3c8be..19cb247dbb8 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -28,7 +28,6 @@  #include <linux/module.h>  #include <linux/gpio.h>  #include <linux/input.h> -#include <linux/init.h>  #include <linux/delay.h>  #include <linux/interrupt.h>  #include <linux/platform_device.h> @@ -37,7 +36,7 @@  #include <plat/adc.h>  #include <plat/regs-adc.h> -#include <plat/ts.h> +#include <linux/platform_data/touchscreen-s3c2410.h>  #define TSC_SLEEP  (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0)) @@ -238,7 +237,7 @@ static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)   * Initialise, find and allocate any resources we need to run and then   * register with the ADC and input systems.   */ -static int __devinit s3c2410ts_probe(struct platform_device *pdev) +static int s3c2410ts_probe(struct platform_device *pdev)  {  	struct s3c2410_ts_mach_info *info;  	struct device *dev = &pdev->dev; @@ -251,7 +250,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev)  	ts.dev = dev; -	info = pdev->dev.platform_data; +	info = dev_get_platdata(&pdev->dev);  	if (!info) {  		dev_err(dev, "no platform data, cannot attach\n");  		return -EINVAL; @@ -328,7 +327,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev)  	ts.shift = info->oversampling_shift;  	ts.features = platform_get_device_id(pdev)->driver_data; -	ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED, +	ret = request_irq(ts.irq_tc, stylus_irq, 0,  			  "s3c2410_ts_pen", ts.input);  	if (ret) {  		dev_err(dev, "cannot get TC interrupt\n"); @@ -365,7 +364,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev)   *   * Free up our state ready to be removed.   */ -static int __devexit s3c2410ts_remove(struct platform_device *pdev) +static int s3c2410ts_remove(struct platform_device *pdev)  {  	free_irq(ts.irq_tc, ts.input);  	del_timer_sync(&touch_timer); @@ -392,7 +391,7 @@ static int s3c2410ts_suspend(struct device *dev)  static int s3c2410ts_resume(struct device *dev)  {  	struct platform_device *pdev = to_platform_device(dev); -	struct s3c2410_ts_mach_info *info = pdev->dev.platform_data; +	struct s3c2410_ts_mach_info *info = dev_get_platdata(&pdev->dev);  	clk_enable(ts.clock);  	enable_irq(ts.irq_tc); @@ -406,7 +405,7 @@ static int s3c2410ts_resume(struct device *dev)  	return 0;  } -static struct dev_pm_ops s3c_ts_pmops = { +static const struct dev_pm_ops s3c_ts_pmops = {  	.suspend	= s3c2410ts_suspend,  	.resume		= s3c2410ts_resume,  }; @@ -430,21 +429,9 @@ static struct platform_driver s3c_ts_driver = {  	},  	.id_table	= s3cts_driver_ids,  	.probe		= s3c2410ts_probe, -	.remove		= __devexit_p(s3c2410ts_remove), +	.remove		= s3c2410ts_remove,  }; - -static int __init s3c2410ts_init(void) -{ -	return platform_driver_register(&s3c_ts_driver); -} - -static void __exit s3c2410ts_exit(void) -{ -	platform_driver_unregister(&s3c_ts_driver); -} - -module_init(s3c2410ts_init); -module_exit(s3c2410ts_exit); +module_platform_driver(s3c_ts_driver);  MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "  	      "Ben Dooks <ben@simtec.co.uk>, " diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c new file mode 100644 index 00000000000..3c0f57efe7b --- /dev/null +++ b/drivers/input/touchscreen/st1232.c @@ -0,0 +1,312 @@ +/* + * ST1232 Touchscreen Controller Driver + * + * Copyright (C) 2010 Renesas Solutions Corp. + *	Tony SIM <chinyeow.sim.xt@renesas.com> + * + * Using code from: + *  - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c + *	Copyright (C) 2007 Google, Inc. + * + * 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.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/pm_qos.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/platform_data/st1232_pdata.h> + +#define ST1232_TS_NAME	"st1232-ts" + +#define MIN_X		0x00 +#define MIN_Y		0x00 +#define MAX_X		0x31f	/* (800 - 1) */ +#define MAX_Y		0x1df	/* (480 - 1) */ +#define MAX_AREA	0xff +#define MAX_FINGERS	2 + +struct st1232_ts_finger { +	u16 x; +	u16 y; +	u8 t; +	bool is_valid; +}; + +struct st1232_ts_data { +	struct i2c_client *client; +	struct input_dev *input_dev; +	struct st1232_ts_finger finger[MAX_FINGERS]; +	struct dev_pm_qos_request low_latency_req; +	int reset_gpio; +}; + +static int st1232_ts_read_data(struct st1232_ts_data *ts) +{ +	struct st1232_ts_finger *finger = ts->finger; +	struct i2c_client *client = ts->client; +	struct i2c_msg msg[2]; +	int error; +	u8 start_reg; +	u8 buf[10]; + +	/* read touchscreen data from ST1232 */ +	msg[0].addr = client->addr; +	msg[0].flags = 0; +	msg[0].len = 1; +	msg[0].buf = &start_reg; +	start_reg = 0x10; + +	msg[1].addr = ts->client->addr; +	msg[1].flags = I2C_M_RD; +	msg[1].len = sizeof(buf); +	msg[1].buf = buf; + +	error = i2c_transfer(client->adapter, msg, 2); +	if (error < 0) +		return error; + +	/* get "valid" bits */ +	finger[0].is_valid = buf[2] >> 7; +	finger[1].is_valid = buf[5] >> 7; + +	/* get xy coordinate */ +	if (finger[0].is_valid) { +		finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3]; +		finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4]; +		finger[0].t = buf[8]; +	} + +	if (finger[1].is_valid) { +		finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6]; +		finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7]; +		finger[1].t = buf[9]; +	} + +	return 0; +} + +static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) +{ +	struct st1232_ts_data *ts = dev_id; +	struct st1232_ts_finger *finger = ts->finger; +	struct input_dev *input_dev = ts->input_dev; +	int count = 0; +	int i, ret; + +	ret = st1232_ts_read_data(ts); +	if (ret < 0) +		goto end; + +	/* multi touch protocol */ +	for (i = 0; i < MAX_FINGERS; i++) { +		if (!finger[i].is_valid) +			continue; + +		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); +		input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); +		input_mt_sync(input_dev); +		count++; +	} + +	/* SYN_MT_REPORT only if no contact */ +	if (!count) { +		input_mt_sync(input_dev); +		if (ts->low_latency_req.dev) { +			dev_pm_qos_remove_request(&ts->low_latency_req); +			ts->low_latency_req.dev = NULL; +		} +	} else if (!ts->low_latency_req.dev) { +		/* First contact, request 100 us latency. */ +		dev_pm_qos_add_ancestor_request(&ts->client->dev, +						&ts->low_latency_req, +						DEV_PM_QOS_RESUME_LATENCY, 100); +	} + +	/* SYN_REPORT */ +	input_sync(input_dev); + +end: +	return IRQ_HANDLED; +} + +static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) +{ +	if (gpio_is_valid(ts->reset_gpio)) +		gpio_direction_output(ts->reset_gpio, poweron); +} + +static int st1232_ts_probe(struct i2c_client *client, +					const struct i2c_device_id *id) +{ +	struct st1232_ts_data *ts; +	struct st1232_pdata *pdata = dev_get_platdata(&client->dev); +	struct input_dev *input_dev; +	int error; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		dev_err(&client->dev, "need I2C_FUNC_I2C\n"); +		return -EIO; +	} + +	if (!client->irq) { +		dev_err(&client->dev, "no IRQ?\n"); +		return -EINVAL; +	} + +	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); +	if (!ts) +		return -ENOMEM; + +	input_dev = devm_input_allocate_device(&client->dev); +	if (!input_dev) +		return -ENOMEM; + +	ts->client = client; +	ts->input_dev = input_dev; + +	if (pdata) +		ts->reset_gpio = pdata->reset_gpio; +	else if (client->dev.of_node) +		ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); +	else +		ts->reset_gpio = -ENODEV; + +	if (gpio_is_valid(ts->reset_gpio)) { +		error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL); +		if (error) { +			dev_err(&client->dev, +				"Unable to request GPIO pin %d.\n", +				ts->reset_gpio); +				return error; +		} +	} + +	st1232_ts_power(ts, true); + +	input_dev->name = "st1232-touchscreen"; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &client->dev; + +	__set_bit(EV_SYN, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(EV_ABS, input_dev->evbit); + +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); + +	error = devm_request_threaded_irq(&client->dev, client->irq, +					  NULL, st1232_ts_irq_handler, +					  IRQF_ONESHOT, +					  client->name, ts); +	if (error) { +		dev_err(&client->dev, "Failed to register interrupt\n"); +		return error; +	} + +	error = input_register_device(ts->input_dev); +	if (error) { +		dev_err(&client->dev, "Unable to register %s input device\n", +			input_dev->name); +		return error; +	} + +	i2c_set_clientdata(client, ts); +	device_init_wakeup(&client->dev, 1); + +	return 0; +} + +static int st1232_ts_remove(struct i2c_client *client) +{ +	struct st1232_ts_data *ts = i2c_get_clientdata(client); + +	device_init_wakeup(&client->dev, 0); +	st1232_ts_power(ts, false); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st1232_ts_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct st1232_ts_data *ts = i2c_get_clientdata(client); + +	if (device_may_wakeup(&client->dev)) { +		enable_irq_wake(client->irq); +	} else { +		disable_irq(client->irq); +		st1232_ts_power(ts, false); +	} + +	return 0; +} + +static int st1232_ts_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct st1232_ts_data *ts = i2c_get_clientdata(client); + +	if (device_may_wakeup(&client->dev)) { +		disable_irq_wake(client->irq); +	} else { +		st1232_ts_power(ts, true); +		enable_irq(client->irq); +	} + +	return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops, +			 st1232_ts_suspend, st1232_ts_resume); + +static const struct i2c_device_id st1232_ts_id[] = { +	{ ST1232_TS_NAME, 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, st1232_ts_id); + +#ifdef CONFIG_OF +static const struct of_device_id st1232_ts_dt_ids[] = { +	{ .compatible = "sitronix,st1232", }, +	{ } +}; +MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids); +#endif + +static struct i2c_driver st1232_ts_driver = { +	.probe		= st1232_ts_probe, +	.remove		= st1232_ts_remove, +	.id_table	= st1232_ts_id, +	.driver = { +		.name	= ST1232_TS_NAME, +		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(st1232_ts_dt_ids), +		.pm	= &st1232_ts_pm_ops, +	}, +}; + +module_i2c_driver(st1232_ts_driver); + +MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>"); +MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index ae88e13c99f..42ce31afa25 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -1,4 +1,5 @@ -/* STMicroelectronics STMPE811 Touchscreen Driver +/* + * STMicroelectronics STMPE811 Touchscreen Driver   *   * (C) 2010 Luotao Fu <l.fu@pengutronix.de>   * All rights reserved. @@ -14,8 +15,8 @@  #include <linux/module.h>  #include <linux/sched.h>  #include <linux/interrupt.h> -#include <linux/init.h>  #include <linux/device.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/input.h>  #include <linux/slab.h> @@ -118,6 +119,7 @@ static void stmpe_work(struct work_struct *work)  	__stmpe_reset_fifo(ts->stmpe);  	input_report_abs(ts->idev, ABS_PRESSURE, 0); +	input_report_key(ts->idev, BTN_TOUCH, 0);  	input_sync(ts->idev);  } @@ -151,6 +153,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)  	input_report_abs(ts->idev, ABS_X, x);  	input_report_abs(ts->idev, ABS_Y, y);  	input_report_abs(ts->idev, ABS_PRESSURE, z); +	input_report_key(ts->idev, BTN_TOUCH, 1);  	input_sync(ts->idev);         /* flush the FIFO after we have read out our values. */ @@ -166,7 +169,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)  	return IRQ_HANDLED;  } -static int __devinit stmpe_init_hw(struct stmpe_touch *ts) +static int stmpe_init_hw(struct stmpe_touch *ts)  {  	int ret;  	u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask; @@ -261,41 +264,18 @@ static void stmpe_ts_close(struct input_dev *dev)  			STMPE_TSC_CTRL_TSC_EN, 0);  } -static int __devinit stmpe_input_probe(struct platform_device *pdev) +static void stmpe_ts_get_platform_info(struct platform_device *pdev, +					struct stmpe_touch *ts)  {  	struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); -	struct stmpe_platform_data *pdata = stmpe->pdata; -	struct stmpe_touch *ts; -	struct input_dev *idev; +	struct device_node *np = pdev->dev.of_node;  	struct stmpe_ts_platform_data *ts_pdata = NULL; -	int ret; -	int ts_irq; - -	ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); -	if (ts_irq < 0) -		return ts_irq; - -	ts = kzalloc(sizeof(*ts), GFP_KERNEL); -	if (!ts) { -		ret = -ENOMEM; -		goto err_out; -	} - -	idev = input_allocate_device(); -	if (!idev) { -		ret = -ENOMEM; -		goto err_free_ts; -	} -	platform_set_drvdata(pdev, ts);  	ts->stmpe = stmpe; -	ts->idev = idev; -	ts->dev = &pdev->dev; -	if (pdata) -		ts_pdata = pdata->ts; +	if (stmpe->pdata && stmpe->pdata->ts) { +		ts_pdata = stmpe->pdata->ts; -	if (ts_pdata) {  		ts->sample_time = ts_pdata->sample_time;  		ts->mod_12b = ts_pdata->mod_12b;  		ts->ref_sel = ts_pdata->ref_sel; @@ -305,22 +285,71 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)  		ts->settling = ts_pdata->settling;  		ts->fraction_z = ts_pdata->fraction_z;  		ts->i_drive = ts_pdata->i_drive; +	} else if (np) { +		u32 val; + +		if (!of_property_read_u32(np, "st,sample-time", &val)) +			ts->sample_time = val; +		if (!of_property_read_u32(np, "st,mod-12b", &val)) +			ts->mod_12b = val; +		if (!of_property_read_u32(np, "st,ref-sel", &val)) +			ts->ref_sel = val; +		if (!of_property_read_u32(np, "st,adc-freq", &val)) +			ts->adc_freq = val; +		if (!of_property_read_u32(np, "st,ave-ctrl", &val)) +			ts->ave_ctrl = val; +		if (!of_property_read_u32(np, "st,touch-det-delay", &val)) +			ts->touch_det_delay = val; +		if (!of_property_read_u32(np, "st,settling", &val)) +			ts->settling = val; +		if (!of_property_read_u32(np, "st,fraction-z", &val)) +			ts->fraction_z = val; +		if (!of_property_read_u32(np, "st,i-drive", &val)) +			ts->i_drive = val;  	} +} + +static int stmpe_input_probe(struct platform_device *pdev) +{ +	struct stmpe_touch *ts; +	struct input_dev *idev; +	int error; +	int ts_irq; + +	ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); +	if (ts_irq < 0) +		return ts_irq; + +	ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); +	if (!ts) +		return -ENOMEM; + +	idev = devm_input_allocate_device(&pdev->dev); +	if (!idev) +		return -ENOMEM; + +	platform_set_drvdata(pdev, ts); +	ts->idev = idev; +	ts->dev = &pdev->dev; + +	stmpe_ts_get_platform_info(pdev, ts);  	INIT_DELAYED_WORK(&ts->work, stmpe_work); -	ret = request_threaded_irq(ts_irq, NULL, stmpe_ts_handler, -			IRQF_ONESHOT, STMPE_TS_NAME, ts); -	if (ret) { +	error = devm_request_threaded_irq(&pdev->dev, ts_irq, +					  NULL, stmpe_ts_handler, +					  IRQF_ONESHOT, STMPE_TS_NAME, ts); +	if (error) {  		dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq); -		goto err_free_input; +		return error;  	} -	ret = stmpe_init_hw(ts); -	if (ret) -		goto err_free_irq; +	error = stmpe_init_hw(ts); +	if (error) +		return error;  	idev->name = STMPE_TS_NAME; +	idev->phys = STMPE_TS_NAME"/input0";  	idev->id.bustype = BUS_I2C;  	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); @@ -334,40 +363,21 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)  	input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);  	input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0); -	ret = input_register_device(idev); -	if (ret) { +	error = input_register_device(idev); +	if (error) {  		dev_err(&pdev->dev, "Could not register input device\n"); -		goto err_free_irq; +		return error;  	} -	return ret; - -err_free_irq: -	free_irq(ts_irq, ts); -err_free_input: -	input_free_device(idev); -	platform_set_drvdata(pdev, NULL); -err_free_ts: -	kfree(ts); -err_out: -	return ret; +	return 0;  } -static int __devexit stmpe_ts_remove(struct platform_device *pdev) +static int stmpe_ts_remove(struct platform_device *pdev)  {  	struct stmpe_touch *ts = platform_get_drvdata(pdev); -	unsigned int ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");  	stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN); -	free_irq(ts_irq, ts); - -	platform_set_drvdata(pdev, NULL); - -	input_unregister_device(ts->idev); - -	kfree(ts); -  	return 0;  } @@ -377,22 +387,9 @@ static struct platform_driver stmpe_ts_driver = {  		   .owner = THIS_MODULE,  		   },  	.probe = stmpe_input_probe, -	.remove = __devexit_p(stmpe_ts_remove), +	.remove = stmpe_ts_remove,  }; - -static int __init stmpe_ts_init(void) -{ -	return platform_driver_register(&stmpe_ts_driver); -} - -module_init(stmpe_ts_init); - -static void __exit stmpe_ts_exit(void) -{ -	platform_driver_unregister(&stmpe_ts_driver); -} - -module_exit(stmpe_ts_exit); +module_platform_driver(stmpe_ts_driver);  MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");  MODULE_DESCRIPTION("STMPEXXX touchscreen driver"); diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c new file mode 100644 index 00000000000..2ba82602495 --- /dev/null +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -0,0 +1,339 @@ +/* + * Allwinner sunxi resistive touchscreen controller driver + * + * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede@redhat.com> + * + * The hwmon parts are based on work by Corentin LABBE which is: + * Copyright (C) 2013 Corentin LABBE <clabbe.montjoie@gmail.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. + */ + +/* + * The sun4i-ts controller is capable of detecting a second touch, but when a + * second touch is present then the accuracy becomes so bad the reported touch + * location is not useable. + * + * The original android driver contains some complicated heuristics using the + * aprox. distance between the 2 touches to see if the user is making a pinch + * open / close movement, and then reports emulated multi-touch events around + * the last touch coordinate (as the dual-touch coordinates are worthless). + * + * These kinds of heuristics are just asking for trouble (and don't belong + * in the kernel). So this driver offers straight forward, reliable single + * touch functionality only. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define TP_CTRL0		0x00 +#define TP_CTRL1		0x04 +#define TP_CTRL2		0x08 +#define TP_CTRL3		0x0c +#define TP_INT_FIFOC		0x10 +#define TP_INT_FIFOS		0x14 +#define TP_TPR			0x18 +#define TP_CDAT			0x1c +#define TEMP_DATA		0x20 +#define TP_DATA			0x24 + +/* TP_CTRL0 bits */ +#define ADC_FIRST_DLY(x)	((x) << 24) /* 8 bits */ +#define ADC_FIRST_DLY_MODE(x)	((x) << 23) +#define ADC_CLK_SEL(x)		((x) << 22) +#define ADC_CLK_DIV(x)		((x) << 20) /* 3 bits */ +#define FS_DIV(x)		((x) << 16) /* 4 bits */ +#define T_ACQ(x)		((x) << 0) /* 16 bits */ + +/* TP_CTRL1 bits */ +#define STYLUS_UP_DEBOUN(x)	((x) << 12) /* 8 bits */ +#define STYLUS_UP_DEBOUN_EN(x)	((x) << 9) +#define TOUCH_PAN_CALI_EN(x)	((x) << 6) +#define TP_DUAL_EN(x)		((x) << 5) +#define TP_MODE_EN(x)		((x) << 4) +#define TP_ADC_SELECT(x)	((x) << 3) +#define ADC_CHAN_SELECT(x)	((x) << 0)  /* 3 bits */ + +/* TP_CTRL2 bits */ +#define TP_SENSITIVE_ADJUST(x)	((x) << 28) /* 4 bits */ +#define TP_MODE_SELECT(x)	((x) << 26) /* 2 bits */ +#define PRE_MEA_EN(x)		((x) << 24) +#define PRE_MEA_THRE_CNT(x)	((x) << 0) /* 24 bits */ + +/* TP_CTRL3 bits */ +#define FILTER_EN(x)		((x) << 2) +#define FILTER_TYPE(x)		((x) << 0)  /* 2 bits */ + +/* TP_INT_FIFOC irq and fifo mask / control bits */ +#define TEMP_IRQ_EN(x)		((x) << 18) +#define OVERRUN_IRQ_EN(x)	((x) << 17) +#define DATA_IRQ_EN(x)		((x) << 16) +#define TP_DATA_XY_CHANGE(x)	((x) << 13) +#define FIFO_TRIG(x)		((x) << 8)  /* 5 bits */ +#define DATA_DRQ_EN(x)		((x) << 7) +#define FIFO_FLUSH(x)		((x) << 4) +#define TP_UP_IRQ_EN(x)		((x) << 1) +#define TP_DOWN_IRQ_EN(x)	((x) << 0) + +/* TP_INT_FIFOS irq and fifo status bits */ +#define TEMP_DATA_PENDING	BIT(18) +#define FIFO_OVERRUN_PENDING	BIT(17) +#define FIFO_DATA_PENDING	BIT(16) +#define TP_IDLE_FLG		BIT(2) +#define TP_UP_PENDING		BIT(1) +#define TP_DOWN_PENDING		BIT(0) + +/* TP_TPR bits */ +#define TEMP_ENABLE(x)		((x) << 16) +#define TEMP_PERIOD(x)		((x) << 0)  /* t = x * 256 * 16 / clkin */ + +struct sun4i_ts_data { +	struct device *dev; +	struct input_dev *input; +	void __iomem *base; +	unsigned int irq; +	bool ignore_fifo_data; +	int temp_data; +}; + +static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val) +{ +	u32 x, y; + +	if (reg_val & FIFO_DATA_PENDING) { +		x = readl(ts->base + TP_DATA); +		y = readl(ts->base + TP_DATA); +		/* The 1st location reported after an up event is unreliable */ +		if (!ts->ignore_fifo_data) { +			input_report_abs(ts->input, ABS_X, x); +			input_report_abs(ts->input, ABS_Y, y); +			/* +			 * The hardware has a separate down status bit, but +			 * that gets set before we get the first location, +			 * resulting in reporting a click on the old location. +			 */ +			input_report_key(ts->input, BTN_TOUCH, 1); +			input_sync(ts->input); +		} else { +			ts->ignore_fifo_data = false; +		} +	} + +	if (reg_val & TP_UP_PENDING) { +		ts->ignore_fifo_data = true; +		input_report_key(ts->input, BTN_TOUCH, 0); +		input_sync(ts->input); +	} +} + +static irqreturn_t sun4i_ts_irq(int irq, void *dev_id) +{ +	struct sun4i_ts_data *ts = dev_id; +	u32 reg_val; + +	reg_val  = readl(ts->base + TP_INT_FIFOS); + +	if (reg_val & TEMP_DATA_PENDING) +		ts->temp_data = readl(ts->base + TEMP_DATA); + +	if (ts->input) +		sun4i_ts_irq_handle_input(ts, reg_val); + +	writel(reg_val, ts->base + TP_INT_FIFOS); + +	return IRQ_HANDLED; +} + +static int sun4i_ts_open(struct input_dev *dev) +{ +	struct sun4i_ts_data *ts = input_get_drvdata(dev); + +	/* Flush, set trig level to 1, enable temp, data and up irqs */ +	writel(TEMP_IRQ_EN(1) | DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) | +		TP_UP_IRQ_EN(1), ts->base + TP_INT_FIFOC); + +	return 0; +} + +static void sun4i_ts_close(struct input_dev *dev) +{ +	struct sun4i_ts_data *ts = input_get_drvdata(dev); + +	/* Deactivate all input IRQs */ +	writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, +			 char *buf) +{ +	struct sun4i_ts_data *ts = dev_get_drvdata(dev); + +	/* No temp_data until the first irq */ +	if (ts->temp_data == -1) +		return -EAGAIN; + +	return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100); +} + +static ssize_t show_temp_label(struct device *dev, +			      struct device_attribute *devattr, char *buf) +{ +	return sprintf(buf, "SoC temperature\n"); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); +static DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL); + +static struct attribute *sun4i_ts_attrs[] = { +	&dev_attr_temp1_input.attr, +	&dev_attr_temp1_label.attr, +	NULL +}; +ATTRIBUTE_GROUPS(sun4i_ts); + +static int sun4i_ts_probe(struct platform_device *pdev) +{ +	struct sun4i_ts_data *ts; +	struct device *dev = &pdev->dev; +	struct device_node *np = dev->of_node; +	struct device *hwmon; +	int error; +	bool ts_attached; + +	ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); +	if (!ts) +		return -ENOMEM; + +	ts->dev = dev; +	ts->ignore_fifo_data = true; +	ts->temp_data = -1; + +	ts_attached = of_property_read_bool(np, "allwinner,ts-attached"); +	if (ts_attached) { +		ts->input = devm_input_allocate_device(dev); +		if (!ts->input) +			return -ENOMEM; + +		ts->input->name = pdev->name; +		ts->input->phys = "sun4i_ts/input0"; +		ts->input->open = sun4i_ts_open; +		ts->input->close = sun4i_ts_close; +		ts->input->id.bustype = BUS_HOST; +		ts->input->id.vendor = 0x0001; +		ts->input->id.product = 0x0001; +		ts->input->id.version = 0x0100; +		ts->input->evbit[0] =  BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); +		__set_bit(BTN_TOUCH, ts->input->keybit); +		input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0); +		input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0); +		input_set_drvdata(ts->input, ts); +	} + +	ts->base = devm_ioremap_resource(dev, +			      platform_get_resource(pdev, IORESOURCE_MEM, 0)); +	if (IS_ERR(ts->base)) +		return PTR_ERR(ts->base); + +	ts->irq = platform_get_irq(pdev, 0); +	error = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts); +	if (error) +		return error; + +	/* +	 * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192, +	 * t_acq = clkin / (16 * 64) +	 */ +	writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63), +	       ts->base + TP_CTRL0); + +	/* +	 * sensitive_adjust = 15 : max, which is not all that sensitive, +	 * tp_mode = 0 : only x and y coordinates, as we don't use dual touch +	 */ +	writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0), +	       ts->base + TP_CTRL2); + +	/* Enable median filter, type 1 : 5/3 */ +	writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3); + +	/* Enable temperature measurement, period 1953 (2 seconds) */ +	writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR); + +	/* +	 * Set stylus up debounce to aprox 10 ms, enable debounce, and +	 * finally enable tp mode. +	 */ +	writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1), +	       ts->base + TP_CTRL1); + +	hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts", +						       ts, sun4i_ts_groups); +	if (IS_ERR(hwmon)) +		return PTR_ERR(hwmon); + +	writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); + +	if (ts_attached) { +		error = input_register_device(ts->input); +		if (error) { +			writel(0, ts->base + TP_INT_FIFOC); +			return error; +		} +	} + +	platform_set_drvdata(pdev, ts); +	return 0; +} + +static int sun4i_ts_remove(struct platform_device *pdev) +{ +	struct sun4i_ts_data *ts = platform_get_drvdata(pdev); + +	/* Explicit unregister to avoid open/close changing the imask later */ +	if (ts->input) +		input_unregister_device(ts->input); + +	/* Deactivate all IRQs */ +	writel(0, ts->base + TP_INT_FIFOC); + +	return 0; +} + +static const struct of_device_id sun4i_ts_of_match[] = { +	{ .compatible = "allwinner,sun4i-a10-ts", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun4i_ts_of_match); + +static struct platform_driver sun4i_ts_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "sun4i-ts", +		.of_match_table = of_match_ptr(sun4i_ts_of_match), +	}, +	.probe	= sun4i_ts_probe, +	.remove	= sun4i_ts_remove, +}; + +module_platform_driver(sun4i_ts_driver); + +MODULE_DESCRIPTION("Allwinner sun4i resistive touchscreen controller driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c new file mode 100644 index 00000000000..f1cb05148b4 --- /dev/null +++ b/drivers/input/touchscreen/sur40.c @@ -0,0 +1,466 @@ +/* + * Surface2.0/SUR40/PixelSense input driver + * + * Copyright (c) 2013 by Florian 'floe' Echtler <floe@butterbrot.org> + * + * Derived from the USB Skeleton driver 1.1, + * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com) + * + * and from the Apple USB BCM5974 multitouch driver, + * Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se) + * + * and from the generic hid-multitouch driver, + * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/completion.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/printk.h> +#include <linux/input-polldev.h> +#include <linux/input/mt.h> +#include <linux/usb/input.h> + +/* read 512 bytes from endpoint 0x86 -> get header + blobs */ +struct sur40_header { + +	__le16 type;       /* always 0x0001 */ +	__le16 count;      /* count of blobs (if 0: continue prev. packet) */ + +	__le32 packet_id;  /* unique ID for all packets in one frame */ + +	__le32 timestamp;  /* milliseconds (inc. by 16 or 17 each frame) */ +	__le32 unknown;    /* "epoch?" always 02/03 00 00 00 */ + +} __packed; + +struct sur40_blob { + +	__le16 blob_id; + +	u8 action;         /* 0x02 = enter/exit, 0x03 = update (?) */ +	u8 unknown;        /* always 0x01 or 0x02 (no idea what this is?) */ + +	__le16 bb_pos_x;   /* upper left corner of bounding box */ +	__le16 bb_pos_y; + +	__le16 bb_size_x;  /* size of bounding box */ +	__le16 bb_size_y; + +	__le16 pos_x;      /* finger tip position */ +	__le16 pos_y; + +	__le16 ctr_x;      /* centroid position */ +	__le16 ctr_y; + +	__le16 axis_x;     /* somehow related to major/minor axis, mostly: */ +	__le16 axis_y;     /* axis_x == bb_size_y && axis_y == bb_size_x */ + +	__le32 angle;      /* orientation in radians relative to x axis - +	                      actually an IEEE754 float, don't use in kernel */ + +	__le32 area;       /* size in pixels/pressure (?) */ + +	u8 padding[32]; + +} __packed; + +/* combined header/blob data */ +struct sur40_data { +	struct sur40_header header; +	struct sur40_blob   blobs[]; +} __packed; + + +/* version information */ +#define DRIVER_SHORT   "sur40" +#define DRIVER_AUTHOR  "Florian 'floe' Echtler <floe@butterbrot.org>" +#define DRIVER_DESC    "Surface2.0/SUR40/PixelSense input driver" + +/* vendor and device IDs */ +#define ID_MICROSOFT 0x045e +#define ID_SUR40     0x0775 + +/* sensor resolution */ +#define SENSOR_RES_X 1920 +#define SENSOR_RES_Y 1080 + +/* touch data endpoint */ +#define TOUCH_ENDPOINT 0x86 + +/* polling interval (ms) */ +#define POLL_INTERVAL 10 + +/* maximum number of contacts FIXME: this is a guess? */ +#define MAX_CONTACTS 64 + +/* control commands */ +#define SUR40_GET_VERSION 0xb0 /* 12 bytes string    */ +#define SUR40_UNKNOWN1    0xb3 /*  5 bytes           */ +#define SUR40_UNKNOWN2    0xc1 /* 24 bytes           */ + +#define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */ +#define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */ + +/* + * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT + * here by mistake which is very likely to have corrupted the firmware EEPROM + * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. + * Should you ever run into a similar problem, the background story to this + * incident and instructions on how to fix the corrupted EEPROM are available + * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html +*/ + +struct sur40_state { + +	struct usb_device *usbdev; +	struct device *dev; +	struct input_polled_dev *input; + +	struct sur40_data *bulk_in_buffer; +	size_t bulk_in_size; +	u8 bulk_in_epaddr; + +	char phys[64]; +}; + +static int sur40_command(struct sur40_state *dev, +			 u8 command, u16 index, void *buffer, u16 size) +{ +	return usb_control_msg(dev->usbdev, usb_rcvctrlpipe(dev->usbdev, 0), +			       command, +			       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, +			       0x00, index, buffer, size, 1000); +} + +/* Initialization routine, called from sur40_open */ +static int sur40_init(struct sur40_state *dev) +{ +	int result; +	u8 buffer[24]; + +	/* stupidly replay the original MS driver init sequence */ +	result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12); +	if (result < 0) +		return result; + +	result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12); +	if (result < 0) +		return result; + +	result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12); +	if (result < 0) +		return result; + +	result = sur40_command(dev, SUR40_UNKNOWN2,    0x00, buffer, 24); +	if (result < 0) +		return result; + +	result = sur40_command(dev, SUR40_UNKNOWN1,    0x00, buffer,  5); +	if (result < 0) +		return result; + +	result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12); + +	/* +	 * Discard the result buffer - no known data inside except +	 * some version strings, maybe extract these sometime... +	 */ + +	return result; +} + +/* + * Callback routines from input_polled_dev + */ + +/* Enable the device, polling will now start. */ +static void sur40_open(struct input_polled_dev *polldev) +{ +	struct sur40_state *sur40 = polldev->private; + +	dev_dbg(sur40->dev, "open\n"); +	sur40_init(sur40); +} + +/* Disable device, polling has stopped. */ +static void sur40_close(struct input_polled_dev *polldev) +{ +	struct sur40_state *sur40 = polldev->private; + +	dev_dbg(sur40->dev, "close\n"); +	/* +	 * There is no known way to stop the device, so we simply +	 * stop polling. +	 */ +} + +/* + * This function is called when a whole contact has been processed, + * so that it can assign it to a slot and store the data there. + */ +static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input) +{ +	int wide, major, minor; + +	int bb_size_x = le16_to_cpu(blob->bb_size_x); +	int bb_size_y = le16_to_cpu(blob->bb_size_y); + +	int pos_x = le16_to_cpu(blob->pos_x); +	int pos_y = le16_to_cpu(blob->pos_y); + +	int ctr_x = le16_to_cpu(blob->ctr_x); +	int ctr_y = le16_to_cpu(blob->ctr_y); + +	int slotnum = input_mt_get_slot_by_key(input, blob->blob_id); +	if (slotnum < 0 || slotnum >= MAX_CONTACTS) +		return; + +	input_mt_slot(input, slotnum); +	input_mt_report_slot_state(input, MT_TOOL_FINGER, 1); +	wide = (bb_size_x > bb_size_y); +	major = max(bb_size_x, bb_size_y); +	minor = min(bb_size_x, bb_size_y); + +	input_report_abs(input, ABS_MT_POSITION_X, pos_x); +	input_report_abs(input, ABS_MT_POSITION_Y, pos_y); +	input_report_abs(input, ABS_MT_TOOL_X, ctr_x); +	input_report_abs(input, ABS_MT_TOOL_Y, ctr_y); + +	/* TODO: use a better orientation measure */ +	input_report_abs(input, ABS_MT_ORIENTATION, wide); +	input_report_abs(input, ABS_MT_TOUCH_MAJOR, major); +	input_report_abs(input, ABS_MT_TOUCH_MINOR, minor); +} + +/* core function: poll for new input data */ +static void sur40_poll(struct input_polled_dev *polldev) +{ + +	struct sur40_state *sur40 = polldev->private; +	struct input_dev *input = polldev->input; +	int result, bulk_read, need_blobs, packet_blobs, i; +	u32 uninitialized_var(packet_id); + +	struct sur40_header *header = &sur40->bulk_in_buffer->header; +	struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0]; + +	dev_dbg(sur40->dev, "poll\n"); + +	need_blobs = -1; + +	do { + +		/* perform a blocking bulk read to get data from the device */ +		result = usb_bulk_msg(sur40->usbdev, +			usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr), +			sur40->bulk_in_buffer, sur40->bulk_in_size, +			&bulk_read, 1000); + +		dev_dbg(sur40->dev, "received %d bytes\n", bulk_read); + +		if (result < 0) { +			dev_err(sur40->dev, "error in usb_bulk_read\n"); +			return; +		} + +		result = bulk_read - sizeof(struct sur40_header); + +		if (result % sizeof(struct sur40_blob) != 0) { +			dev_err(sur40->dev, "transfer size mismatch\n"); +			return; +		} + +		/* first packet? */ +		if (need_blobs == -1) { +			need_blobs = le16_to_cpu(header->count); +			dev_dbg(sur40->dev, "need %d blobs\n", need_blobs); +			packet_id = le32_to_cpu(header->packet_id); +		} + +		/* +		 * Sanity check. when video data is also being retrieved, the +		 * packet ID will usually increase in the middle of a series +		 * instead of at the end. +		 */ +		if (packet_id != header->packet_id) +			dev_warn(sur40->dev, "packet ID mismatch\n"); + +		packet_blobs = result / sizeof(struct sur40_blob); +		dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs); + +		/* packets always contain at least 4 blobs, even if empty */ +		if (packet_blobs > need_blobs) +			packet_blobs = need_blobs; + +		for (i = 0; i < packet_blobs; i++) { +			need_blobs--; +			dev_dbg(sur40->dev, "processing blob\n"); +			sur40_report_blob(&(inblob[i]), input); +		} + +	} while (need_blobs > 0); + +	input_mt_sync_frame(input); +	input_sync(input); +} + +/* Initialize input device parameters. */ +static void sur40_input_setup(struct input_dev *input_dev) +{ +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(EV_ABS, input_dev->evbit); + +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, +			     0, SENSOR_RES_X, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, +			     0, SENSOR_RES_Y, 0, 0); + +	input_set_abs_params(input_dev, ABS_MT_TOOL_X, +			     0, SENSOR_RES_X, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_TOOL_Y, +			     0, SENSOR_RES_Y, 0, 0); + +	/* max value unknown, but major/minor axis +	 * can never be larger than screen */ +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, +			     0, SENSOR_RES_X, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, +			     0, SENSOR_RES_Y, 0, 0); + +	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); + +	input_mt_init_slots(input_dev, MAX_CONTACTS, +			    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); +} + +/* Check candidate USB interface. */ +static int sur40_probe(struct usb_interface *interface, +		       const struct usb_device_id *id) +{ +	struct usb_device *usbdev = interface_to_usbdev(interface); +	struct sur40_state *sur40; +	struct usb_host_interface *iface_desc; +	struct usb_endpoint_descriptor *endpoint; +	struct input_polled_dev *poll_dev; +	int error; + +	/* Check if we really have the right interface. */ +	iface_desc = &interface->altsetting[0]; +	if (iface_desc->desc.bInterfaceClass != 0xFF) +		return -ENODEV; + +	/* Use endpoint #4 (0x86). */ +	endpoint = &iface_desc->endpoint[4].desc; +	if (endpoint->bEndpointAddress != TOUCH_ENDPOINT) +		return -ENODEV; + +	/* Allocate memory for our device state and initialize it. */ +	sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL); +	if (!sur40) +		return -ENOMEM; + +	poll_dev = input_allocate_polled_device(); +	if (!poll_dev) { +		error = -ENOMEM; +		goto err_free_dev; +	} + +	/* Set up polled input device control structure */ +	poll_dev->private = sur40; +	poll_dev->poll_interval = POLL_INTERVAL; +	poll_dev->open = sur40_open; +	poll_dev->poll = sur40_poll; +	poll_dev->close = sur40_close; + +	/* Set up regular input device structure */ +	sur40_input_setup(poll_dev->input); + +	poll_dev->input->name = "Samsung SUR40"; +	usb_to_input_id(usbdev, &poll_dev->input->id); +	usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys)); +	strlcat(sur40->phys, "/input0", sizeof(sur40->phys)); +	poll_dev->input->phys = sur40->phys; +	poll_dev->input->dev.parent = &interface->dev; + +	sur40->usbdev = usbdev; +	sur40->dev = &interface->dev; +	sur40->input = poll_dev; + +	/* use the bulk-in endpoint tested above */ +	sur40->bulk_in_size = usb_endpoint_maxp(endpoint); +	sur40->bulk_in_epaddr = endpoint->bEndpointAddress; +	sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL); +	if (!sur40->bulk_in_buffer) { +		dev_err(&interface->dev, "Unable to allocate input buffer."); +		error = -ENOMEM; +		goto err_free_polldev; +	} + +	error = input_register_polled_device(poll_dev); +	if (error) { +		dev_err(&interface->dev, +			"Unable to register polled input device."); +		goto err_free_buffer; +	} + +	/* we can register the device now, as it is ready */ +	usb_set_intfdata(interface, sur40); +	dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC); + +	return 0; + +err_free_buffer: +	kfree(sur40->bulk_in_buffer); +err_free_polldev: +	input_free_polled_device(sur40->input); +err_free_dev: +	kfree(sur40); + +	return error; +} + +/* Unregister device & clean up. */ +static void sur40_disconnect(struct usb_interface *interface) +{ +	struct sur40_state *sur40 = usb_get_intfdata(interface); + +	input_unregister_polled_device(sur40->input); +	input_free_polled_device(sur40->input); +	kfree(sur40->bulk_in_buffer); +	kfree(sur40); + +	usb_set_intfdata(interface, NULL); +	dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC); +} + +static const struct usb_device_id sur40_table[] = { +	{ USB_DEVICE(ID_MICROSOFT, ID_SUR40) },  /* Samsung SUR40 */ +	{ }                                      /* terminating null entry */ +}; +MODULE_DEVICE_TABLE(usb, sur40_table); + +/* USB-specific object needed to register this driver with the USB subsystem. */ +static struct usb_driver sur40_driver = { +	.name = DRIVER_SHORT, +	.probe = sur40_probe, +	.disconnect = sur40_disconnect, +	.id_table = sur40_table, +}; + +module_usb_driver(sur40_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c new file mode 100644 index 00000000000..2ce649520fe --- /dev/null +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -0,0 +1,530 @@ +/* + * TI Touch Screen driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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/err.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include <linux/mfd/ti_am335x_tscadc.h> + +#define ADCFSM_STEPID		0x10 +#define SEQ_SETTLE		275 +#define MAX_12BIT		((1 << 12) - 1) + +static const int config_pins[] = { +	STEPCONFIG_XPP, +	STEPCONFIG_XNN, +	STEPCONFIG_YPP, +	STEPCONFIG_YNN, +}; + +struct titsc { +	struct input_dev	*input; +	struct ti_tscadc_dev	*mfd_tscadc; +	unsigned int		irq; +	unsigned int		wires; +	unsigned int		x_plate_resistance; +	bool			pen_down; +	int			coordinate_readouts; +	u32			config_inp[4]; +	u32			bit_xp, bit_xn, bit_yp, bit_yn; +	u32			inp_xp, inp_xn, inp_yp, inp_yn; +	u32			step_mask; +}; + +static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) +{ +	return readl(ts->mfd_tscadc->tscadc_base + reg); +} + +static void titsc_writel(struct titsc *tsc, unsigned int reg, +					unsigned int val) +{ +	writel(val, tsc->mfd_tscadc->tscadc_base + reg); +} + +static int titsc_config_wires(struct titsc *ts_dev) +{ +	u32 analog_line[4]; +	u32 wire_order[4]; +	int i, bit_cfg; + +	for (i = 0; i < 4; i++) { +		/* +		 * Get the order in which TSC wires are attached +		 * w.r.t. each of the analog input lines on the EVM. +		 */ +		analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4; +		wire_order[i] = ts_dev->config_inp[i] & 0x0F; +		if (WARN_ON(analog_line[i] > 7)) +			return -EINVAL; +		if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins))) +			return -EINVAL; +	} + +	for (i = 0; i < 4; i++) { +		int an_line; +		int wi_order; + +		an_line = analog_line[i]; +		wi_order = wire_order[i]; +		bit_cfg = config_pins[wi_order]; +		if (bit_cfg == 0) +			return -EINVAL; +		switch (wi_order) { +		case 0: +			ts_dev->bit_xp = bit_cfg; +			ts_dev->inp_xp = an_line; +			break; + +		case 1: +			ts_dev->bit_xn = bit_cfg; +			ts_dev->inp_xn = an_line; +			break; + +		case 2: +			ts_dev->bit_yp = bit_cfg; +			ts_dev->inp_yp = an_line; +			break; +		case 3: +			ts_dev->bit_yn = bit_cfg; +			ts_dev->inp_yn = an_line; +			break; +		} +	} +	return 0; +} + +static void titsc_step_config(struct titsc *ts_dev) +{ +	unsigned int	config; +	int i; +	int end_step; +	u32 stepenable; + +	config = STEPCONFIG_MODE_HWSYNC | +			STEPCONFIG_AVG_16 | ts_dev->bit_xp; +	switch (ts_dev->wires) { +	case 4: +		config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; +		break; +	case 5: +		config |= ts_dev->bit_yn | +				STEPCONFIG_INP_AN4 | ts_dev->bit_xn | +				ts_dev->bit_yp; +		break; +	case 8: +		config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; +		break; +	} + +	/* 1 … coordinate_readouts is for X */ +	end_step = ts_dev->coordinate_readouts; +	for (i = 0; i < end_step; i++) { +		titsc_writel(ts_dev, REG_STEPCONFIG(i), config); +		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); +	} + +	config = 0; +	config = STEPCONFIG_MODE_HWSYNC | +			STEPCONFIG_AVG_16 | ts_dev->bit_yn | +			STEPCONFIG_INM_ADCREFM; +	switch (ts_dev->wires) { +	case 4: +		config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); +		break; +	case 5: +		config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 | +				ts_dev->bit_xn | ts_dev->bit_yp; +		break; +	case 8: +		config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); +		break; +	} + +	/* coordinate_readouts … coordinate_readouts * 2 is for Y */ +	end_step = ts_dev->coordinate_readouts * 2; +	for (i = ts_dev->coordinate_readouts; i < end_step; i++) { +		titsc_writel(ts_dev, REG_STEPCONFIG(i), config); +		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); +	} + +	/* Charge step configuration */ +	config = ts_dev->bit_xp | ts_dev->bit_yn | +			STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | +			STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp); + +	titsc_writel(ts_dev, REG_CHARGECONFIG, config); +	titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); + +	/* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */ +	config = STEPCONFIG_MODE_HWSYNC | +			STEPCONFIG_AVG_16 | ts_dev->bit_yp | +			ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | +			STEPCONFIG_INP(ts_dev->inp_xp); +	titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); +	titsc_writel(ts_dev, REG_STEPDELAY(end_step), +			STEPCONFIG_OPENDLY); + +	end_step++; +	config |= STEPCONFIG_INP(ts_dev->inp_yn); +	titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); +	titsc_writel(ts_dev, REG_STEPDELAY(end_step), +			STEPCONFIG_OPENDLY); + +	/* The steps1 … end and bit 0 for TS_Charge */ +	stepenable = (1 << (end_step + 2)) - 1; +	ts_dev->step_mask = stepenable; +	am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); +} + +static void titsc_read_coordinates(struct titsc *ts_dev, +		u32 *x, u32 *y, u32 *z1, u32 *z2) +{ +	unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); +	unsigned int prev_val_x = ~0, prev_val_y = ~0; +	unsigned int prev_diff_x = ~0, prev_diff_y = ~0; +	unsigned int read, diff; +	unsigned int i, channel; +	unsigned int creads = ts_dev->coordinate_readouts; + +	*z1 = *z2 = 0; +	if (fifocount % (creads * 2 + 2)) +		fifocount -= fifocount % (creads * 2 + 2); +	/* +	 * Delta filter is used to remove large variations in sampled +	 * values from ADC. The filter tries to predict where the next +	 * coordinate could be. This is done by taking a previous +	 * coordinate and subtracting it form current one. Further the +	 * algorithm compares the difference with that of a present value, +	 * if true the value is reported to the sub system. +	 */ +	for (i = 0; i < fifocount; i++) { +		read = titsc_readl(ts_dev, REG_FIFO0); + +		channel = (read & 0xf0000) >> 16; +		read &= 0xfff; +		if (channel < creads) { +			diff = abs(read - prev_val_x); +			if (diff < prev_diff_x) { +				prev_diff_x = diff; +				*x = read; +			} +			prev_val_x = read; + +		} else if (channel < creads * 2) { +			diff = abs(read - prev_val_y); +			if (diff < prev_diff_y) { +				prev_diff_y = diff; +				*y = read; +			} +			prev_val_y = read; + +		} else if (channel < creads * 2 + 1) { +			*z1 = read; + +		} else if (channel < creads * 2 + 2) { +			*z2 = read; +		} +	} +} + +static irqreturn_t titsc_irq(int irq, void *dev) +{ +	struct titsc *ts_dev = dev; +	struct input_dev *input_dev = ts_dev->input; +	unsigned int status, irqclr = 0; +	unsigned int x = 0, y = 0; +	unsigned int z1, z2, z; +	unsigned int fsm; + +	status = titsc_readl(ts_dev, REG_IRQSTATUS); +	/* +	 * ADC and touchscreen share the IRQ line. +	 * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only +	 */ +	if (status & IRQENB_FIFO0THRES) { + +		titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2); + +		if (ts_dev->pen_down && z1 != 0 && z2 != 0) { +			/* +			 * Calculate pressure using formula +			 * Resistance(touch) = x plate resistance * +			 * x postion/4096 * ((z2 / z1) - 1) +			 */ +			z = z1 - z2; +			z *= x; +			z *= ts_dev->x_plate_resistance; +			z /= z2; +			z = (z + 2047) >> 12; + +			if (z <= MAX_12BIT) { +				input_report_abs(input_dev, ABS_X, x); +				input_report_abs(input_dev, ABS_Y, y); +				input_report_abs(input_dev, ABS_PRESSURE, z); +				input_report_key(input_dev, BTN_TOUCH, 1); +				input_sync(input_dev); +			} +		} +		irqclr |= IRQENB_FIFO0THRES; +	} + +	/* +	 * Time for sequencer to settle, to read +	 * correct state of the sequencer. +	 */ +	udelay(SEQ_SETTLE); + +	status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); +	if (status & IRQENB_PENUP) { +		/* Pen up event */ +		fsm = titsc_readl(ts_dev, REG_ADCFSM); +		if (fsm == ADCFSM_STEPID) { +			ts_dev->pen_down = false; +			input_report_key(input_dev, BTN_TOUCH, 0); +			input_report_abs(input_dev, ABS_PRESSURE, 0); +			input_sync(input_dev); +		} else { +			ts_dev->pen_down = true; +		} +		irqclr |= IRQENB_PENUP; +	} + +	if (status & IRQENB_HW_PEN) { + +		titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); +		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); +	} + +	if (irqclr) { +		titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); +		am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); +		return IRQ_HANDLED; +	} +	return IRQ_NONE; +} + +static int titsc_parse_dt(struct platform_device *pdev, +					struct titsc *ts_dev) +{ +	struct device_node *node = pdev->dev.of_node; +	int err; + +	if (!node) +		return -EINVAL; + +	err = of_property_read_u32(node, "ti,wires", &ts_dev->wires); +	if (err < 0) +		return err; +	switch (ts_dev->wires) { +	case 4: +	case 5: +	case 8: +		break; +	default: +		return -EINVAL; +	} + +	err = of_property_read_u32(node, "ti,x-plate-resistance", +			&ts_dev->x_plate_resistance); +	if (err < 0) +		return err; + +	/* +	 * Try with the new binding first. If it fails, try again with +	 * bogus, miss-spelled version. +	 */ +	err = of_property_read_u32(node, "ti,coordinate-readouts", +			&ts_dev->coordinate_readouts); +	if (err < 0) { +		dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n"); +		err = of_property_read_u32(node, "ti,coordiante-readouts", +				&ts_dev->coordinate_readouts); +	} + +	if (err < 0) +		return err; + +	return of_property_read_u32_array(node, "ti,wire-config", +			ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp)); +} + +/* + * The functions for inserting/removing driver as a module. + */ + +static int titsc_probe(struct platform_device *pdev) +{ +	struct titsc *ts_dev; +	struct input_dev *input_dev; +	struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); +	int err; + +	/* Allocate memory for device */ +	ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!ts_dev || !input_dev) { +		dev_err(&pdev->dev, "failed to allocate memory.\n"); +		err = -ENOMEM; +		goto err_free_mem; +	} + +	tscadc_dev->tsc = ts_dev; +	ts_dev->mfd_tscadc = tscadc_dev; +	ts_dev->input = input_dev; +	ts_dev->irq = tscadc_dev->irq; + +	err = titsc_parse_dt(pdev, ts_dev); +	if (err) { +		dev_err(&pdev->dev, "Could not find valid DT data.\n"); +		goto err_free_mem; +	} + +	err = request_irq(ts_dev->irq, titsc_irq, +			  IRQF_SHARED, pdev->dev.driver->name, ts_dev); +	if (err) { +		dev_err(&pdev->dev, "failed to allocate irq.\n"); +		goto err_free_mem; +	} + +	titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); +	err = titsc_config_wires(ts_dev); +	if (err) { +		dev_err(&pdev->dev, "wrong i/p wire configuration\n"); +		goto err_free_irq; +	} +	titsc_step_config(ts_dev); +	titsc_writel(ts_dev, REG_FIFO0THR, +			ts_dev->coordinate_readouts * 2 + 2 - 1); + +	input_dev->name = "ti-tsc"; +	input_dev->dev.parent = &pdev->dev; + +	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + +	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); +	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + +	/* register to the input system */ +	err = input_register_device(input_dev); +	if (err) +		goto err_free_irq; + +	platform_set_drvdata(pdev, ts_dev); +	return 0; + +err_free_irq: +	free_irq(ts_dev->irq, ts_dev); +err_free_mem: +	input_free_device(input_dev); +	kfree(ts_dev); +	return err; +} + +static int titsc_remove(struct platform_device *pdev) +{ +	struct titsc *ts_dev = platform_get_drvdata(pdev); +	u32 steps; + +	free_irq(ts_dev->irq, ts_dev); + +	/* total steps followed by the enable mask */ +	steps = 2 * ts_dev->coordinate_readouts + 2; +	steps = (1 << steps) - 1; +	am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps); + +	input_unregister_device(ts_dev->input); + +	kfree(ts_dev); +	return 0; +} + +#ifdef CONFIG_PM +static int titsc_suspend(struct device *dev) +{ +	struct titsc *ts_dev = dev_get_drvdata(dev); +	struct ti_tscadc_dev *tscadc_dev; +	unsigned int idle; + +	tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); +	if (device_may_wakeup(tscadc_dev->dev)) { +		idle = titsc_readl(ts_dev, REG_IRQENABLE); +		titsc_writel(ts_dev, REG_IRQENABLE, +				(idle | IRQENB_HW_PEN)); +		titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); +	} +	return 0; +} + +static int titsc_resume(struct device *dev) +{ +	struct titsc *ts_dev = dev_get_drvdata(dev); +	struct ti_tscadc_dev *tscadc_dev; + +	tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); +	if (device_may_wakeup(tscadc_dev->dev)) { +		titsc_writel(ts_dev, REG_IRQWAKEUP, +				0x00); +		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); +	} +	titsc_step_config(ts_dev); +	titsc_writel(ts_dev, REG_FIFO0THR, +			ts_dev->coordinate_readouts * 2 + 2 - 1); +	return 0; +} + +static const struct dev_pm_ops titsc_pm_ops = { +	.suspend = titsc_suspend, +	.resume  = titsc_resume, +}; +#define TITSC_PM_OPS (&titsc_pm_ops) +#else +#define TITSC_PM_OPS NULL +#endif + +static const struct of_device_id ti_tsc_dt_ids[] = { +	{ .compatible = "ti,am3359-tsc", }, +	{ } +}; +MODULE_DEVICE_TABLE(of, ti_tsc_dt_ids); + +static struct platform_driver ti_tsc_driver = { +	.probe	= titsc_probe, +	.remove	= titsc_remove, +	.driver	= { +		.name   = "TI-am335x-tsc", +		.owner	= THIS_MODULE, +		.pm	= TITSC_PM_OPS, +		.of_match_table = ti_tsc_dt_ids, +	}, +}; +module_platform_driver(ti_tsc_driver); + +MODULE_DESCRIPTION("TI touchscreen controller driver"); +MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c deleted file mode 100644 index cf1dba2e267..00000000000 --- a/drivers/input/touchscreen/tnetv107x-ts.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Texas Instruments TNETV107X Touchscreen Driver - * - * Copyright (C) 2010 Texas Instruments - * - * 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 version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; 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/errno.h> -#include <linux/input.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/ctype.h> -#include <linux/io.h> -#include <linux/clk.h> - -#include <mach/tnetv107x.h> - -#define TSC_PENUP_POLL		(HZ / 5) -#define IDLE_TIMEOUT		100 /* msec */ - -/* - * The first and last samples of a touch interval are usually garbage and need - * to be filtered out with these devices.  The following definitions control - * the number of samples skipped. - */ -#define TSC_HEAD_SKIP		1 -#define TSC_TAIL_SKIP		1 -#define TSC_SKIP		(TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1) -#define TSC_SAMPLES		(TSC_SKIP + 1) - -/* Register Offsets */ -struct tsc_regs { -	u32	rev; -	u32	tscm; -	u32	bwcm; -	u32	swc; -	u32	adcchnl; -	u32	adcdata; -	u32	chval[4]; -}; - -/* TSC Mode Configuration Register (tscm) bits */ -#define WMODE		BIT(0) -#define TSKIND		BIT(1) -#define ZMEASURE_EN	BIT(2) -#define IDLE		BIT(3) -#define TSC_EN		BIT(4) -#define STOP		BIT(5) -#define ONE_SHOT	BIT(6) -#define SINGLE		BIT(7) -#define AVG		BIT(8) -#define AVGNUM(x)	(((x) & 0x03) <<  9) -#define PVSTC(x)	(((x) & 0x07) << 11) -#define PON		BIT(14) -#define PONBG		BIT(15) -#define AFERST		BIT(16) - -/* ADC DATA Capture Register bits */ -#define DATA_VALID	BIT(16) - -/* Register Access Macros */ -#define tsc_read(ts, reg)		__raw_readl(&(ts)->regs->reg) -#define tsc_write(ts, reg, val)		__raw_writel(val, &(ts)->regs->reg); -#define tsc_set_bits(ts, reg, val)	\ -	tsc_write(ts, reg, tsc_read(ts, reg) | (val)) -#define tsc_clr_bits(ts, reg, val)	\ -	tsc_write(ts, reg, tsc_read(ts, reg) & ~(val)) - -struct sample { -	int x, y, p; -}; - -struct tsc_data { -	struct input_dev		*input_dev; -	struct resource			*res; -	struct tsc_regs __iomem		*regs; -	struct timer_list		timer; -	spinlock_t			lock; -	struct clk			*clk; -	struct device			*dev; -	int				sample_count; -	struct sample			samples[TSC_SAMPLES]; -	int				tsc_irq; -}; - -static int tsc_read_sample(struct tsc_data *ts, struct sample* sample) -{ -	int	x, y, z1, z2, t, p = 0; -	u32	val; - -	val = tsc_read(ts, chval[0]); -	if (val & DATA_VALID) -		x = val & 0xffff; -	else -		return -EINVAL; - -	y  = tsc_read(ts, chval[1]) & 0xffff; -	z1 = tsc_read(ts, chval[2]) & 0xffff; -	z2 = tsc_read(ts, chval[3]) & 0xffff; - -	if (z1) { -		t = ((600 * x) * (z2 - z1)); -		p = t / (u32) (z1 << 12); -		if (p < 0) -			p = 0; -	} - -	sample->x  = x; -	sample->y  = y; -	sample->p  = p; - -	return 0; -} - -static void tsc_poll(unsigned long data) -{ -	struct tsc_data *ts = (struct tsc_data *)data; -	unsigned long flags; -	int i, val, x, y, p; - -	spin_lock_irqsave(&ts->lock, flags); - -	if (ts->sample_count >= TSC_SKIP) { -		input_report_abs(ts->input_dev, ABS_PRESSURE, 0); -		input_report_key(ts->input_dev, BTN_TOUCH, 0); -		input_sync(ts->input_dev); -	} else if (ts->sample_count > 0) { -		/* -		 * A touch event lasted less than our skip count.  Salvage and -		 * report anyway. -		 */ -		for (i = 0, val = 0; i < ts->sample_count; i++) -			val += ts->samples[i].x; -		x = val / ts->sample_count; - -		for (i = 0, val = 0; i < ts->sample_count; i++) -			val += ts->samples[i].y; -		y = val / ts->sample_count; - -		for (i = 0, val = 0; i < ts->sample_count; i++) -			val += ts->samples[i].p; -		p = val / ts->sample_count; - -		input_report_abs(ts->input_dev, ABS_X, x); -		input_report_abs(ts->input_dev, ABS_Y, y); -		input_report_abs(ts->input_dev, ABS_PRESSURE, p); -		input_report_key(ts->input_dev, BTN_TOUCH, 1); -		input_sync(ts->input_dev); -	} - -	ts->sample_count = 0; - -	spin_unlock_irqrestore(&ts->lock, flags); -} - -static irqreturn_t tsc_irq(int irq, void *dev_id) -{ -	struct tsc_data *ts = (struct tsc_data *)dev_id; -	struct sample *sample; -	int index; - -	spin_lock(&ts->lock); - -	index = ts->sample_count % TSC_SAMPLES; -	sample = &ts->samples[index]; -	if (tsc_read_sample(ts, sample) < 0) -		goto out; - -	if (++ts->sample_count >= TSC_SKIP) { -		index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES; -		sample = &ts->samples[index]; - -		input_report_abs(ts->input_dev, ABS_X, sample->x); -		input_report_abs(ts->input_dev, ABS_Y, sample->y); -		input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p); -		if (ts->sample_count == TSC_SKIP) -			input_report_key(ts->input_dev, BTN_TOUCH, 1); -		input_sync(ts->input_dev); -	} -	mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL); -out: -	spin_unlock(&ts->lock); -	return IRQ_HANDLED; -} - -static int tsc_start(struct input_dev *dev) -{ -	struct tsc_data *ts = input_get_drvdata(dev); -	unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT); -	u32 val; - -	clk_enable(ts->clk); - -	/* Go to idle mode, before any initialization */ -	while (time_after(timeout, jiffies)) { -		if (tsc_read(ts, tscm) & IDLE) -			break; -	} - -	if (time_before(timeout, jiffies)) { -		dev_warn(ts->dev, "timeout waiting for idle\n"); -		clk_disable(ts->clk); -		return -EIO; -	} - -	/* Configure TSC Control register*/ -	val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN); -	tsc_write(ts, tscm, val); - -	/* Bring TSC out of reset: Clear AFE reset bit */ -	val &= ~(AFERST); -	tsc_write(ts, tscm, val); - -	/* Configure all pins for hardware control*/ -	tsc_write(ts, bwcm, 0); - -	/* Finally enable the TSC */ -	tsc_set_bits(ts, tscm, TSC_EN); - -	return 0; -} - -static void tsc_stop(struct input_dev *dev) -{ -	struct tsc_data *ts = input_get_drvdata(dev); - -	tsc_clr_bits(ts, tscm, TSC_EN); -	synchronize_irq(ts->tsc_irq); -	del_timer_sync(&ts->timer); -	clk_disable(ts->clk); -} - -static int __devinit tsc_probe(struct platform_device *pdev) -{ -	struct device *dev = &pdev->dev; -	struct tsc_data *ts; -	int error = 0; -	u32 rev = 0; - -	ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL); -	if (!ts) { -		dev_err(dev, "cannot allocate device info\n"); -		return -ENOMEM; -	} - -	ts->dev = dev; -	spin_lock_init(&ts->lock); -	setup_timer(&ts->timer, tsc_poll, (unsigned long)ts); -	platform_set_drvdata(pdev, ts); - -	ts->tsc_irq = platform_get_irq(pdev, 0); -	if (ts->tsc_irq < 0) { -		dev_err(dev, "cannot determine device interrupt\n"); -		error = -ENODEV; -		goto error_res; -	} - -	ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!ts->res) { -		dev_err(dev, "cannot determine register area\n"); -		error = -ENODEV; -		goto error_res; -	} - -	if (!request_mem_region(ts->res->start, resource_size(ts->res), -				pdev->name)) { -		dev_err(dev, "cannot claim register memory\n"); -		ts->res = NULL; -		error = -EINVAL; -		goto error_res; -	} - -	ts->regs = ioremap(ts->res->start, resource_size(ts->res)); -	if (!ts->regs) { -		dev_err(dev, "cannot map register memory\n"); -		error = -ENOMEM; -		goto error_map; -	} - -	ts->clk = clk_get(dev, NULL); -	if (!ts->clk) { -		dev_err(dev, "cannot claim device clock\n"); -		error = -EINVAL; -		goto error_clk; -	} - -	error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, 0, -				     dev_name(dev), ts); -	if (error < 0) { -		dev_err(ts->dev, "Could not allocate ts irq\n"); -		goto error_irq; -	} - -	ts->input_dev = input_allocate_device(); -	if (!ts->input_dev) { -		dev_err(dev, "cannot allocate input device\n"); -		error = -ENOMEM; -		goto error_input; -	} -	input_set_drvdata(ts->input_dev, ts); - -	ts->input_dev->name       = pdev->name; -	ts->input_dev->id.bustype = BUS_HOST; -	ts->input_dev->dev.parent = &pdev->dev; -	ts->input_dev->open	  = tsc_start; -	ts->input_dev->close	  = tsc_stop; - -	clk_enable(ts->clk); -	rev = tsc_read(ts, rev); -	ts->input_dev->id.product = ((rev >>  8) & 0x07); -	ts->input_dev->id.version = ((rev >> 16) & 0xfff); -	clk_disable(ts->clk); - -	__set_bit(EV_KEY,    ts->input_dev->evbit); -	__set_bit(EV_ABS,    ts->input_dev->evbit); -	__set_bit(BTN_TOUCH, ts->input_dev->keybit); - -	input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0); -	input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0); -	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0); - -	error = input_register_device(ts->input_dev); -	if (error < 0) { -		dev_err(dev, "failed input device registration\n"); -		goto error_reg; -	} - -	return 0; - -error_reg: -	input_free_device(ts->input_dev); -error_input: -	free_irq(ts->tsc_irq, ts); -error_irq: -	clk_put(ts->clk); -error_clk: -	iounmap(ts->regs); -error_map: -	release_mem_region(ts->res->start, resource_size(ts->res)); -error_res: -	platform_set_drvdata(pdev, NULL); -	kfree(ts); - -	return error; -} - -static int __devexit tsc_remove(struct platform_device *pdev) -{ -	struct tsc_data *ts = platform_get_drvdata(pdev); - -	input_unregister_device(ts->input_dev); -	free_irq(ts->tsc_irq, ts); -	clk_put(ts->clk); -	iounmap(ts->regs); -	release_mem_region(ts->res->start, resource_size(ts->res)); -	platform_set_drvdata(pdev, NULL); -	kfree(ts); - -	return 0; -} - -static struct platform_driver tsc_driver = { -	.probe		= tsc_probe, -	.remove		= __devexit_p(tsc_remove), -	.driver.name	= "tnetv107x-ts", -	.driver.owner	= THIS_MODULE, -}; - -static int __init tsc_init(void) -{ -	return platform_driver_register(&tsc_driver); -} - -static void __exit tsc_exit(void) -{ -	platform_driver_unregister(&tsc_driver); -} - -module_init(tsc_init); -module_exit(tsc_exit); - -MODULE_AUTHOR("Cyril Chemparathy"); -MODULE_DESCRIPTION("TNETV107X Touchscreen Driver"); -MODULE_ALIAS("platform: tnetv107x-ts"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/touchit213.c b/drivers/input/touchscreen/touchit213.c index d1297ba19da..c27cf8f3d1c 100644 --- a/drivers/input/touchscreen/touchit213.c +++ b/drivers/input/touchscreen/touchit213.c @@ -21,7 +21,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Sahara TouchIT-213 serial touchscreen driver" @@ -216,19 +215,4 @@ static struct serio_driver touchit213_drv = {  	.disconnect	= touchit213_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init touchit213_init(void) -{ -	return serio_register_driver(&touchit213_drv); -} - -static void __exit touchit213_exit(void) -{ -	serio_unregister_driver(&touchit213_drv); -} - -module_init(touchit213_init); -module_exit(touchit213_exit); +module_serio_driver(touchit213_drv); diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c index 3a5c142c2a7..4000e520540 100644 --- a/drivers/input/touchscreen/touchright.c +++ b/drivers/input/touchscreen/touchright.c @@ -20,7 +20,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Touchright serial touchscreen driver" @@ -176,19 +175,4 @@ static struct serio_driver tr_drv = {  	.disconnect	= tr_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init tr_init(void) -{ -	return serio_register_driver(&tr_drv); -} - -static void __exit tr_exit(void) -{ -	serio_unregister_driver(&tr_drv); -} - -module_init(tr_init); -module_exit(tr_exit); +module_serio_driver(tr_drv); diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c index 763a656a59f..ba90f447df7 100644 --- a/drivers/input/touchscreen/touchwin.c +++ b/drivers/input/touchscreen/touchwin.c @@ -27,7 +27,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/serio.h> -#include <linux/init.h>  #define DRIVER_DESC	"Touchwindow serial touchscreen driver" @@ -183,19 +182,4 @@ static struct serio_driver tw_drv = {  	.disconnect	= tw_disconnect,  }; -/* - * The functions for inserting/removing us as a module. - */ - -static int __init tw_init(void) -{ -	return serio_register_driver(&tw_drv); -} - -static void __exit tw_exit(void) -{ -	serio_unregister_driver(&tw_drv); -} - -module_init(tw_init); -module_exit(tw_exit); +module_serio_driver(tw_drv); diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index c8c136cf7bb..94cde2cb149 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -1,6 +1,4 @@  /* - * drivers/input/touchscreen/tps6507x_ts.c - *   * Touchscreen driver for the tps6507x chip.   *   * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) @@ -19,6 +17,7 @@  #include <linux/workqueue.h>  #include <linux/slab.h>  #include <linux/input.h> +#include <linux/input-polldev.h>  #include <linux/platform_device.h>  #include <linux/mfd/tps6507x.h>  #include <linux/input/tps6507x-ts.h> @@ -40,21 +39,13 @@ struct ts_event {  };  struct tps6507x_ts { -	struct input_dev	*input_dev;  	struct device		*dev; +	struct input_polled_dev	*poll_dev; +	struct tps6507x_dev	*mfd;  	char			phys[32]; -	struct workqueue_struct *wq; -	struct delayed_work	work; -	unsigned		polling;	/* polling is active */  	struct ts_event		tc; -	struct tps6507x_dev	*mfd; -	u16			model; -	unsigned		pendown; -	int			irq; -	void			(*clear_penirq)(void); -	unsigned long		poll_period;	/* ms */  	u16			min_pressure; -	int			vref;		/* non-zero to leave vref on */ +	bool			pendown;  };  static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data) @@ -164,18 +155,15 @@ static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)  	return ret;  } -static void tps6507x_ts_handler(struct work_struct *work) +static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)  { -	struct tps6507x_ts *tsc =  container_of(work, -				struct tps6507x_ts, work.work); -	struct input_dev *input_dev = tsc->input_dev; -	int pendown; -	int schd; -	int poll = 0; +	struct tps6507x_ts *tsc = poll_dev->private; +	struct input_dev *input_dev = poll_dev->input; +	bool pendown;  	s32 ret; -	ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, -				       &tsc->tc.pressure); +	ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, +				      &tsc->tc.pressure);  	if (ret)  		goto done; @@ -186,7 +174,7 @@ static void tps6507x_ts_handler(struct work_struct *work)  		input_report_key(input_dev, BTN_TOUCH, 0);  		input_report_abs(input_dev, ABS_PRESSURE, 0);  		input_sync(input_dev); -		tsc->pendown = 0; +		tsc->pendown = false;  	}  	if (pendown) { @@ -211,76 +199,69 @@ static void tps6507x_ts_handler(struct work_struct *work)  		input_report_abs(input_dev, ABS_Y, tsc->tc.y);  		input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);  		input_sync(input_dev); -		tsc->pendown = 1; -		poll = 1; +		tsc->pendown = true;  	}  done: -	/* always poll if not using interrupts */ -	poll = 1; - -	if (poll) { -		schd = queue_delayed_work(tsc->wq, &tsc->work, -					  msecs_to_jiffies(tsc->poll_period)); -		if (schd) -			tsc->polling = 1; -		else { -			tsc->polling = 0; -			dev_err(tsc->dev, "re-schedule failed"); -		} -	} else -		tsc->polling = 0; - -	ret = tps6507x_adc_standby(tsc); +	tps6507x_adc_standby(tsc);  }  static int tps6507x_ts_probe(struct platform_device *pdev)  { -	int error; -	struct tps6507x_ts *tsc;  	struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); -	struct touchscreen_init_data *init_data; +	const struct tps6507x_board *tps_board; +	const struct touchscreen_init_data *init_data; +	struct tps6507x_ts *tsc; +	struct input_polled_dev *poll_dev;  	struct input_dev *input_dev; -	struct tps6507x_board *tps_board; -	int schd; +	int error; -	/** +	/*  	 * tps_board points to pmic related constants  	 * coming from the board-evm file.  	 */ - -	tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data; - +	tps_board = dev_get_platdata(tps6507x_dev->dev);  	if (!tps_board) {  		dev_err(tps6507x_dev->dev,  			"Could not find tps6507x platform data\n"); -		return -EIO; +		return -ENODEV;  	} -	/** +	/*  	 * init_data points to array of regulator_init structures  	 * coming from the board-evm file.  	 */ -  	init_data = tps_board->tps6507x_ts_init_data;  	tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);  	if (!tsc) {  		dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); -		error = -ENOMEM; -		goto err0; +		return -ENOMEM;  	} -	tps6507x_dev->ts = tsc;  	tsc->mfd = tps6507x_dev;  	tsc->dev = tps6507x_dev->dev; -	input_dev = input_allocate_device(); -	if (!input_dev) { -		dev_err(tsc->dev, "Failed to allocate input device.\n"); +	tsc->min_pressure = init_data ? +			init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE; + +	snprintf(tsc->phys, sizeof(tsc->phys), +		 "%s/input0", dev_name(tsc->dev)); + +	poll_dev = input_allocate_polled_device(); +	if (!poll_dev) { +		dev_err(tsc->dev, "Failed to allocate polled input device.\n");  		error = -ENOMEM; -		goto err1; +		goto err_free_mem;  	} +	tsc->poll_dev = poll_dev; + +	poll_dev->private = tsc; +	poll_dev->poll = tps6507x_ts_poll; +	poll_dev->poll_interval = init_data ? +			init_data->poll_period : TSC_DEFAULT_POLL_PERIOD; + +	input_dev = poll_dev->input;  	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); @@ -289,79 +270,42 @@ static int tps6507x_ts_probe(struct platform_device *pdev)  	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);  	input_dev->name = "TPS6507x Touchscreen"; -	input_dev->id.bustype = BUS_I2C; -	input_dev->dev.parent = tsc->dev; - -	snprintf(tsc->phys, sizeof(tsc->phys), -		 "%s/input0", dev_name(tsc->dev));  	input_dev->phys = tsc->phys; - -	dev_dbg(tsc->dev, "device: %s\n", input_dev->phys); - -	input_set_drvdata(input_dev, tsc); - -	tsc->input_dev = input_dev; - -	INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler); -	tsc->wq = create_workqueue("TPS6507x Touchscreen"); - +	input_dev->dev.parent = tsc->dev; +	input_dev->id.bustype = BUS_I2C;  	if (init_data) { -		tsc->poll_period = init_data->poll_period; -		tsc->vref = init_data->vref; -		tsc->min_pressure = init_data->min_pressure;  		input_dev->id.vendor = init_data->vendor;  		input_dev->id.product = init_data->product;  		input_dev->id.version = init_data->version; -	} else { -		tsc->poll_period = TSC_DEFAULT_POLL_PERIOD; -		tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE;  	}  	error = tps6507x_adc_standby(tsc);  	if (error) -		goto err2; +		goto err_free_polled_dev; -	error = input_register_device(input_dev); +	error = input_register_polled_device(poll_dev);  	if (error) -		goto err2; - -	schd = queue_delayed_work(tsc->wq, &tsc->work, -				  msecs_to_jiffies(tsc->poll_period)); +		goto err_free_polled_dev; -	if (schd) -		tsc->polling = 1; -	else { -		tsc->polling = 0; -		dev_err(tsc->dev, "schedule failed"); -		goto err2; -	 } -	platform_set_drvdata(pdev, tps6507x_dev); +	platform_set_drvdata(pdev, tsc);  	return 0; -err2: -	cancel_delayed_work_sync(&tsc->work); -	destroy_workqueue(tsc->wq); -	input_free_device(input_dev); -err1: +err_free_polled_dev: +	input_free_polled_device(poll_dev); +err_free_mem:  	kfree(tsc); -	tps6507x_dev->ts = NULL; -err0:  	return error;  } -static int __devexit tps6507x_ts_remove(struct platform_device *pdev) +static int tps6507x_ts_remove(struct platform_device *pdev)  { -	struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); -	struct tps6507x_ts *tsc = tps6507x_dev->ts; -	struct input_dev *input_dev = tsc->input_dev; - -	cancel_delayed_work_sync(&tsc->work); -	destroy_workqueue(tsc->wq); +	struct tps6507x_ts *tsc = platform_get_drvdata(pdev); +	struct input_polled_dev *poll_dev = tsc->poll_dev; -	input_unregister_device(input_dev); +	input_unregister_polled_device(poll_dev); +	input_free_polled_device(poll_dev); -	tps6507x_dev->ts = NULL;  	kfree(tsc);  	return 0; @@ -373,22 +317,11 @@ static struct platform_driver tps6507x_ts_driver = {  		.owner = THIS_MODULE,  	},  	.probe = tps6507x_ts_probe, -	.remove = __devexit_p(tps6507x_ts_remove), +	.remove = tps6507x_ts_remove,  }; - -static int __init tps6507x_ts_init(void) -{ -	return platform_driver_register(&tps6507x_ts_driver); -} -module_init(tps6507x_ts_init); - -static void __exit tps6507x_ts_exit(void) -{ -	platform_driver_unregister(&tps6507x_ts_driver); -} -module_exit(tps6507x_ts_exit); +module_platform_driver(tps6507x_ts_driver);  MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");  MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");  MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tps6507x-tsc"); +MODULE_ALIAS("platform:tps6507x-ts"); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c new file mode 100644 index 00000000000..52380b68ebd --- /dev/null +++ b/drivers/input/touchscreen/tsc2005.c @@ -0,0 +1,829 @@ +/* + * TSC2005 touchscreen driver + * + * Copyright (C) 2006-2010 Nokia Corporation + * + * 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. + * + * 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/of_gpio.h> +#include <linux/spi/spi.h> +#include <linux/spi/tsc2005.h> +#include <linux/regulator/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		0x0001 +#define TSC2005_REG_PND0		0x0002 +#define TSC2005_REG_X			0x0000 +#define TSC2005_REG_Y			0x0008 +#define TSC2005_REG_Z1			0x0010 +#define TSC2005_REG_Z2			0x0018 +#define TSC2005_REG_TEMP_HIGH		0x0050 +#define TSC2005_REG_CFR0		0x0060 +#define TSC2005_REG_CFR1		0x0068 +#define TSC2005_REG_CFR2		0x0070 + +/* 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 + +struct tsc2005_spi_rd { +	struct spi_transfer	spi_xfer; +	u32			spi_tx; +	u32			spi_rx; +}; + +struct tsc2005 { +	struct spi_device	*spi; + +	struct spi_message      spi_read_msg; +	struct tsc2005_spi_rd	spi_x; +	struct tsc2005_spi_rd	spi_y; +	struct tsc2005_spi_rd	spi_z1; +	struct tsc2005_spi_rd	spi_z2; + +	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; + +	int			reset_gpio; +	void			(*set_reset)(bool enable); +}; + +static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) +{ +	u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; +	struct spi_transfer xfer = { +		.tx_buf		= &tx, +		.len		= 1, +		.bits_per_word	= 8, +	}; +	struct spi_message msg; +	int error; + +	spi_message_init(&msg); +	spi_message_add_tail(&xfer, &msg); + +	error = spi_sync(ts->spi, &msg); +	if (error) { +		dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", +			__func__, cmd, error); +		return error; +	} + +	return 0; +} + +static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) +{ +	u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; +	struct spi_transfer xfer = { +		.tx_buf		= &tx, +		.len		= 4, +		.bits_per_word	= 24, +	}; +	struct spi_message msg; +	int error; + +	spi_message_init(&msg); +	spi_message_add_tail(&xfer, &msg); + +	error = spi_sync(ts->spi, &msg); +	if (error) { +		dev_err(&ts->spi->dev, +			"%s: failed, register: %x, value: %x, error: %d\n", +			__func__, reg, value, error); +		return error; +	} + +	return 0; +} + +static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) +{ +	memset(rd, 0, sizeof(*rd)); + +	rd->spi_tx		   = (reg | TSC2005_REG_READ) << 16; +	rd->spi_xfer.tx_buf	   = &rd->spi_tx; +	rd->spi_xfer.rx_buf	   = &rd->spi_rx; +	rd->spi_xfer.len	   = 4; +	rd->spi_xfer.bits_per_word = 24; +	rd->spi_xfer.cs_change	   = !last; +} + +static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) +{ +	struct tsc2005_spi_rd spi_rd; +	struct spi_message msg; +	int error; + +	tsc2005_setup_read(&spi_rd, reg, true); + +	spi_message_init(&msg); +	spi_message_add_tail(&spi_rd.spi_xfer, &msg); + +	error = spi_sync(ts->spi, &msg); +	if (error) +		return error; + +	*value = spi_rd.spi_rx; +	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; +	u32 x, y; +	u32 z1, z2; +	int error; + +	/* read the coordinates */ +	error = spi_sync(ts->spi, &ts->spi_read_msg); +	if (unlikely(error)) +		goto out; + +	x = ts->spi_x.spi_rx; +	y = ts->spi_y.spi_rx; +	z1 = ts->spi_z1.spi_rx; +	z2 = ts->spi_z2.spi_rx; + +	/* validate position */ +	if (unlikely(x > MAX_12BIT || y > MAX_12BIT)) +		goto out; + +	/* Skip reading if the pressure components are out of range */ +	if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= 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 == x && ts->in_y == y && +	    ts->in_z1 == z1 && ts->in_z2 == 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 = x; +	ts->in_y = y; +	ts->in_z1 = z1; +	ts->in_z2 = z2; + +	/* Compute touch pressure resistance using equation #1 */ +	pressure = x * (z2 - z1) / 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, x, 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) +{ +	tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE); +	tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE); +	tsc2005_write(ts, 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 >= 0) +		gpio_set_value(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 spi_device *spi = to_spi_device(dev); +	struct tsc2005 *ts = spi_get_drvdata(spi); +	u16 temp_high; +	u16 temp_high_orig; +	u16 temp_high_test; +	bool success = true; +	int error; + +	mutex_lock(&ts->mutex); + +	/* +	 * Test TSC2005 communications via temp high register. +	 */ +	__tsc2005_disable(ts); + +	error = tsc2005_read(ts, 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 = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); +	if (error) { +		dev_warn(dev, "selftest failed: write error %d\n", error); +		success = false; +		goto out; +	} + +	error = tsc2005_read(ts, 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 = tsc2005_read(ts, 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 spi_device *spi = to_spi_device(dev); +	struct tsc2005 *ts = spi_get_drvdata(spi); +	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; +	u16 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 = tsc2005_read(ts, 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 void tsc2005_setup_spi_xfer(struct tsc2005 *ts) +{ +	tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false); +	tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false); +	tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false); +	tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true); + +	spi_message_init(&ts->spi_read_msg); +	spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg); +	spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg); +	spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg); +	spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); +} + +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) +		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); +	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->x_plate_ohm = x_plate_ohm; +	ts->esd_timeout = esd_timeout; + +	if (np) { +		ts->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); +		if (ts->reset_gpio == -EPROBE_DEFER) +			return ts->reset_gpio; +		if (ts->reset_gpio < 0) { +			dev_err(&spi->dev, "error acquiring reset gpio: %d\n", +				ts->reset_gpio); +			return ts->reset_gpio; +		} + +		error = devm_gpio_request_one(&spi->dev, ts->reset_gpio, 0, +					      "reset-gpios"); +		if (error) { +			dev_err(&spi->dev, "error requesting reset gpio: %d\n", +				error); +			return error; +		} + +		ts->vio = devm_regulator_get(&spi->dev, "vio"); +		if (IS_ERR(ts->vio)) { +			error = PTR_ERR(ts->vio); +			dev_err(&spi->dev, "vio regulator missing (%d)", error); +			return error; +		} +	} else { +		ts->reset_gpio = -1; +		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); + +	tsc2005_setup_spi_xfer(ts); + +	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_of_params(input_dev); + +	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; +	} + +	spi_set_drvdata(spi, 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; +} + +static int tsc2005_remove(struct spi_device *spi) +{ +	struct tsc2005 *ts = spi_get_drvdata(spi); + +	sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); + +	if (ts->vio) +		regulator_disable(ts->vio); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tsc2005_suspend(struct device *dev) +{ +	struct spi_device *spi = to_spi_device(dev); +	struct tsc2005 *ts = spi_get_drvdata(spi); + +	mutex_lock(&ts->mutex); + +	if (!ts->suspended && ts->opened) +		__tsc2005_disable(ts); + +	ts->suspended = true; + +	mutex_unlock(&ts->mutex); + +	return 0; +} + +static int tsc2005_resume(struct device *dev) +{ +	struct spi_device *spi = to_spi_device(dev); +	struct tsc2005 *ts = spi_get_drvdata(spi); + +	mutex_lock(&ts->mutex); + +	if (ts->suspended && ts->opened) +		__tsc2005_enable(ts); + +	ts->suspended = false; + +	mutex_unlock(&ts->mutex); + +	return 0; +} +#endif + +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, +	}, +	.probe	= tsc2005_probe, +	.remove	= tsc2005_remove, +}; + +module_spi_driver(tsc2005_driver); + +MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); +MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tsc2005"); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 80467f26233..1bf9906b5a3 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -26,9 +26,9 @@  #include <linux/interrupt.h>  #include <linux/i2c.h>  #include <linux/i2c/tsc2007.h> - -#define TS_POLL_DELAY			1 /* ms delay between samples */ -#define TS_POLL_PERIOD			1 /* ms delay between samples */ +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/of_gpio.h>  #define TSC2007_MEASURE_TEMP0		(0x0 << 4)  #define TSC2007_MEASURE_AUX		(0x2 << 4) @@ -69,17 +69,24 @@ struct ts_event {  struct tsc2007 {  	struct input_dev	*input;  	char			phys[32]; -	struct delayed_work	work;  	struct i2c_client	*client;  	u16			model;  	u16			x_plate_ohms; +	u16			max_rt; +	unsigned long		poll_period; +	int			fuzzx; +	int			fuzzy; +	int			fuzzz; -	bool			pendown; +	unsigned		gpio;  	int			irq; -	int			(*get_pendown_state)(void); +	wait_queue_head_t	wait; +	bool			stopped; + +	int			(*get_pendown_state)(struct device *);  	void			(*clear_penirq)(void);  }; @@ -141,24 +148,8 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)  	return rt;  } -static void tsc2007_send_up_event(struct tsc2007 *tsc) +static bool tsc2007_is_pen_down(struct tsc2007 *ts)  { -	struct input_dev *input = tsc->input; - -	dev_dbg(&tsc->client->dev, "UP\n"); - -	input_report_key(input, BTN_TOUCH, 0); -	input_report_abs(input, ABS_PRESSURE, 0); -	input_sync(input); -} - -static void tsc2007_work(struct work_struct *work) -{ -	struct tsc2007 *ts = -		container_of(to_delayed_work(work), struct tsc2007, work); -	struct ts_event tc; -	u32 rt; -  	/*  	 * NOTE: We can't rely on the pressure to determine the pen down  	 * state, even though this controller has a pressure sensor. @@ -169,78 +160,82 @@ static void tsc2007_work(struct work_struct *work)  	 * The only safe way to check for the pen up condition is in the  	 * work function by reading the pen signal state (it's a GPIO  	 * and IRQ). Unfortunately such callback is not always available, -	 * in that case we have rely on the pressure anyway. +	 * in that case we assume that the pen is down and expect caller +	 * to fall back on the pressure reading.  	 */ -	if (ts->get_pendown_state) { -		if (unlikely(!ts->get_pendown_state())) { -			tsc2007_send_up_event(ts); -			ts->pendown = false; -			goto out; -		} -		dev_dbg(&ts->client->dev, "pen is still down\n"); -	} +	if (!ts->get_pendown_state) +		return true; + +	return ts->get_pendown_state(&ts->client->dev); +} -	tsc2007_read_values(ts, &tc); +static irqreturn_t tsc2007_soft_irq(int irq, void *handle) +{ +	struct tsc2007 *ts = handle; +	struct input_dev *input = ts->input; +	struct ts_event tc; +	u32 rt; -	rt = tsc2007_calculate_pressure(ts, &tc); -	if (rt > MAX_12BIT) { -		/* -		 * Sample found inconsistent by debouncing or pressure is -		 * beyond the maximum. Don't report it to user space, -		 * repeat at least once more the measurement. -		 */ -		dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); -		goto out; +	while (!ts->stopped && tsc2007_is_pen_down(ts)) { -	} +		/* pen is down, continue with the measurement */ +		tsc2007_read_values(ts, &tc); -	if (rt) { -		struct input_dev *input = ts->input; +		rt = tsc2007_calculate_pressure(ts, &tc); -		if (!ts->pendown) { -			dev_dbg(&ts->client->dev, "DOWN\n"); +		if (!rt && !ts->get_pendown_state) { +			/* +			 * If pressure reported is 0 and we don't have +			 * callback to check pendown state, we have to +			 * assume that pen was lifted up. +			 */ +			break; +		} + +		if (rt <= ts->max_rt) { +			dev_dbg(&ts->client->dev, +				"DOWN point(%4d,%4d), pressure (%4u)\n", +				tc.x, tc.y, rt);  			input_report_key(input, BTN_TOUCH, 1); -			ts->pendown = true; +			input_report_abs(input, ABS_X, tc.x); +			input_report_abs(input, ABS_Y, tc.y); +			input_report_abs(input, ABS_PRESSURE, rt); + +			input_sync(input); + +		} else { +			/* +			 * Sample found inconsistent by debouncing or pressure is +			 * beyond the maximum. Don't report it to user space, +			 * repeat at least once more the measurement. +			 */ +			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);  		} -		input_report_abs(input, ABS_X, tc.x); -		input_report_abs(input, ABS_Y, tc.y); -		input_report_abs(input, ABS_PRESSURE, rt); +		wait_event_timeout(ts->wait, ts->stopped, +				   msecs_to_jiffies(ts->poll_period)); +	} -		input_sync(input); +	dev_dbg(&ts->client->dev, "UP\n"); -		dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", -			tc.x, tc.y, rt); +	input_report_key(input, BTN_TOUCH, 0); +	input_report_abs(input, ABS_PRESSURE, 0); +	input_sync(input); -	} else if (!ts->get_pendown_state && ts->pendown) { -		/* -		 * We don't have callback to check pendown state, so we -		 * have to assume that since pressure reported is 0 the -		 * pen was lifted up. -		 */ -		tsc2007_send_up_event(ts); -		ts->pendown = false; -	} +	if (ts->clear_penirq) +		ts->clear_penirq(); - out: -	if (ts->pendown) -		schedule_delayed_work(&ts->work, -				      msecs_to_jiffies(TS_POLL_PERIOD)); -	else -		enable_irq(ts->irq); +	return IRQ_HANDLED;  } -static irqreturn_t tsc2007_irq(int irq, void *handle) +static irqreturn_t tsc2007_hard_irq(int irq, void *handle)  {  	struct tsc2007 *ts = handle; -	if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { -		disable_irq_nosync(ts->irq); -		schedule_delayed_work(&ts->work, -				      msecs_to_jiffies(TS_POLL_DELAY)); -	} +	if (tsc2007_is_pen_down(ts)) +		return IRQ_WAKE_THREAD;  	if (ts->clear_penirq)  		ts->clear_penirq(); @@ -248,112 +243,225 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)  	return IRQ_HANDLED;  } -static void tsc2007_free_irq(struct tsc2007 *ts) +static void tsc2007_stop(struct tsc2007 *ts)  { -	free_irq(ts->irq, ts); -	if (cancel_delayed_work_sync(&ts->work)) { -		/* -		 * Work was pending, therefore we need to enable -		 * IRQ here to balance the disable_irq() done in the -		 * interrupt handler. -		 */ -		enable_irq(ts->irq); -	} +	ts->stopped = true; +	mb(); +	wake_up(&ts->wait); + +	disable_irq(ts->irq);  } -static int __devinit tsc2007_probe(struct i2c_client *client, -				   const struct i2c_device_id *id) +static int tsc2007_open(struct input_dev *input_dev)  { -	struct tsc2007 *ts; -	struct tsc2007_platform_data *pdata = client->dev.platform_data; -	struct input_dev *input_dev; +	struct tsc2007 *ts = input_get_drvdata(input_dev);  	int err; -	if (!pdata) { -		dev_err(&client->dev, "platform data is required!\n"); +	ts->stopped = false; +	mb(); + +	enable_irq(ts->irq); + +	/* Prepare for touch readings - power down ADC and enable PENIRQ */ +	err = tsc2007_xfer(ts, PWRDOWN); +	if (err < 0) { +		tsc2007_stop(ts); +		return err; +	} + +	return 0; +} + +static void tsc2007_close(struct input_dev *input_dev) +{ +	struct tsc2007 *ts = input_get_drvdata(input_dev); + +	tsc2007_stop(ts); +} + +#ifdef CONFIG_OF +static int tsc2007_get_pendown_state_gpio(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct tsc2007 *ts = i2c_get_clientdata(client); + +	return !gpio_get_value(ts->gpio); +} + +static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts) +{ +	struct device_node *np = client->dev.of_node; +	u32 val32; +	u64 val64; + +	if (!np) { +		dev_err(&client->dev, "missing device tree data\n");  		return -EINVAL;  	} -	if (!i2c_check_functionality(client->adapter, -				     I2C_FUNC_SMBUS_READ_WORD_DATA)) -		return -EIO; +	if (!of_property_read_u32(np, "ti,max-rt", &val32)) +		ts->max_rt = val32; +	else +		ts->max_rt = MAX_12BIT; + +	if (!of_property_read_u32(np, "ti,fuzzx", &val32)) +		ts->fuzzx = val32; + +	if (!of_property_read_u32(np, "ti,fuzzy", &val32)) +		ts->fuzzy = val32; -	ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); -	input_dev = input_allocate_device(); -	if (!ts || !input_dev) { -		err = -ENOMEM; -		goto err_free_mem; +	if (!of_property_read_u32(np, "ti,fuzzz", &val32)) +		ts->fuzzz = val32; + +	if (!of_property_read_u64(np, "ti,poll-period", &val64)) +		ts->poll_period = val64; +	else +		ts->poll_period = 1; + +	if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) { +		ts->x_plate_ohms = val32; +	} else { +		dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property."); +		return -EINVAL;  	} -	ts->client = client; -	ts->irq = client->irq; -	ts->input = input_dev; -	INIT_DELAYED_WORK(&ts->work, tsc2007_work); +	ts->gpio = of_get_gpio(np, 0); +	if (gpio_is_valid(ts->gpio)) +		ts->get_pendown_state = tsc2007_get_pendown_state_gpio; +	else +		dev_warn(&client->dev, +			 "GPIO not specified in DT (of_get_gpio returned %d)\n", +			 ts->gpio); + +	return 0; +} +#else +static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts) +{ +	dev_err(&client->dev, "platform data is required!\n"); +	return -EINVAL; +} +#endif +static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts, +			      const struct tsc2007_platform_data *pdata, +			      const struct i2c_device_id *id) +{  	ts->model             = pdata->model;  	ts->x_plate_ohms      = pdata->x_plate_ohms; +	ts->max_rt            = pdata->max_rt ? : MAX_12BIT; +	ts->poll_period       = pdata->poll_period ? : 1;  	ts->get_pendown_state = pdata->get_pendown_state;  	ts->clear_penirq      = pdata->clear_penirq; +	ts->fuzzx             = pdata->fuzzx; +	ts->fuzzy             = pdata->fuzzy; +	ts->fuzzz             = pdata->fuzzz; -	snprintf(ts->phys, sizeof(ts->phys), -		 "%s/input0", dev_name(&client->dev)); +	if (pdata->x_plate_ohms == 0) { +		dev_err(&client->dev, "x_plate_ohms is not set up in platform data"); +		return -EINVAL; +	} -	input_dev->name = "TSC2007 Touchscreen"; -	input_dev->phys = ts->phys; -	input_dev->id.bustype = BUS_I2C; +	return 0; +} -	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); -	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); +static void tsc2007_call_exit_platform_hw(void *data) +{ +	struct device *dev = data; +	const struct tsc2007_platform_data *pdata = dev_get_platdata(dev); -	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); -	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); -	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); +	pdata->exit_platform_hw(); +} -	if (pdata->init_platform_hw) -		pdata->init_platform_hw(); +static int tsc2007_probe(struct i2c_client *client, +			 const struct i2c_device_id *id) +{ +	const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev); +	struct tsc2007 *ts; +	struct input_dev *input_dev; +	int err; -	err = request_irq(ts->irq, tsc2007_irq, 0, -			client->dev.driver->name, ts); -	if (err < 0) { -		dev_err(&client->dev, "irq %d busy?\n", ts->irq); -		goto err_free_mem; -	} +	if (!i2c_check_functionality(client->adapter, +				     I2C_FUNC_SMBUS_READ_WORD_DATA)) +		return -EIO; -	/* Prepare for touch readings - power down ADC and enable PENIRQ */ -	err = tsc2007_xfer(ts, PWRDOWN); -	if (err < 0) -		goto err_free_irq; +	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL); +	if (!ts) +		return -ENOMEM; -	err = input_register_device(input_dev); +	if (pdata) +		err = tsc2007_probe_pdev(client, ts, pdata, id); +	else +		err = tsc2007_probe_dt(client, ts);  	if (err) -		goto err_free_irq; +		return err; + +	input_dev = devm_input_allocate_device(&client->dev); +	if (!input_dev) +		return -ENOMEM;  	i2c_set_clientdata(client, ts); -	return 0; +	ts->client = client; +	ts->irq = client->irq; +	ts->input = input_dev; +	init_waitqueue_head(&ts->wait); - err_free_irq: -	tsc2007_free_irq(ts); -	if (pdata->exit_platform_hw) -		pdata->exit_platform_hw(); - err_free_mem: -	input_free_device(input_dev); -	kfree(ts); -	return err; -} +	snprintf(ts->phys, sizeof(ts->phys), +		 "%s/input0", dev_name(&client->dev)); -static int __devexit tsc2007_remove(struct i2c_client *client) -{ -	struct tsc2007	*ts = i2c_get_clientdata(client); -	struct tsc2007_platform_data *pdata = client->dev.platform_data; +	input_dev->name = "TSC2007 Touchscreen"; +	input_dev->phys = ts->phys; +	input_dev->id.bustype = BUS_I2C; + +	input_dev->open = tsc2007_open; +	input_dev->close = tsc2007_close; + +	input_set_drvdata(input_dev, ts); + +	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); -	tsc2007_free_irq(ts); +	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0); +	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, +			     ts->fuzzz, 0); + +	if (pdata) { +		if (pdata->exit_platform_hw) { +			err = devm_add_action(&client->dev, +					      tsc2007_call_exit_platform_hw, +					      &client->dev); +			if (err) { +				dev_err(&client->dev, +					"Failed to register exit_platform_hw action, %d\n", +					err); +				return err; +			} +		} -	if (pdata->exit_platform_hw) -		pdata->exit_platform_hw(); +		if (pdata->init_platform_hw) +			pdata->init_platform_hw(); +	} -	input_unregister_device(ts->input); -	kfree(ts); +	err = devm_request_threaded_irq(&client->dev, ts->irq, +					tsc2007_hard_irq, tsc2007_soft_irq, +					IRQF_ONESHOT, +					client->dev.driver->name, ts); +	if (err) { +		dev_err(&client->dev, "Failed to request irq %d: %d\n", +			ts->irq, err); +		return err; +	} + +	tsc2007_stop(ts); + +	err = input_register_device(input_dev); +	if (err) { +		dev_err(&client->dev, +			"Failed to register input device: %d\n", err); +		return err; +	}  	return 0;  } @@ -365,28 +473,25 @@ static const struct i2c_device_id tsc2007_idtable[] = {  MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); +#ifdef CONFIG_OF +static const struct of_device_id tsc2007_of_match[] = { +	{ .compatible = "ti,tsc2007" }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tsc2007_of_match); +#endif +  static struct i2c_driver tsc2007_driver = {  	.driver = {  		.owner	= THIS_MODULE, -		.name	= "tsc2007" +		.name	= "tsc2007", +		.of_match_table = of_match_ptr(tsc2007_of_match),  	},  	.id_table	= tsc2007_idtable,  	.probe		= tsc2007_probe, -	.remove		= __devexit_p(tsc2007_remove),  }; -static int __init tsc2007_init(void) -{ -	return i2c_add_driver(&tsc2007_driver); -} - -static void __exit tsc2007_exit(void) -{ -	i2c_del_driver(&tsc2007_driver); -} - -module_init(tsc2007_init); -module_exit(tsc2007_exit); +module_i2c_driver(tsc2007_driver);  MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");  MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); diff --git a/drivers/input/touchscreen/tsc40.c b/drivers/input/touchscreen/tsc40.c new file mode 100644 index 00000000000..29687872cb9 --- /dev/null +++ b/drivers/input/touchscreen/tsc40.c @@ -0,0 +1,172 @@ +/* + * TSC-40 serial touchscreen driver. It should be compatible with + * TSC-10 and 25. + * + * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * License: GPLv2 as published by the FSF. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> + +#define PACKET_LENGTH  5 +struct tsc_ser { +	struct input_dev *dev; +	struct serio *serio; +	u32 idx; +	unsigned char data[PACKET_LENGTH]; +	char phys[32]; +}; + +static void tsc_process_data(struct tsc_ser *ptsc) +{ +	struct input_dev *dev = ptsc->dev; +	u8 *data = ptsc->data; +	u32 x; +	u32 y; + +	x = ((data[1] & 0x03) << 8) | data[2]; +	y = ((data[3] & 0x03) << 8) | data[4]; + +	input_report_abs(dev, ABS_X, x); +	input_report_abs(dev, ABS_Y, y); +	input_report_key(dev, BTN_TOUCH, 1); + +	input_sync(dev); +} + +static irqreturn_t tsc_interrupt(struct serio *serio, +		unsigned char data, unsigned int flags) +{ +	struct tsc_ser *ptsc = serio_get_drvdata(serio); +	struct input_dev *dev = ptsc->dev; + +	ptsc->data[ptsc->idx] = data; +	switch (ptsc->idx++) { +	case 0: +		if (unlikely((data & 0x3e) != 0x10)) { +			dev_dbg(&serio->dev, +				"unsynchronized packet start (0x%02x)\n", data); +			ptsc->idx = 0; +		} else if (!(data & 0x01)) { +			input_report_key(dev, BTN_TOUCH, 0); +			input_sync(dev); +			ptsc->idx = 0; +		} +		break; + +	case 1: +	case 3: +		if (unlikely(data & 0xfc)) { +			dev_dbg(&serio->dev, +				"unsynchronized data 0x%02x at offset %d\n", +				data, ptsc->idx - 1); +			ptsc->idx = 0; +		} +		break; + +	case 4: +		tsc_process_data(ptsc); +		ptsc->idx = 0; +		break; +	} + +	return IRQ_HANDLED; +} + +static int tsc_connect(struct serio *serio, struct serio_driver *drv) +{ +	struct tsc_ser *ptsc; +	struct input_dev *input_dev; +	int error; + +	ptsc = kzalloc(sizeof(struct tsc_ser), GFP_KERNEL); +	input_dev = input_allocate_device(); +	if (!ptsc || !input_dev) { +		error = -ENOMEM; +		goto fail1; +	} + +	ptsc->serio = serio; +	ptsc->dev = input_dev; +	snprintf(ptsc->phys, sizeof(ptsc->phys), "%s/input0", serio->phys); + +	input_dev->name = "TSC-10/25/40 Serial TouchScreen"; +	input_dev->phys = ptsc->phys; +	input_dev->id.bustype = BUS_RS232; +	input_dev->id.vendor = SERIO_TSC40; +	input_dev->id.product = 40; +	input_dev->id.version = 0x0001; +	input_dev->dev.parent = &serio->dev; + +	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	__set_bit(BTN_TOUCH, input_dev->keybit); +	input_set_abs_params(ptsc->dev, ABS_X, 0, 0x3ff, 0, 0); +	input_set_abs_params(ptsc->dev, ABS_Y, 0, 0x3ff, 0, 0); + +	serio_set_drvdata(serio, ptsc); + +	error = serio_open(serio, drv); +	if (error) +		goto fail2; + +	error = input_register_device(ptsc->dev); +	if (error) +		goto fail3; + +	return 0; + +fail3: +	serio_close(serio); +fail2: +	serio_set_drvdata(serio, NULL); +fail1: +	input_free_device(input_dev); +	kfree(ptsc); +	return error; +} + +static void tsc_disconnect(struct serio *serio) +{ +	struct tsc_ser *ptsc = serio_get_drvdata(serio); + +	serio_close(serio); + +	input_unregister_device(ptsc->dev); +	kfree(ptsc); + +	serio_set_drvdata(serio, NULL); +} + +static struct serio_device_id tsc_serio_ids[] = { +	{ +		.type   = SERIO_RS232, +		.proto  = SERIO_TSC40, +		.id     = SERIO_ANY, +		.extra  = SERIO_ANY, +	}, +	{ 0 } +}; +MODULE_DEVICE_TABLE(serio, tsc_serio_ids); + +#define DRIVER_DESC    "TSC-10/25/40 serial touchscreen driver" + +static struct serio_driver tsc_drv = { +	.driver	= { +		.name   = "tsc40", +	}, +	.description    = DRIVER_DESC, +	.id_table	= tsc_serio_ids, +	.interrupt      = tsc_interrupt, +	.connect	= tsc_connect, +	.disconnect     = tsc_disconnect, +}; + +module_serio_driver(tsc_drv); + +MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 028a5363eea..b46c55cd1bb 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -6,7 +6,7 @@   *  Copyright:	MontaVista Software, Inc.   *   * Spliting done by: Marek Vasut <marek.vasut@gmail.com> - * If something doesnt work and it worked before spliting, e-mail me, + * If something doesn't work and it worked before spliting, e-mail me,   * dont bother Nicolas please ;-)   *   * This program is free software; you can redistribute it and/or modify @@ -19,25 +19,24 @@   */  #include <linux/module.h> -#include <linux/init.h> -#include <linux/completion.h>  #include <linux/delay.h> +#include <linux/sched.h> +#include <linux/wait.h>  #include <linux/input.h>  #include <linux/device.h>  #include <linux/interrupt.h> -#include <linux/suspend.h> -#include <linux/kthread.h> -#include <linux/freezer.h>  #include <linux/ucb1400.h> -static int adcsync; +#define UCB1400_TS_POLL_PERIOD	10 /* ms */ + +static bool adcsync;  static int ts_delay = 55; /* us */  static int ts_delay_pressure;	/* us */  /* Switch to interrupt mode. */ -static inline void ucb1400_ts_mode_int(struct snd_ac97 *ac97) +static void ucb1400_ts_mode_int(struct ucb1400_ts *ucb)  { -	ucb1400_reg_write(ac97, UCB_TS_CR, +	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,  			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |  			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |  			UCB_TS_CR_MODE_INT); @@ -47,13 +46,15 @@ static inline void ucb1400_ts_mode_int(struct snd_ac97 *ac97)   * Switch to pressure mode, and read pressure.  We don't need to wait   * here, since both plates are being driven.   */ -static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb) +static unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)  {  	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,  			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |  			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |  			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); +  	udelay(ts_delay_pressure); +  	return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);  } @@ -63,7 +64,7 @@ static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)   * gives a faster response time.  Even so, we need to wait about 55us   * for things to stabilise.   */ -static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb) +static unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)  {  	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,  			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | @@ -86,7 +87,7 @@ static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)   * gives a faster response time.  Even so, we need to wait about 55us   * for things to stabilise.   */ -static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb) +static int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)  {  	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,  			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | @@ -107,7 +108,7 @@ static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)   * Switch to X plate resistance mode.  Set MX to ground, PX to   * supply.  Measure current.   */ -static inline unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb) +static unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)  {  	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,  			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | @@ -119,7 +120,7 @@ static inline unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)   * Switch to Y plate resistance mode.  Set MY to ground, PY to   * supply.  Measure current.   */ -static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb) +static unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)  {  	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,  			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | @@ -127,26 +128,26 @@ static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)  	return ucb1400_adc_read(ucb->ac97, 0, adcsync);  } -static inline int ucb1400_ts_pen_up(struct snd_ac97 *ac97) +static int ucb1400_ts_pen_up(struct ucb1400_ts *ucb)  { -	unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR); +	unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR);  	return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW);  } -static inline void ucb1400_ts_irq_enable(struct snd_ac97 *ac97) +static void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb)  { -	ucb1400_reg_write(ac97, UCB_IE_CLEAR, UCB_IE_TSPX); -	ucb1400_reg_write(ac97, UCB_IE_CLEAR, 0); -	ucb1400_reg_write(ac97, UCB_IE_FAL, UCB_IE_TSPX); +	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX); +	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); +	ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX);  } -static inline void ucb1400_ts_irq_disable(struct snd_ac97 *ac97) +static void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb)  { -	ucb1400_reg_write(ac97, UCB_IE_FAL, 0); +	ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);  } -static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y) +static void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y)  {  	input_report_abs(idev, ABS_X, x);  	input_report_abs(idev, ABS_Y, y); @@ -162,7 +163,7 @@ static void ucb1400_ts_event_release(struct input_dev *idev)  	input_sync(idev);  } -static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb) +static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb)  {  	unsigned int isr; @@ -171,32 +172,34 @@ static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb)  	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);  	if (isr & UCB_IE_TSPX) -		ucb1400_ts_irq_disable(ucb->ac97); +		ucb1400_ts_irq_disable(ucb);  	else -		dev_dbg(&ucb->ts_idev->dev, "ucb1400: unexpected IE_STATUS = %#x\n", isr); -	enable_irq(ucb->irq); +		dev_dbg(&ucb->ts_idev->dev, +			"ucb1400: unexpected IE_STATUS = %#x\n", isr);  } -static int ucb1400_ts_thread(void *_ucb) +/* + * A restriction with interrupts exists when using the ucb1400, as + * the codec read/write routines may sleep while waiting for codec + * access completion and uses semaphores for access control to the + * AC97 bus. Therefore the driver is forced to use threaded interrupt + * handler. + */ +static irqreturn_t ucb1400_irq(int irqnr, void *devid)  { -	struct ucb1400_ts *ucb = _ucb; -	struct task_struct *tsk = current; -	int valid = 0; -	struct sched_param param = { .sched_priority = 1 }; +	struct ucb1400_ts *ucb = devid; +	unsigned int x, y, p; +	bool penup; -	sched_setscheduler(tsk, SCHED_FIFO, ¶m); +	if (unlikely(irqnr != ucb->irq)) +		return IRQ_NONE; -	set_freezable(); -	while (!kthread_should_stop()) { -		unsigned int x, y, p; -		long timeout; +	ucb1400_clear_pending_irq(ucb); -		ucb->ts_restart = 0; +	/* Start with a small delay before checking pendown state */ +	msleep(UCB1400_TS_POLL_PERIOD); -		if (ucb->irq_pending) { -			ucb->irq_pending = 0; -			ucb1400_handle_pending_irq(ucb); -		} +	while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) {  		ucb1400_adc_enable(ucb->ac97);  		x = ucb1400_ts_read_xpos(ucb); @@ -204,91 +207,62 @@ static int ucb1400_ts_thread(void *_ucb)  		p = ucb1400_ts_read_pressure(ucb);  		ucb1400_adc_disable(ucb->ac97); -		/* Switch back to interrupt mode. */ -		ucb1400_ts_mode_int(ucb->ac97); - -		msleep(10); - -		if (ucb1400_ts_pen_up(ucb->ac97)) { -			ucb1400_ts_irq_enable(ucb->ac97); - -			/* -			 * If we spat out a valid sample set last time, -			 * spit out a "pen off" sample here. -			 */ -			if (valid) { -				ucb1400_ts_event_release(ucb->ts_idev); -				valid = 0; -			} - -			timeout = MAX_SCHEDULE_TIMEOUT; -		} else { -			valid = 1; -			ucb1400_ts_evt_add(ucb->ts_idev, p, x, y); -			timeout = msecs_to_jiffies(10); -		} +		ucb1400_ts_report_event(ucb->ts_idev, p, x, y); -		wait_event_freezable_timeout(ucb->ts_wait, -			ucb->irq_pending || ucb->ts_restart || -			kthread_should_stop(), timeout); +		wait_event_timeout(ucb->ts_wait, ucb->stopped, +				   msecs_to_jiffies(UCB1400_TS_POLL_PERIOD));  	} -	/* Send the "pen off" if we are stopping with the pen still active */ -	if (valid) -		ucb1400_ts_event_release(ucb->ts_idev); +	ucb1400_ts_event_release(ucb->ts_idev); -	ucb->ts_task = NULL; -	return 0; +	if (!ucb->stopped) { +		/* Switch back to interrupt mode. */ +		ucb1400_ts_mode_int(ucb); +		ucb1400_ts_irq_enable(ucb); +	} + +	return IRQ_HANDLED;  } -/* - * A restriction with interrupts exists when using the ucb1400, as - * the codec read/write routines may sleep while waiting for codec - * access completion and uses semaphores for access control to the - * AC97 bus.  A complete codec read cycle could take  anywhere from - * 60 to 100uSec so we *definitely* don't want to spin inside the - * interrupt handler waiting for codec access.  So, we handle the - * interrupt by scheduling a RT kernel thread to run in process - * context instead of interrupt context. - */ -static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) +static void ucb1400_ts_stop(struct ucb1400_ts *ucb)  { -	struct ucb1400_ts *ucb = devid; +	/* Signal IRQ thread to stop polling and disable the handler. */ +	ucb->stopped = true; +	mb(); +	wake_up(&ucb->ts_wait); +	disable_irq(ucb->irq); -	if (irqnr == ucb->irq) { -		disable_irq_nosync(ucb->irq); -		ucb->irq_pending = 1; -		wake_up(&ucb->ts_wait); -		return IRQ_HANDLED; -	} -	return IRQ_NONE; +	ucb1400_ts_irq_disable(ucb); +	ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); +} + +/* Must be called with ts->lock held */ +static void ucb1400_ts_start(struct ucb1400_ts *ucb) +{ +	/* Tell IRQ thread that it may poll the device. */ +	ucb->stopped = false; +	mb(); + +	ucb1400_ts_mode_int(ucb); +	ucb1400_ts_irq_enable(ucb); + +	enable_irq(ucb->irq);  }  static int ucb1400_ts_open(struct input_dev *idev)  {  	struct ucb1400_ts *ucb = input_get_drvdata(idev); -	int ret = 0; -	BUG_ON(ucb->ts_task); +	ucb1400_ts_start(ucb); -	ucb->ts_task = kthread_run(ucb1400_ts_thread, ucb, "UCB1400_ts"); -	if (IS_ERR(ucb->ts_task)) { -		ret = PTR_ERR(ucb->ts_task); -		ucb->ts_task = NULL; -	} - -	return ret; +	return 0;  }  static void ucb1400_ts_close(struct input_dev *idev)  {  	struct ucb1400_ts *ucb = input_get_drvdata(idev); -	if (ucb->ts_task) -		kthread_stop(ucb->ts_task); - -	ucb1400_ts_irq_disable(ucb->ac97); -	ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); +	ucb1400_ts_stop(ucb);  }  #ifndef NO_IRQ @@ -299,7 +273,8 @@ static void ucb1400_ts_close(struct input_dev *idev)   * Try to probe our interrupt, rather than relying on lots of   * hard-coded machine dependencies.   */ -static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb) +static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb, +					   struct platform_device *pdev)  {  	unsigned long mask, timeout; @@ -321,7 +296,7 @@ static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb)  						UCB_ADC_DAT_VALID)) {  		cpu_relax();  		if (time_after(jiffies, timeout)) { -			printk(KERN_ERR "ucb1400: timed out in IRQ probe\n"); +			dev_err(&pdev->dev, "timed out in IRQ probe\n");  			probe_irq_off(mask);  			return -ENODEV;  		} @@ -342,11 +317,11 @@ static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb)  	return 0;  } -static int ucb1400_ts_probe(struct platform_device *dev) +static int ucb1400_ts_probe(struct platform_device *pdev)  { +	struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev);  	int error, x_res, y_res;  	u16 fcsr; -	struct ucb1400_ts *ucb = dev->dev.platform_data;  	ucb->ts_idev = input_allocate_device();  	if (!ucb->ts_idev) { @@ -356,27 +331,19 @@ static int ucb1400_ts_probe(struct platform_device *dev)  	/* Only in case the IRQ line wasn't supplied, try detecting it */  	if (ucb->irq < 0) { -		error = ucb1400_ts_detect_irq(ucb); +		error = ucb1400_ts_detect_irq(ucb, pdev);  		if (error) { -			printk(KERN_ERR "UCB1400: IRQ probe failed\n"); +			dev_err(&pdev->dev, "IRQ probe failed\n");  			goto err_free_devs;  		}  	} +	dev_dbg(&pdev->dev, "found IRQ %d\n", ucb->irq);  	init_waitqueue_head(&ucb->ts_wait); -	error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING, -				"UCB1400", ucb); -	if (error) { -		printk(KERN_ERR "ucb1400: unable to grab irq%d: %d\n", -				ucb->irq, error); -		goto err_free_devs; -	} -	printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq); -  	input_set_drvdata(ucb->ts_idev, ucb); -	ucb->ts_idev->dev.parent	= &dev->dev; +	ucb->ts_idev->dev.parent	= &pdev->dev;  	ucb->ts_idev->name		= "UCB1400 touchscreen interface";  	ucb->ts_idev->id.vendor		= ucb1400_reg_read(ucb->ac97,  						AC97_VENDOR_ID1); @@ -398,12 +365,23 @@ static int ucb1400_ts_probe(struct platform_device *dev)  	x_res = ucb1400_ts_read_xres(ucb);  	y_res = ucb1400_ts_read_yres(ucb);  	ucb1400_adc_disable(ucb->ac97); -	printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res); +	dev_dbg(&pdev->dev, "x/y = %d/%d\n", x_res, y_res);  	input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0);  	input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0);  	input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0); +	ucb1400_ts_stop(ucb); + +	error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq, +				     IRQF_TRIGGER_RISING | IRQF_ONESHOT, +				     "UCB1400", ucb); +	if (error) { +		dev_err(&pdev->dev, +			"unable to grab irq%d: %d\n", ucb->irq, error); +		goto err_free_devs; +	} +  	error = input_register_device(ucb->ts_idev);  	if (error)  		goto err_free_irq; @@ -416,56 +394,61 @@ err_free_devs:  	input_free_device(ucb->ts_idev);  err:  	return error; -  } -static int ucb1400_ts_remove(struct platform_device *dev) +static int ucb1400_ts_remove(struct platform_device *pdev)  { -	struct ucb1400_ts *ucb = dev->dev.platform_data; +	struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev);  	free_irq(ucb->irq, ucb);  	input_unregister_device(ucb->ts_idev); +  	return 0;  } -#ifdef CONFIG_PM -static int ucb1400_ts_resume(struct platform_device *dev) +#ifdef CONFIG_PM_SLEEP +static int ucb1400_ts_suspend(struct device *dev)  { -	struct ucb1400_ts *ucb = dev->dev.platform_data; - -	if (ucb->ts_task) { -		/* -		 * Restart the TS thread to ensure the -		 * TS interrupt mode is set up again -		 * after sleep. -		 */ -		ucb->ts_restart = 1; -		wake_up(&ucb->ts_wait); -	} +	struct ucb1400_ts *ucb = dev_get_platdata(dev); +	struct input_dev *idev = ucb->ts_idev; + +	mutex_lock(&idev->mutex); + +	if (idev->users) +		ucb1400_ts_start(ucb); + +	mutex_unlock(&idev->mutex); +	return 0; +} + +static int ucb1400_ts_resume(struct device *dev) +{ +	struct ucb1400_ts *ucb = dev_get_platdata(dev); +	struct input_dev *idev = ucb->ts_idev; + +	mutex_lock(&idev->mutex); + +	if (idev->users) +		ucb1400_ts_stop(ucb); + +	mutex_unlock(&idev->mutex);  	return 0;  } -#else -#define ucb1400_ts_resume NULL  #endif +static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops, +			 ucb1400_ts_suspend, ucb1400_ts_resume); +  static struct platform_driver ucb1400_ts_driver = {  	.probe	= ucb1400_ts_probe,  	.remove	= ucb1400_ts_remove, -	.resume	= ucb1400_ts_resume,  	.driver	= {  		.name	= "ucb1400_ts", +		.owner	= THIS_MODULE, +		.pm	= &ucb1400_ts_pm_ops,  	},  }; - -static int __init ucb1400_ts_init(void) -{ -	return platform_driver_register(&ucb1400_ts_driver); -} - -static void __exit ucb1400_ts_exit(void) -{ -	platform_driver_unregister(&ucb1400_ts_driver); -} +module_platform_driver(ucb1400_ts_driver);  module_param(adcsync, bool, 0444);  MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); @@ -479,8 +462,5 @@ MODULE_PARM_DESC(ts_delay_pressure,  		"delay between panel setup and pressure read."  		"  Default = 0us."); -module_init(ucb1400_ts_init); -module_exit(ucb1400_ts_exit); -  MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index f45f80f6d33..a0966331a89 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -16,6 +16,8 @@   *  - JASTEC USB touch controller/DigiTech DTR-02U   *  - Zytronic capacitive touchscreen   *  - NEXIO/iNexio + *  - Elo TouchSystems 2700 IntelliTouch + *  - EasyTouch USB Dual/Multi touch controller from Data Modul   *   * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>   * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -49,7 +51,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/module.h> -#include <linux/init.h>  #include <linux/usb.h>  #include <linux/usb/input.h>  #include <linux/hid.h> @@ -59,11 +60,11 @@  #define DRIVER_AUTHOR		"Daniel Ritz <daniel.ritz@gmx.ch>"  #define DRIVER_DESC		"USB Touchscreen Driver" -static int swap_xy; +static bool swap_xy;  module_param(swap_xy, bool, 0644);  MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped."); -static int hwcalib_xy; +static bool hwcalib_xy;  module_param(hwcalib_xy, bool, 0644);  MODULE_PARM_DESC(hwcalib_xy, "If set hw-calibrated X/Y are used if available"); @@ -104,6 +105,7 @@ struct usbtouch_device_info {  struct usbtouch_usb {  	unsigned char *data;  	dma_addr_t data_dma; +	int data_size;  	unsigned char *buffer;  	int buf_len;  	struct urb *irq; @@ -138,16 +140,16 @@ enum {  	DEVTYPE_ZYTRONIC,  	DEVTYPE_TC45USB,  	DEVTYPE_NEXIO, +	DEVTYPE_ELO, +	DEVTYPE_ETOUCH,  };  #define USB_DEVICE_HID_CLASS(vend, prod) \  	.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS \ -		| USB_DEVICE_ID_MATCH_INT_PROTOCOL \  		| USB_DEVICE_ID_MATCH_DEVICE, \  	.idVendor = (vend), \  	.idProduct = (prod), \ -	.bInterfaceClass = USB_INTERFACE_CLASS_HID, \ -	.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE +	.bInterfaceClass = USB_INTERFACE_CLASS_HID  static const struct usb_device_id usbtouch_devices[] = {  #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX @@ -178,6 +180,7 @@ static const struct usb_device_id usbtouch_devices[] = {  #ifdef CONFIG_TOUCHSCREEN_USB_ITM  	{USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM}, +	{USB_DEVICE(0x16e3, 0xf9e9), .driver_info = DEVTYPE_ITM},  #endif  #ifdef CONFIG_TOUCHSCREEN_USB_ETURBO @@ -238,6 +241,14 @@ static const struct usb_device_id usbtouch_devices[] = {  		.driver_info = DEVTYPE_NEXIO},  #endif +#ifdef CONFIG_TOUCHSCREEN_USB_ELO +	{USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH +	{USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH}, +#endif +  	{}  }; @@ -256,8 +267,9 @@ static int e2i_init(struct usbtouch_usb *usbtouch)  	                      0x01, 0x02, 0x0000, 0x0081,  	                      NULL, 0, USB_CTRL_SET_TIMEOUT); -	dbg("%s - usb_control_msg - E2I_RESET - bytes|err: %d", -	    __func__, ret); +	dev_dbg(&usbtouch->interface->dev, +		"%s - usb_control_msg - E2I_RESET - bytes|err: %d\n", +		__func__, ret);  	return ret;  } @@ -290,6 +302,45 @@ static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)  #define EGALAX_PKT_TYPE_REPT		0x80  #define EGALAX_PKT_TYPE_DIAG		0x0A +static int egalax_init(struct usbtouch_usb *usbtouch) +{ +	int ret, i; +	unsigned char *buf; +	struct usb_device *udev = interface_to_usbdev(usbtouch->interface); + +	/* +	 * An eGalax diagnostic packet kicks the device into using the right +	 * protocol.  We send a "check active" packet.  The response will be +	 * read later and ignored. +	 */ + +	buf = kmalloc(3, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	buf[0] = EGALAX_PKT_TYPE_DIAG; +	buf[1] = 1;	/* length */ +	buf[2] = 'A';	/* command - check active */ + +	for (i = 0; i < 3; i++) { +		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), +				      0, +				      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +				      0, 0, buf, 3, +				      USB_CTRL_SET_TIMEOUT); +		if (ret >= 0) { +			ret = 0; +			break; +		} +		if (ret != -EPIPE) +			break; +	} + +	kfree(buf); + +	return ret; +} +  static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)  {  	if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT) @@ -319,6 +370,51 @@ static int egalax_get_pkt_len(unsigned char *buf, int len)  }  #endif +/***************************************************************************** + * EasyTouch part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + +#ifndef MULTI_PACKET +#define MULTI_PACKET +#endif + +#define ETOUCH_PKT_TYPE_MASK		0xFE +#define ETOUCH_PKT_TYPE_REPT		0x80 +#define ETOUCH_PKT_TYPE_REPT2		0xB0 +#define ETOUCH_PKT_TYPE_DIAG		0x0A + +static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ +	if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT && +		(pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2) +		return 0; + +	dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F); +	dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F); +	dev->touch = pkt[0] & 0x01; + +	return 1; +} + +static int etouch_get_pkt_len(unsigned char *buf, int len) +{ +	switch (buf[0] & ETOUCH_PKT_TYPE_MASK) { +	case ETOUCH_PKT_TYPE_REPT: +	case ETOUCH_PKT_TYPE_REPT2: +		return 5; + +	case ETOUCH_PKT_TYPE_DIAG: +		if (len < 2) +			return -1; + +		return buf[1] + 2; +	} + +	return 0; +} +#endif  /*****************************************************************************   * PanJit Part @@ -367,8 +463,9 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)  	                      MTOUCHUSB_RESET,  	                      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,  	                      1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); -	dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", -	    __func__, ret); +	dev_dbg(&usbtouch->interface->dev, +		"%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d\n", +		__func__, ret);  	if (ret < 0)  		return ret;  	msleep(150); @@ -378,8 +475,9 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)  				      MTOUCHUSB_ASYNC_REPORT,  				      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,  				      1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT); -		dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", -		    __func__, ret); +		dev_dbg(&usbtouch->interface->dev, +			"%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d\n", +			__func__, ret);  		if (ret >= 0)  			break;  		if (ret != -EPIPE) @@ -679,27 +777,29 @@ static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)  #ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC  static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)  { +	struct usb_interface *intf = dev->interface; +  	switch (pkt[0]) {  	case 0x3A: /* command response */ -		dbg("%s: Command response %d", __func__, pkt[1]); +		dev_dbg(&intf->dev, "%s: Command response %d\n", __func__, pkt[1]);  		break;  	case 0xC0: /* down */  		dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);  		dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);  		dev->touch = 1; -		dbg("%s: down %d,%d", __func__, dev->x, dev->y); +		dev_dbg(&intf->dev, "%s: down %d,%d\n", __func__, dev->x, dev->y);  		return 1;  	case 0x80: /* up */  		dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);  		dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);  		dev->touch = 0; -		dbg("%s: up %d,%d", __func__, dev->x, dev->y); +		dev_dbg(&intf->dev, "%s: up %d,%d\n", __func__, dev->x, dev->y);  		return 1;  	default: -		dbg("%s: Unknown return %d", __func__, pkt[0]); +		dev_dbg(&intf->dev, "%s: Unknown return %d\n", __func__, pkt[0]);  		break;  	} @@ -754,7 +854,8 @@ static int nexio_alloc(struct usbtouch_usb *usbtouch)  	priv->ack = usb_alloc_urb(0, GFP_KERNEL);  	if (!priv->ack) { -		dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); +		dev_dbg(&usbtouch->interface->dev, +			"%s - usb_alloc_urb failed: usbtouch->ack\n", __func__);  		goto err_ack_buf;  	} @@ -944,6 +1045,24 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)  /***************************************************************************** + * ELO part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_ELO + +static int elo_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ +	dev->x = (pkt[3] << 8) | pkt[2]; +	dev->y = (pkt[5] << 8) | pkt[4]; +	dev->touch = pkt[6] > 0; +	dev->press = pkt[6]; + +	return 1; +} +#endif + + +/*****************************************************************************   * the different device descriptors   */  #ifdef MULTI_PACKET @@ -952,6 +1071,18 @@ static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,  #endif  static struct usbtouch_device_info usbtouch_dev_info[] = { +#ifdef CONFIG_TOUCHSCREEN_USB_ELO +	[DEVTYPE_ELO] = { +		.min_xc		= 0x0, +		.max_xc		= 0x0fff, +		.min_yc		= 0x0, +		.max_yc		= 0x0fff, +		.max_press	= 0xff, +		.rept_size	= 8, +		.read_data	= elo_read_data, +	}, +#endif +  #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX  	[DEVTYPE_EGALAX] = {  		.min_xc		= 0x0, @@ -962,6 +1093,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {  		.process_pkt	= usbtouch_process_multi,  		.get_pkt_len	= egalax_get_pkt_len,  		.read_data	= egalax_read_data, +		.init		= egalax_init,  	},  #endif @@ -1138,6 +1270,18 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {  		.exit		= nexio_exit,  	},  #endif +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH +	[DEVTYPE_ETOUCH] = { +		.min_xc		= 0x0, +		.max_xc		= 0x07ff, +		.min_yc		= 0x0, +		.max_yc		= 0x07ff, +		.rept_size	= 16, +		.process_pkt	= usbtouch_process_multi, +		.get_pkt_len	= etouch_get_pkt_len, +		.read_data	= etouch_read_data, +	}, +#endif  }; @@ -1249,6 +1393,7 @@ out_flush_buf:  static void usbtouch_irq(struct urb *urb)  {  	struct usbtouch_usb *usbtouch = urb->context; +	struct device *dev = &usbtouch->interface->dev;  	int retval;  	switch (urb->status) { @@ -1257,20 +1402,21 @@ static void usbtouch_irq(struct urb *urb)  		break;  	case -ETIME:  		/* this urb is timing out */ -		dbg("%s - urb timed out - was the device unplugged?", -		    __func__); +		dev_dbg(dev, +			"%s - urb timed out - was the device unplugged?\n", +			__func__);  		return;  	case -ECONNRESET:  	case -ENOENT:  	case -ESHUTDOWN:  	case -EPIPE:  		/* this urb is terminated, clean up */ -		dbg("%s - urb shutting down with status: %d", -		    __func__, urb->status); +		dev_dbg(dev, "%s - urb shutting down with status: %d\n", +			__func__, urb->status);  		return;  	default: -		dbg("%s - nonzero urb status received: %d", -		    __func__, urb->status); +		dev_dbg(dev, "%s - nonzero urb status received: %d\n", +			__func__, urb->status);  		goto exit;  	} @@ -1280,8 +1426,8 @@ exit:  	usb_mark_last_busy(interface_to_usbdev(usbtouch->interface));  	retval = usb_submit_urb(urb, GFP_ATOMIC);  	if (retval) -		err("%s - usb_submit_urb failed with result: %d", -		    __func__, retval); +		dev_err(dev, "%s - usb_submit_urb failed with result: %d\n", +			__func__, retval);  }  static int usbtouch_open(struct input_dev *input) @@ -1356,8 +1502,9 @@ static int usbtouch_reset_resume(struct usb_interface *intf)  	if (usbtouch->type->init) {  		err = usbtouch->type->init(usbtouch);  		if (err) { -			dbg("%s - type->init() failed, err: %d", -			    __func__, err); +			dev_dbg(&intf->dev, +				"%s - type->init() failed, err: %d\n", +				__func__, err);  			return err;  		}  	} @@ -1374,7 +1521,7 @@ static int usbtouch_reset_resume(struct usb_interface *intf)  static void usbtouch_free_buffers(struct usb_device *udev,  				  struct usbtouch_usb *usbtouch)  { -	usb_free_coherent(udev, usbtouch->type->rept_size, +	usb_free_coherent(udev, usbtouch->data_size,  			  usbtouch->data, usbtouch->data_dma);  	kfree(usbtouch->buffer);  } @@ -1419,7 +1566,20 @@ static int usbtouch_probe(struct usb_interface *intf,  	if (!type->process_pkt)  		type->process_pkt = usbtouch_process_pkt; -	usbtouch->data = usb_alloc_coherent(udev, type->rept_size, +	usbtouch->data_size = type->rept_size; +	if (type->get_pkt_len) { +		/* +		 * When dealing with variable-length packets we should +		 * not request more than wMaxPacketSize bytes at once +		 * as we do not know if there is more data coming or +		 * we filled exactly wMaxPacketSize bytes and there is +		 * nothing else. +		 */ +		usbtouch->data_size = min(usbtouch->data_size, +					  usb_endpoint_maxp(endpoint)); +	} + +	usbtouch->data = usb_alloc_coherent(udev, usbtouch->data_size,  					    GFP_KERNEL, &usbtouch->data_dma);  	if (!usbtouch->data)  		goto out_free; @@ -1432,7 +1592,8 @@ static int usbtouch_probe(struct usb_interface *intf,  	usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);  	if (!usbtouch->irq) { -		dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__); +		dev_dbg(&intf->dev, +			"%s - usb_alloc_urb failed: usbtouch->irq\n", __func__);  		goto out_free_buffers;  	} @@ -1478,12 +1639,12 @@ static int usbtouch_probe(struct usb_interface *intf,  	if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)  		usb_fill_int_urb(usbtouch->irq, udev,  			 usb_rcvintpipe(udev, endpoint->bEndpointAddress), -			 usbtouch->data, type->rept_size, +			 usbtouch->data, usbtouch->data_size,  			 usbtouch_irq, usbtouch, endpoint->bInterval);  	else  		usb_fill_bulk_urb(usbtouch->irq, udev,  			 usb_rcvbulkpipe(udev, endpoint->bEndpointAddress), -			 usbtouch->data, type->rept_size, +			 usbtouch->data, usbtouch->data_size,  			 usbtouch_irq, usbtouch);  	usbtouch->irq->dev = udev; @@ -1494,7 +1655,9 @@ static int usbtouch_probe(struct usb_interface *intf,  	if (type->alloc) {  		err = type->alloc(usbtouch);  		if (err) { -			dbg("%s - type->alloc() failed, err: %d", __func__, err); +			dev_dbg(&intf->dev, +				"%s - type->alloc() failed, err: %d\n", +				__func__, err);  			goto out_free_urb;  		}  	} @@ -1503,14 +1666,18 @@ static int usbtouch_probe(struct usb_interface *intf,  	if (type->init) {  		err = type->init(usbtouch);  		if (err) { -			dbg("%s - type->init() failed, err: %d", __func__, err); +			dev_dbg(&intf->dev, +				"%s - type->init() failed, err: %d\n", +				__func__, err);  			goto out_do_exit;  		}  	}  	err = input_register_device(usbtouch->input);  	if (err) { -		dbg("%s - input_register_device failed, err: %d", __func__, err); +		dev_dbg(&intf->dev, +			"%s - input_register_device failed, err: %d\n", +			__func__, err);  		goto out_do_exit;  	} @@ -1522,8 +1689,9 @@ static int usbtouch_probe(struct usb_interface *intf,  		err = usb_submit_urb(usbtouch->irq, GFP_KERNEL);  		if (err) {  			usb_autopm_put_interface(intf); -			err("%s - usb_submit_urb failed with result: %d", -			    __func__, err); +			dev_err(&intf->dev, +				"%s - usb_submit_urb failed with result: %d\n", +				__func__, err);  			goto out_unregister_input;  		}  	} @@ -1550,12 +1718,12 @@ static void usbtouch_disconnect(struct usb_interface *intf)  {  	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); -	dbg("%s - called", __func__); -  	if (!usbtouch)  		return; -	dbg("%s - usbtouch is initialized, cleaning up", __func__); +	dev_dbg(&intf->dev, +		"%s - usbtouch is initialized, cleaning up\n", __func__); +  	usb_set_intfdata(intf, NULL);  	/* this will stop IO via close */  	input_unregister_device(usbtouch->input); @@ -1579,18 +1747,7 @@ static struct usb_driver usbtouch_driver = {  	.supports_autosuspend = 1,  }; -static int __init usbtouch_init(void) -{ -	return usb_register(&usbtouch_driver); -} - -static void __exit usbtouch_cleanup(void) -{ -	usb_deregister(&usbtouch_driver); -} - -module_init(usbtouch_init); -module_exit(usbtouch_cleanup); +module_usb_driver(usbtouch_driver);  MODULE_AUTHOR(DRIVER_AUTHOR);  MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c index 7a45d68c351..003d0c3b5d0 100644 --- a/drivers/input/touchscreen/w90p910_ts.c +++ b/drivers/input/touchscreen/w90p910_ts.c @@ -215,7 +215,7 @@ static void w90p910_close(struct input_dev *dev)  	clk_disable(w90p910_ts->clk);  } -static int __devinit w90x900ts_probe(struct platform_device *pdev) +static int w90x900ts_probe(struct platform_device *pdev)  {  	struct w90p910_ts *w90p910_ts;  	struct input_dev *input_dev; @@ -279,7 +279,7 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev)  	w90p910_ts->irq_num = platform_get_irq(pdev, 0);  	if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt, -			IRQF_DISABLED, "w90p910ts", w90p910_ts)) { +			0, "w90p910ts", w90p910_ts)) {  		err = -EBUSY;  		goto fail4;  	} @@ -301,7 +301,7 @@ fail1:	input_free_device(input_dev);  	return err;  } -static int __devexit w90x900ts_remove(struct platform_device *pdev) +static int w90x900ts_remove(struct platform_device *pdev)  {  	struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev);  	struct resource *res; @@ -318,32 +318,18 @@ static int __devexit w90x900ts_remove(struct platform_device *pdev)  	input_unregister_device(w90p910_ts->input);  	kfree(w90p910_ts); -	platform_set_drvdata(pdev, NULL); -  	return 0;  }  static struct platform_driver w90x900ts_driver = {  	.probe		= w90x900ts_probe, -	.remove		= __devexit_p(w90x900ts_remove), +	.remove		= w90x900ts_remove,  	.driver		= {  		.name	= "nuc900-ts",  		.owner	= THIS_MODULE,  	},  }; - -static int __init w90x900ts_init(void) -{ -	return platform_driver_register(&w90x900ts_driver); -} - -static void __exit w90x900ts_exit(void) -{ -	platform_driver_unregister(&w90x900ts_driver); -} - -module_init(w90x900ts_init); -module_exit(w90x900ts_exit); +module_platform_driver(w90x900ts_driver);  MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");  MODULE_DESCRIPTION("w90p910 touch screen driver!"); diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c new file mode 100644 index 00000000000..7ccaa1b12b0 --- /dev/null +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -0,0 +1,288 @@ +/* + * Wacom Penabled Driver for I2C + * + * Copyright (c) 2011 - 2013 Tatsunosuke Tobita, Wacom. + * <tobita.tatsunosuke@wacom.co.jp> + * + * 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 of 2 of the License, + * or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <asm/unaligned.h> + +#define WACOM_CMD_QUERY0	0x04 +#define WACOM_CMD_QUERY1	0x00 +#define WACOM_CMD_QUERY2	0x33 +#define WACOM_CMD_QUERY3	0x02 +#define WACOM_CMD_THROW0	0x05 +#define WACOM_CMD_THROW1	0x00 +#define WACOM_QUERY_SIZE	19 + +struct wacom_features { +	int x_max; +	int y_max; +	int pressure_max; +	char fw_version; +}; + +struct wacom_i2c { +	struct i2c_client *client; +	struct input_dev *input; +	u8 data[WACOM_QUERY_SIZE]; +	bool prox; +	int tool; +}; + +static int wacom_query_device(struct i2c_client *client, +			      struct wacom_features *features) +{ +	int ret; +	u8 cmd1[] = { WACOM_CMD_QUERY0, WACOM_CMD_QUERY1, +			WACOM_CMD_QUERY2, WACOM_CMD_QUERY3 }; +	u8 cmd2[] = { WACOM_CMD_THROW0, WACOM_CMD_THROW1 }; +	u8 data[WACOM_QUERY_SIZE]; +	struct i2c_msg msgs[] = { +		{ +			.addr = client->addr, +			.flags = 0, +			.len = sizeof(cmd1), +			.buf = cmd1, +		}, +		{ +			.addr = client->addr, +			.flags = 0, +			.len = sizeof(cmd2), +			.buf = cmd2, +		}, +		{ +			.addr = client->addr, +			.flags = I2C_M_RD, +			.len = sizeof(data), +			.buf = data, +		}, +	}; + +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +	if (ret < 0) +		return ret; +	if (ret != ARRAY_SIZE(msgs)) +		return -EIO; + +	features->x_max = get_unaligned_le16(&data[3]); +	features->y_max = get_unaligned_le16(&data[5]); +	features->pressure_max = get_unaligned_le16(&data[11]); +	features->fw_version = get_unaligned_le16(&data[13]); + +	dev_dbg(&client->dev, +		"x_max:%d, y_max:%d, pressure:%d, fw:%d\n", +		features->x_max, features->y_max, +		features->pressure_max, features->fw_version); + +	return 0; +} + +static irqreturn_t wacom_i2c_irq(int irq, void *dev_id) +{ +	struct wacom_i2c *wac_i2c = dev_id; +	struct input_dev *input = wac_i2c->input; +	u8 *data = wac_i2c->data; +	unsigned int x, y, pressure; +	unsigned char tsw, f1, f2, ers; +	int error; + +	error = i2c_master_recv(wac_i2c->client, +				wac_i2c->data, sizeof(wac_i2c->data)); +	if (error < 0) +		goto out; + +	tsw = data[3] & 0x01; +	ers = data[3] & 0x04; +	f1 = data[3] & 0x02; +	f2 = data[3] & 0x10; +	x = le16_to_cpup((__le16 *)&data[4]); +	y = le16_to_cpup((__le16 *)&data[6]); +	pressure = le16_to_cpup((__le16 *)&data[8]); + +	if (!wac_i2c->prox) +		wac_i2c->tool = (data[3] & 0x0c) ? +			BTN_TOOL_RUBBER : BTN_TOOL_PEN; + +	wac_i2c->prox = data[3] & 0x20; + +	input_report_key(input, BTN_TOUCH, tsw || ers); +	input_report_key(input, wac_i2c->tool, wac_i2c->prox); +	input_report_key(input, BTN_STYLUS, f1); +	input_report_key(input, BTN_STYLUS2, f2); +	input_report_abs(input, ABS_X, x); +	input_report_abs(input, ABS_Y, y); +	input_report_abs(input, ABS_PRESSURE, pressure); +	input_sync(input); + +out: +	return IRQ_HANDLED; +} + +static int wacom_i2c_open(struct input_dev *dev) +{ +	struct wacom_i2c *wac_i2c = input_get_drvdata(dev); +	struct i2c_client *client = wac_i2c->client; + +	enable_irq(client->irq); + +	return 0; +} + +static void wacom_i2c_close(struct input_dev *dev) +{ +	struct wacom_i2c *wac_i2c = input_get_drvdata(dev); +	struct i2c_client *client = wac_i2c->client; + +	disable_irq(client->irq); +} + +static int wacom_i2c_probe(struct i2c_client *client, +				     const struct i2c_device_id *id) +{ +	struct wacom_i2c *wac_i2c; +	struct input_dev *input; +	struct wacom_features features = { 0 }; +	int error; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		dev_err(&client->dev, "i2c_check_functionality error\n"); +		return -EIO; +	} + +	error = wacom_query_device(client, &features); +	if (error) +		return error; + +	wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL); +	input = input_allocate_device(); +	if (!wac_i2c || !input) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	wac_i2c->client = client; +	wac_i2c->input = input; + +	input->name = "Wacom I2C Digitizer"; +	input->id.bustype = BUS_I2C; +	input->id.vendor = 0x56a; +	input->id.version = features.fw_version; +	input->dev.parent = &client->dev; +	input->open = wacom_i2c_open; +	input->close = wacom_i2c_close; + +	input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + +	__set_bit(BTN_TOOL_PEN, input->keybit); +	__set_bit(BTN_TOOL_RUBBER, input->keybit); +	__set_bit(BTN_STYLUS, input->keybit); +	__set_bit(BTN_STYLUS2, input->keybit); +	__set_bit(BTN_TOUCH, input->keybit); + +	input_set_abs_params(input, ABS_X, 0, features.x_max, 0, 0); +	input_set_abs_params(input, ABS_Y, 0, features.y_max, 0, 0); +	input_set_abs_params(input, ABS_PRESSURE, +			     0, features.pressure_max, 0, 0); + +	input_set_drvdata(input, wac_i2c); + +	error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq, +				     IRQF_TRIGGER_LOW | IRQF_ONESHOT, +				     "wacom_i2c", wac_i2c); +	if (error) { +		dev_err(&client->dev, +			"Failed to enable IRQ, error: %d\n", error); +		goto err_free_mem; +	} + +	/* Disable the IRQ, we'll enable it in wac_i2c_open() */ +	disable_irq(client->irq); + +	error = input_register_device(wac_i2c->input); +	if (error) { +		dev_err(&client->dev, +			"Failed to register input device, error: %d\n", error); +		goto err_free_irq; +	} + +	i2c_set_clientdata(client, wac_i2c); +	return 0; + +err_free_irq: +	free_irq(client->irq, wac_i2c); +err_free_mem: +	input_free_device(input); +	kfree(wac_i2c); + +	return error; +} + +static int wacom_i2c_remove(struct i2c_client *client) +{ +	struct wacom_i2c *wac_i2c = i2c_get_clientdata(client); + +	free_irq(client->irq, wac_i2c); +	input_unregister_device(wac_i2c->input); +	kfree(wac_i2c); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int wacom_i2c_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	disable_irq(client->irq); + +	return 0; +} + +static int wacom_i2c_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	enable_irq(client->irq); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume); + +static const struct i2c_device_id wacom_i2c_id[] = { +	{ "WAC_I2C_EMR", 0 }, +	{ }, +}; +MODULE_DEVICE_TABLE(i2c, wacom_i2c_id); + +static struct i2c_driver wacom_i2c_driver = { +	.driver	= { +		.name	= "wacom_i2c", +		.owner	= THIS_MODULE, +		.pm	= &wacom_i2c_pm, +	}, + +	.probe		= wacom_i2c_probe, +	.remove		= wacom_i2c_remove, +	.id_table	= wacom_i2c_id, +}; +module_i2c_driver(wacom_i2c_driver); + +MODULE_AUTHOR("Tatsunosuke Tobita <tobita.tatsunosuke@wacom.co.jp>"); +MODULE_DESCRIPTION("WACOM EMR I2C Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 9ae4c7b16ba..2792ca397dd 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -3,6 +3,7 @@   *   * Copyright (c) 2008 Jaya Kumar   * Copyright (c) 2010 Red Hat, Inc. + * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>   *   * This file is subject to the terms and conditions of the GNU General Public   * License. See the file COPYING in the main directory of this archive for @@ -15,10 +16,10 @@  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/slab.h> -#include <linux/input.h> +#include <linux/input/mt.h>  #include <linux/serio.h> -#include <linux/init.h>  #include <linux/ctype.h> +#include <linux/delay.h>  #define DRIVER_DESC	"Wacom W8001 serial touchscreen driver" @@ -37,6 +38,7 @@ MODULE_LICENSE("GPL");  #define W8001_QUERY_PACKET	0x20 +#define W8001_CMD_STOP		'0'  #define W8001_CMD_START		'1'  #define W8001_CMD_QUERY		'*'  #define W8001_CMD_TOUCHQUERY	'%' @@ -48,7 +50,9 @@ MODULE_LICENSE("GPL");  #define W8001_PKTLEN_TPCCTL	11	/* control packet */  #define W8001_PKTLEN_TOUCH2FG	13 -#define MAX_TRACKING_ID		0xFF	/* arbitrarily chosen */ +/* resolution in points/mm */ +#define W8001_PEN_RESOLUTION    100 +#define W8001_TOUCH_RESOLUTION  10  struct w8001_coord {  	u8 rdy; @@ -64,11 +68,11 @@ struct w8001_coord {  /* touch query reply packet */  struct w8001_touch_query { +	u16 x; +	u16 y;  	u8 panel_res;  	u8 capacity_res;  	u8 sensor_id; -	u16 x; -	u16 y;  };  /* @@ -87,10 +91,14 @@ struct w8001 {  	char phys[32];  	int type;  	unsigned int pktlen; -	int trkid[2]; +	u16 max_touch_x; +	u16 max_touch_y; +	u16 max_pen_x; +	u16 max_pen_y; +	char name[64];  }; -static void parse_data(u8 *data, struct w8001_coord *coord) +static void parse_pen_data(u8 *data, struct w8001_coord *coord)  {  	memset(coord, 0, sizeof(*coord)); @@ -114,30 +122,58 @@ static void parse_data(u8 *data, struct w8001_coord *coord)  	coord->tilt_y = data[8] & 0x7F;  } -static void parse_touch(struct w8001 *w8001) +static void parse_single_touch(u8 *data, struct w8001_coord *coord) +{ +	coord->x = (data[1] << 7) | data[2]; +	coord->y = (data[3] << 7) | data[4]; +	coord->tsw = data[0] & 0x01; +} + +static void scale_touch_coordinates(struct w8001 *w8001, +				    unsigned int *x, unsigned int *y) +{ +	if (w8001->max_pen_x && w8001->max_touch_x) +		*x = *x * w8001->max_pen_x / w8001->max_touch_x; + +	if (w8001->max_pen_y && w8001->max_touch_y) +		*y = *y * w8001->max_pen_y / w8001->max_touch_y; +} + +static void parse_multi_touch(struct w8001 *w8001)  { -	static int trkid;  	struct input_dev *dev = w8001->dev;  	unsigned char *data = w8001->data; +	unsigned int x, y;  	int i; +	int count = 0;  	for (i = 0; i < 2; i++) { -		input_mt_slot(dev, i); +		bool touch = data[0] & (1 << i); -		if (data[0] & (1 << i)) { -			int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); -			int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); +		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];  			/* data[5,6] and [11,12] is finger capacity */ +			/* scale to pen maximum */ +			scale_touch_coordinates(w8001, &x, &y); +  			input_report_abs(dev, ABS_MT_POSITION_X, x);  			input_report_abs(dev, ABS_MT_POSITION_Y, y); -			input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); -			if (w8001->trkid[i] < 0) -				w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; -		} else { -			w8001->trkid[i] = -1; +			count++;  		} -		input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); +	} + +	/* emulate single touch events when stylus is out of proximity. +	 * This is to make single touch backward support consistent +	 * across all Wacom single touch devices. +	 */ +	if (w8001->type != BTN_TOOL_PEN && +			    w8001->type != BTN_TOOL_RUBBER) { +		w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED; +		input_mt_report_pointer_emulation(dev, true);  	}  	input_sync(dev); @@ -158,6 +194,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)  	query->y = data[5] << 9;  	query->y |= data[6] << 2;  	query->y |= (data[2] >> 3) & 0x3; + +	/* Early days' single-finger touch models need the following defaults */ +	if (!query->x && !query->y) { +		query->x = 1024; +		query->y = 1024; +		if (query->panel_res) +			query->x = query->y = (1 << query->panel_res); +		query->panel_res = W8001_TOUCH_RESOLUTION; +	}  }  static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) @@ -167,16 +212,15 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)  	/*  	 * We have 1 bit for proximity (rdy) and 3 bits for tip, side,  	 * side2/eraser. If rdy && f2 are set, this can be either pen + side2, -	 * or eraser. assume +	 * or eraser. Assume:  	 * - if dev is already in proximity and f2 is toggled → pen + side2  	 * - if dev comes into proximity with f2 set → eraser  	 * If f2 disappears after assuming eraser, fake proximity out for  	 * eraser and in for pen.  	 */ -	if (!w8001->type) { -		w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; -	} else if (w8001->type == BTN_TOOL_RUBBER) { +	switch (w8001->type) { +	case BTN_TOOL_RUBBER:  		if (!coord->f2) {  			input_report_abs(dev, ABS_PRESSURE, 0);  			input_report_key(dev, BTN_TOUCH, 0); @@ -186,8 +230,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)  			input_sync(dev);  			w8001->type = BTN_TOOL_PEN;  		} -	} else { +		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; + +	default:  		input_report_key(dev, BTN_STYLUS2, coord->f2); +		break;  	}  	input_report_abs(dev, ABS_X, coord->x); @@ -199,7 +256,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)  	input_sync(dev);  	if (!coord->rdy) -		w8001->type = 0; +		w8001->type = KEY_RESERVED; +} + +static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) +{ +	struct input_dev *dev = w8001->dev; +	unsigned int x = coord->x; +	unsigned int y = coord->y; + +	/* scale to pen maximum */ +	scale_touch_coordinates(w8001, &x, &y); + +	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); + +	w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;  }  static irqreturn_t w8001_interrupt(struct serio *serio, @@ -220,9 +296,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,  	case W8001_PKTLEN_TOUCH93 - 1:  	case W8001_PKTLEN_TOUCH9A - 1: -		/* ignore one-finger touch packet. */ -		if (w8001->pktlen == w8001->idx) +		tmp = w8001->data[0] & W8001_TOUCH_BYTE; +		if (tmp != W8001_TOUCH_BYTE) +			break; + +		if (w8001->pktlen == w8001->idx) {  			w8001->idx = 0; +			if (w8001->type != BTN_TOOL_PEN && +			    w8001->type != BTN_TOOL_RUBBER) { +				parse_single_touch(w8001->data, &coord); +				report_single_touch(w8001, &coord); +			} +		}  		break;  	/* Pen coordinates packet */ @@ -231,18 +316,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,  		if (unlikely(tmp == W8001_TAB_BYTE))  			break; -		tmp = (w8001->data[0] & W8001_TOUCH_BYTE); +		tmp = w8001->data[0] & W8001_TOUCH_BYTE;  		if (tmp == W8001_TOUCH_BYTE)  			break;  		w8001->idx = 0; -		parse_data(w8001->data, &coord); +		parse_pen_data(w8001->data, &coord);  		report_pen_events(w8001, &coord);  		break;  	/* control packet */  	case W8001_PKTLEN_TPCCTL - 1: -		tmp = (w8001->data[0] & W8001_TOUCH_MASK); +		tmp = w8001->data[0] & W8001_TOUCH_MASK;  		if (tmp == W8001_TOUCH_BYTE)  			break; @@ -255,7 +340,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio,  	/* 2 finger touch packet */  	case W8001_PKTLEN_TOUCH2FG - 1:  		w8001->idx = 0; -		parse_touch(w8001); +		parse_multi_touch(w8001);  		break;  	} @@ -281,57 +366,130 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,  	return rc;  } +static int w8001_open(struct input_dev *dev) +{ +	struct w8001 *w8001 = input_get_drvdata(dev); + +	return w8001_command(w8001, W8001_CMD_START, false); +} + +static void w8001_close(struct input_dev *dev) +{ +	struct w8001 *w8001 = input_get_drvdata(dev); + +	w8001_command(w8001, W8001_CMD_STOP, false); +} +  static int w8001_setup(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_QUERY, true); +	error = w8001_command(w8001, W8001_CMD_STOP, false);  	if (error)  		return error; -	parse_data(w8001->response, &coord); +	msleep(250);	/* wait 250ms before querying the device */ -	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_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); -	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); +	dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +	strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); -	error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); +	__set_bit(INPUT_PROP_DIRECT, dev->propbit); + +	/* penabled? */ +	error = w8001_command(w8001, W8001_CMD_QUERY, true);  	if (!error) { -		struct w8001_touch_query touch; +		__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)); +	} + +	/* Touch enabled? */ +	error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + +	/* +	 * 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; +		} + +		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; +  		case 1:  		case 3:  		case 4:  			w8001->pktlen = W8001_PKTLEN_TOUCH9A; +			strlcat(w8001->name, " 1FG", sizeof(w8001->name)); +			w8001->id = 0x9a;  			break; +  		case 5:  			w8001->pktlen = W8001_PKTLEN_TOUCH2FG; -			input_mt_create_slots(dev, 2); -			input_set_abs_params(dev, ABS_MT_TRACKING_ID, -						0, MAX_TRACKING_ID, 0, 0); +			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, 0, 0, 0); +						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;  		}  	} -	return w8001_command(w8001, W8001_CMD_START, false); +	strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + +	return 0;  }  /* @@ -342,12 +500,12 @@ static void w8001_disconnect(struct serio *serio)  {  	struct w8001 *w8001 = serio_get_drvdata(serio); -	input_get_device(w8001->dev); -	input_unregister_device(w8001->dev);  	serio_close(serio); -	serio_set_drvdata(serio, NULL); -	input_put_device(w8001->dev); + +	input_unregister_device(w8001->dev);  	kfree(w8001); + +	serio_set_drvdata(serio, NULL);  }  /* @@ -370,27 +528,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)  	}  	w8001->serio = serio; -	w8001->id = serio->id.id;  	w8001->dev = input_dev; -	w8001->trkid[0] = w8001->trkid[1] = -1;  	init_completion(&w8001->cmd_done);  	snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); -	input_dev->name = "Wacom W8001 Penabled Serial TouchScreen"; -	input_dev->phys = w8001->phys; -	input_dev->id.bustype = BUS_RS232; -	input_dev->id.vendor = SERIO_W8001; -	input_dev->id.product = w8001->id; -	input_dev->id.version = 0x0100; -	input_dev->dev.parent = &serio->dev; - -	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); -	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); -	input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); -	input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); -	input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); -	input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); -  	serio_set_drvdata(serio, w8001);  	err = serio_open(serio, drv);  	if (err) @@ -400,6 +541,19 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)  	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; + +	input_dev->open = w8001_open; +	input_dev->close = w8001_close; + +	input_set_drvdata(input_dev, w8001); +  	err = input_register_device(w8001->dev);  	if (err)  		goto fail3; @@ -439,15 +593,4 @@ static struct serio_driver w8001_drv = {  	.disconnect	= w8001_disconnect,  }; -static int __init w8001_init(void) -{ -	return serio_register_driver(&w8001_drv); -} - -static void __exit w8001_exit(void) -{ -	serio_unregister_driver(&w8001_drv); -} - -module_init(w8001_init); -module_exit(w8001_exit); +module_serio_driver(w8001_drv); diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c new file mode 100644 index 00000000000..1b953a066b2 --- /dev/null +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -0,0 +1,406 @@ +/* + * Touchscreen driver for WM831x PMICs + * + * Copyright 2011 Wolfson Microelectronics plc. + * Author: Mark Brown <broonie@opensource.wolfsonmicro.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. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/pm.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/irq.h> +#include <linux/mfd/wm831x/pdata.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +/* + * R16424 (0x4028) - Touch Control 1 + */ +#define WM831X_TCH_ENA                          0x8000  /* TCH_ENA */ +#define WM831X_TCH_CVT_ENA                      0x4000  /* TCH_CVT_ENA */ +#define WM831X_TCH_SLPENA                       0x1000  /* TCH_SLPENA */ +#define WM831X_TCH_Z_ENA                        0x0400  /* TCH_Z_ENA */ +#define WM831X_TCH_Y_ENA                        0x0200  /* TCH_Y_ENA */ +#define WM831X_TCH_X_ENA                        0x0100  /* TCH_X_ENA */ +#define WM831X_TCH_DELAY_MASK                   0x00E0  /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_SHIFT                       5  /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_WIDTH                       3  /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_RATE_MASK                    0x001F  /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_SHIFT                        0  /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_WIDTH                        5  /* TCH_RATE - [4:0] */ + +/* + * R16425 (0x4029) - Touch Control 2 + */ +#define WM831X_TCH_PD_WK                        0x2000  /* TCH_PD_WK */ +#define WM831X_TCH_5WIRE                        0x1000  /* TCH_5WIRE */ +#define WM831X_TCH_PDONLY                       0x0800  /* TCH_PDONLY */ +#define WM831X_TCH_ISEL                         0x0100  /* TCH_ISEL */ +#define WM831X_TCH_RPU_MASK                     0x000F  /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_SHIFT                         0  /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_WIDTH                         4  /* TCH_RPU - [3:0] */ + +/* + * R16426-8 (0x402A-C) - Touch Data X/Y/X + */ +#define WM831X_TCH_PD                           0x8000  /* TCH_PD1 */ +#define WM831X_TCH_DATA_MASK                    0x0FFF  /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_SHIFT                        0  /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_WIDTH                       12  /* TCH_DATA - [11:0] */ + +struct wm831x_ts { +	struct input_dev *input_dev; +	struct wm831x *wm831x; +	unsigned int data_irq; +	unsigned int pd_irq; +	bool pressure; +	bool pen_down; +	struct work_struct pd_data_work; +}; + +static void wm831x_pd_data_work(struct work_struct *work) +{ +	struct wm831x_ts *wm831x_ts = +		container_of(work, struct wm831x_ts, pd_data_work); + +	if (wm831x_ts->pen_down) { +		enable_irq(wm831x_ts->data_irq); +		dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n"); +	} else { +		enable_irq(wm831x_ts->pd_irq); +		dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n"); +	} +} + +static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) +{ +	struct wm831x_ts *wm831x_ts = irq_data; +	struct wm831x *wm831x = wm831x_ts->wm831x; +	static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE }; +	u16 data[3]; +	int count; +	int i, ret; + +	if (wm831x_ts->pressure) +		count = 3; +	else +		count = 2; + +	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, +			WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + +	ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, +			       data); +	if (ret != 0) { +		dev_err(wm831x->dev, "Failed to read touch data: %d\n", +			ret); +		return IRQ_NONE; +	} + +	/* +	 * We get a pen down reading on every reading, report pen up if any +	 * individual reading does so. +	 */ +	wm831x_ts->pen_down = true; +	for (i = 0; i < count; i++) { +		if (!(data[i] & WM831X_TCH_PD)) { +			wm831x_ts->pen_down = false; +			continue; +		} +		input_report_abs(wm831x_ts->input_dev, data_types[i], +				 data[i] & WM831X_TCH_DATA_MASK); +	} + +	if (!wm831x_ts->pen_down) { +		/* Switch from data to pen down */ +		dev_dbg(wm831x->dev, "IRQ DATA->PD\n"); + +		disable_irq_nosync(wm831x_ts->data_irq); + +		/* Don't need data any more */ +		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, +				WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | +				WM831X_TCH_Z_ENA, 0); + +		/* Flush any final samples that arrived while reading */ +		wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, +				WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + +		wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data); + +		if (wm831x_ts->pressure) +			input_report_abs(wm831x_ts->input_dev, +					 ABS_PRESSURE, 0); + +		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0); + +		schedule_work(&wm831x_ts->pd_data_work); +	} else { +		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); +	} + +	input_sync(wm831x_ts->input_dev); + +	return IRQ_HANDLED; +} + +static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) +{ +	struct wm831x_ts *wm831x_ts = irq_data; +	struct wm831x *wm831x = wm831x_ts->wm831x; +	int ena = 0; + +	if (wm831x_ts->pen_down) +		return IRQ_HANDLED; + +	disable_irq_nosync(wm831x_ts->pd_irq); + +	/* Start collecting data */ +	if (wm831x_ts->pressure) +		ena |= WM831X_TCH_Z_ENA; + +	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, +			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, +			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena); + +	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, +			WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); + +	wm831x_ts->pen_down = true; + +	/* Switch from pen down to data */ +	dev_dbg(wm831x->dev, "IRQ PD->DATA\n"); +	schedule_work(&wm831x_ts->pd_data_work); + +	return IRQ_HANDLED; +} + +static int wm831x_ts_input_open(struct input_dev *idev) +{ +	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); +	struct wm831x *wm831x = wm831x_ts->wm831x; + +	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, +			WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | +			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | +			WM831X_TCH_Z_ENA, WM831X_TCH_ENA); + +	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, +			WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA); + +	return 0; +} + +static void wm831x_ts_input_close(struct input_dev *idev) +{ +	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); +	struct wm831x *wm831x = wm831x_ts->wm831x; + +	/* Shut the controller down, disabling all other functionality too */ +	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, +			WM831X_TCH_ENA | WM831X_TCH_X_ENA | +			WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0); + +	/* Make sure any pending IRQs are done, the above will prevent +	 * new ones firing. +	 */ +	synchronize_irq(wm831x_ts->data_irq); +	synchronize_irq(wm831x_ts->pd_irq); + +	/* Make sure the IRQ completion work is quiesced */ +	flush_work(&wm831x_ts->pd_data_work); + +	/* If we ended up with the pen down then make sure we revert back +	 * to pen detection state for the next time we start up. +	 */ +	if (wm831x_ts->pen_down) { +		disable_irq(wm831x_ts->data_irq); +		enable_irq(wm831x_ts->pd_irq); +		wm831x_ts->pen_down = false; +	} +} + +static int wm831x_ts_probe(struct platform_device *pdev) +{ +	struct wm831x_ts *wm831x_ts; +	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); +	struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent); +	struct wm831x_touch_pdata *pdata = NULL; +	struct input_dev *input_dev; +	int error, irqf; + +	if (core_pdata) +		pdata = core_pdata->touch; + +	wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts), +				 GFP_KERNEL); +	input_dev = devm_input_allocate_device(&pdev->dev); +	if (!wm831x_ts || !input_dev) { +		error = -ENOMEM; +		goto err_alloc; +	} + +	wm831x_ts->wm831x = wm831x; +	wm831x_ts->input_dev = input_dev; +	INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work); + +	/* +	 * If we have a direct IRQ use it, otherwise use the interrupt +	 * from the WM831x IRQ controller. +	 */ +	wm831x_ts->data_irq = wm831x_irq(wm831x, +					 platform_get_irq_byname(pdev, +								 "TCHDATA")); +	if (pdata && pdata->data_irq) +		wm831x_ts->data_irq = pdata->data_irq; + +	wm831x_ts->pd_irq = wm831x_irq(wm831x, +				       platform_get_irq_byname(pdev, "TCHPD")); +	if (pdata && pdata->pd_irq) +		wm831x_ts->pd_irq = pdata->pd_irq; + +	if (pdata) +		wm831x_ts->pressure = pdata->pressure; +	else +		wm831x_ts->pressure = true; + +	/* Five wire touchscreens can't report pressure */ +	if (pdata && pdata->fivewire) { +		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, +				WM831X_TCH_5WIRE, WM831X_TCH_5WIRE); + +		/* Pressure measurements are not possible for five wire mode */ +		WARN_ON(pdata->pressure && pdata->fivewire); +		wm831x_ts->pressure = false; +	} else { +		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, +				WM831X_TCH_5WIRE, 0); +	} + +	if (pdata) { +		switch (pdata->isel) { +		default: +			dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n", +				pdata->isel); +			/* Fall through */ +		case 200: +		case 0: +			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, +					WM831X_TCH_ISEL, 0); +			break; +		case 400: +			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, +					WM831X_TCH_ISEL, WM831X_TCH_ISEL); +			break; +		} +	} + +	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, +			WM831X_TCH_PDONLY, 0); + +	/* Default to 96 samples/sec */ +	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, +			WM831X_TCH_RATE_MASK, 6); + +	if (pdata && pdata->data_irqf) +		irqf = pdata->data_irqf; +	else +		irqf = IRQF_TRIGGER_HIGH; + +	error = request_threaded_irq(wm831x_ts->data_irq, +				     NULL, wm831x_ts_data_irq, +				     irqf | IRQF_ONESHOT, +				     "Touchscreen data", wm831x_ts); +	if (error) { +		dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n", +			wm831x_ts->data_irq, error); +		goto err_alloc; +	} +	disable_irq(wm831x_ts->data_irq); + +	if (pdata && pdata->pd_irqf) +		irqf = pdata->pd_irqf; +	else +		irqf = IRQF_TRIGGER_HIGH; + +	error = request_threaded_irq(wm831x_ts->pd_irq, +				     NULL, wm831x_ts_pen_down_irq, +				     irqf | IRQF_ONESHOT, +				     "Touchscreen pen down", wm831x_ts); +	if (error) { +		dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n", +			wm831x_ts->pd_irq, error); +		goto err_data_irq; +	} + +	/* set up touch configuration */ +	input_dev->name = "WM831x touchscreen"; +	input_dev->phys = "wm831x"; +	input_dev->open = wm831x_ts_input_open; +	input_dev->close = wm831x_ts_input_close; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); + +	input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0); +	input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0); +	if (wm831x_ts->pressure) +		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0); + +	input_set_drvdata(input_dev, wm831x_ts); +	input_dev->dev.parent = &pdev->dev; + +	error = input_register_device(input_dev); +	if (error) +		goto err_pd_irq; + +	platform_set_drvdata(pdev, wm831x_ts); +	return 0; + +err_pd_irq: +	free_irq(wm831x_ts->pd_irq, wm831x_ts); +err_data_irq: +	free_irq(wm831x_ts->data_irq, wm831x_ts); +err_alloc: + +	return error; +} + +static int wm831x_ts_remove(struct platform_device *pdev) +{ +	struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev); + +	free_irq(wm831x_ts->pd_irq, wm831x_ts); +	free_irq(wm831x_ts->data_irq, wm831x_ts); + +	return 0; +} + +static struct platform_driver wm831x_ts_driver = { +	.driver = { +		.name = "wm831x-touch", +		.owner = THIS_MODULE, +	}, +	.probe = wm831x_ts_probe, +	.remove = wm831x_ts_remove, +}; +module_platform_driver(wm831x_ts_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("WM831x PMIC touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-touch"); diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c index 6b5be742c27..adc13a523ab 100644 --- a/drivers/input/touchscreen/wm9705.c +++ b/drivers/input/touchscreen/wm9705.c @@ -215,8 +215,9 @@ static inline int is_pden(struct wm97xx *wm)  static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  {  	int timeout = 5 * delay; +	bool wants_pen = adcsel & WM97XX_PEN_DOWN; -	if (!wm->pen_probably_down) { +	if (wants_pen && !wm->pen_probably_down) {  		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);  		if (!(data & WM97XX_PEN_DOWN))  			return RC_PENUP; @@ -224,13 +225,10 @@ static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  	}  	/* set up digitiser */ -	if (adcsel & 0x8000) -		adcsel = ((adcsel & 0x7fff) + 3) << 12; -  	if (wm->mach_ops && wm->mach_ops->pre_sample)  		wm->mach_ops->pre_sample(adcsel); -	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, -			 adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); +	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK) +				| WM97XX_POLL | WM97XX_DELAY(delay));  	/* wait 3 AC97 time slots + delay for conversion */  	poll_delay(delay); @@ -256,13 +254,14 @@ static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  		wm->mach_ops->post_sample(adcsel);  	/* check we have correct sample */ -	if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { -		dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, -		*sample & WM97XX_ADCSEL_MASK); +	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { +		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x", +			adcsel & WM97XX_ADCSEL_MASK, +			*sample & WM97XX_ADCSEL_MASK);  		return RC_PENUP;  	} -	if (!(*sample & WM97XX_PEN_DOWN)) { +	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {  		wm->pen_probably_down = 0;  		return RC_PENUP;  	} @@ -277,14 +276,14 @@ static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)  {  	int rc; -	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x); +	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);  	if (rc != RC_VALID)  		return rc; -	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y); +	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);  	if (rc != RC_VALID)  		return rc;  	if (pil) { -		rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p); +		rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);  		if (rc != RC_VALID)  			return rc;  	} else @@ -306,7 +305,7 @@ static int wm9705_acc_enable(struct wm97xx *wm, int enable)  	dig2 = wm->dig[2];  	if (enable) { -		/* continous mode */ +		/* continuous mode */  		if (wm->mach_ops->acc_startup &&  		    (ret = wm->mach_ops->acc_startup(wm)) < 0)  			return ret; diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c index 7490b05c356..16b52115c27 100644 --- a/drivers/input/touchscreen/wm9712.c +++ b/drivers/input/touchscreen/wm9712.c @@ -162,14 +162,14 @@ static void wm9712_phy_init(struct wm97xx *wm)  	if (rpu) {  		dig2 &= 0xffc0;  		dig2 |= WM9712_RPU(rpu); -		dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms", +		dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms\n",  			64000 / rpu);  	}  	/* WM9712 five wire */  	if (five_wire) {  		dig2 |= WM9712_45W; -		dev_dbg(wm->dev, "setting 5-wire touchscreen mode."); +		dev_dbg(wm->dev, "setting 5-wire touchscreen mode.\n");  		if (pil) {  			dev_warn(wm->dev, "pressure measurement is not " @@ -182,21 +182,21 @@ static void wm9712_phy_init(struct wm97xx *wm)  	if (pil == 2) {  		dig2 |= WM9712_PIL;  		dev_dbg(wm->dev, -			"setting pressure measurement current to 400uA."); +			"setting pressure measurement current to 400uA.\n");  	} else if (pil)  		dev_dbg(wm->dev, -			"setting pressure measurement current to 200uA."); +			"setting pressure measurement current to 200uA.\n");  	if (!pil)  		pressure = 0;  	/* polling mode sample settling delay */  	if (delay < 0 || delay > 15) { -		dev_dbg(wm->dev, "supplied delay out of range."); +		dev_dbg(wm->dev, "supplied delay out of range.\n");  		delay = 4;  	}  	dig1 &= 0xff0f;  	dig1 |= WM97XX_DELAY(delay); -	dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", +	dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.\n",  		delay_table[delay]);  	/* mask */ @@ -255,8 +255,9 @@ static inline int is_pden(struct wm97xx *wm)  static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  {  	int timeout = 5 * delay; +	bool wants_pen = adcsel & WM97XX_PEN_DOWN; -	if (!wm->pen_probably_down) { +	if (wants_pen && !wm->pen_probably_down) {  		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);  		if (!(data & WM97XX_PEN_DOWN))  			return RC_PENUP; @@ -264,13 +265,10 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  	}  	/* set up digitiser */ -	if (adcsel & 0x8000) -		adcsel = ((adcsel & 0x7fff) + 3) << 12; -  	if (wm->mach_ops && wm->mach_ops->pre_sample)  		wm->mach_ops->pre_sample(adcsel); -	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, -			 adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); +	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK) +				| WM97XX_POLL | WM97XX_DELAY(delay));  	/* wait 3 AC97 time slots + delay for conversion */  	poll_delay(delay); @@ -287,7 +285,7 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  		if (is_pden(wm))  			wm->pen_probably_down = 0;  		else -			dev_dbg(wm->dev, "adc sample timeout"); +			dev_dbg(wm->dev, "adc sample timeout\n");  		return RC_PENUP;  	} @@ -296,15 +294,20 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  		wm->mach_ops->post_sample(adcsel);  	/* check we have correct sample */ -	if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { -		dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, -		*sample & WM97XX_ADCSEL_MASK); -		return RC_PENUP; +	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { +		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x\n", +			adcsel & WM97XX_ADCSEL_MASK, +			*sample & WM97XX_ADCSEL_MASK); +		return RC_AGAIN;  	} -	if (!(*sample & WM97XX_PEN_DOWN)) { -		wm->pen_probably_down = 0; -		return RC_PENUP; +	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { +		/* Sometimes it reads a wrong value the first time. */ +		*sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); +		if (!(*sample & WM97XX_PEN_DOWN)) { +			wm->pen_probably_down = 0; +			return RC_PENUP; +		}  	}  	return RC_VALID; @@ -346,7 +349,7 @@ static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)  		if (is_pden(wm))  			wm->pen_probably_down = 0;  		else -			dev_dbg(wm->dev, "adc sample timeout"); +			dev_dbg(wm->dev, "adc sample timeout\n");  		return RC_PENUP;  	} @@ -387,16 +390,18 @@ static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)  		if (rc != RC_VALID)  			return rc;  	} else { -		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x); +		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, +					&data->x);  		if (rc != RC_VALID)  			return rc; -		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y); +		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, +					&data->y);  		if (rc != RC_VALID)  			return rc;  		if (pil && !five_wire) { -			rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, +			rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,  						&data->p);  			if (rc != RC_VALID)  				return rc; @@ -419,7 +424,7 @@ static int wm9712_acc_enable(struct wm97xx *wm, int enable)  	dig2 = wm->dig[2];  	if (enable) { -		/* continous mode */ +		/* continuous mode */  		if (wm->mach_ops->acc_startup) {  			ret = wm->mach_ops->acc_startup(wm);  			if (ret < 0) diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c index 238b5132712..7405353199d 100644 --- a/drivers/input/touchscreen/wm9713.c +++ b/drivers/input/touchscreen/wm9713.c @@ -261,8 +261,9 @@ static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  {  	u16 dig1;  	int timeout = 5 * delay; +	bool wants_pen = adcsel & WM97XX_PEN_DOWN; -	if (!wm->pen_probably_down) { +	if (wants_pen && !wm->pen_probably_down) {  		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);  		if (!(data & WM97XX_PEN_DOWN))  			return RC_PENUP; @@ -270,15 +271,14 @@ static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  	}  	/* set up digitiser */ -	if (adcsel & 0x8000) -		adcsel = 1 << ((adcsel & 0x7fff) + 3); -  	dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);  	dig1 &= ~WM9713_ADCSEL_MASK; +	/* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */ +	dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12);  	if (wm->mach_ops && wm->mach_ops->pre_sample)  		wm->mach_ops->pre_sample(adcsel); -	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel | WM9713_POLL); +	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL);  	/* wait 3 AC97 time slots + delay for conversion */  	poll_delay(delay); @@ -304,13 +304,14 @@ static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)  		wm->mach_ops->post_sample(adcsel);  	/* check we have correct sample */ -	if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { -		dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, -			*sample & WM97XX_ADCSRC_MASK); +	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { +		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x", +			adcsel & WM97XX_ADCSEL_MASK, +			*sample & WM97XX_ADCSEL_MASK);  		return RC_PENUP;  	} -	if (!(*sample & WM97XX_PEN_DOWN)) { +	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {  		wm->pen_probably_down = 0;  		return RC_PENUP;  	} @@ -400,14 +401,14 @@ static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)  		if (rc != RC_VALID)  			return rc;  	} else { -		rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x); +		rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);  		if (rc != RC_VALID)  			return rc; -		rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y); +		rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);  		if (rc != RC_VALID)  			return rc;  		if (pil) { -			rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, +			rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,  						&data->p);  			if (rc != RC_VALID)  				return rc; @@ -431,7 +432,7 @@ static int wm9713_acc_enable(struct wm97xx *wm, int enable)  	dig3 = wm->dig[2];  	if (enable) { -		/* continous mode */ +		/* continuous mode */  		if (wm->mach_ops->acc_startup &&  			(ret = wm->mach_ops->acc_startup(wm)) < 0)  			return ret; diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 6b75c9f660a..d0ef91fc87d 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -335,7 +335,7 @@ static void wm97xx_pen_irq_worker(struct work_struct *work)  	 */  	if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) {  		if (wm->pen_is_down && !pen_was_down) { -			/* Data is not availiable immediately on pen down */ +			/* Data is not available immediately on pen down */  			queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1);  		} @@ -354,7 +354,7 @@ static void wm97xx_pen_irq_worker(struct work_struct *work)   * Codec PENDOWN irq handler   *   * We have to disable the codec interrupt in the handler because it - * can take upto 1ms to clear the interrupt source. We schedule a task + * can take up to 1ms to clear the interrupt source. We schedule a task   * in a work queue to do the actual interaction with the chip.  The   * interrupt is then enabled again in the slow handler when the source   * has been cleared. @@ -442,6 +442,16 @@ static int wm97xx_read_samples(struct wm97xx *wm)  			"pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",  			data.x >> 12, data.x & 0xfff, data.y >> 12,  			data.y & 0xfff, data.p >> 12, data.p & 0xfff); + +		if (abs_x[0] > (data.x & 0xfff) || +		    abs_x[1] < (data.x & 0xfff) || +		    abs_y[0] > (data.y & 0xfff) || +		    abs_y[1] < (data.y & 0xfff)) { +			dev_dbg(wm->dev, "Measurement out of range, dropping it\n"); +			rc = RC_AGAIN; +			goto out; +		} +  		input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);  		input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);  		input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); @@ -455,6 +465,7 @@ static int wm97xx_read_samples(struct wm97xx *wm)  		wm->ts_reader_interval = wm->ts_reader_min_interval;  	} +out:  	mutex_unlock(&wm->codec_mutex);  	return rc;  } @@ -573,7 +584,7 @@ static void wm97xx_ts_input_close(struct input_dev *idev)  static int wm97xx_probe(struct device *dev)  {  	struct wm97xx *wm; -	struct wm97xx_pdata *pdata = dev->platform_data; +	struct wm97xx_pdata *pdata = dev_get_platdata(dev);  	int ret = 0, id = 0;  	wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c new file mode 100644 index 00000000000..feea85b52fa --- /dev/null +++ b/drivers/input/touchscreen/zforce_ts.c @@ -0,0 +1,905 @@ +/* + * Copyright (C) 2012-2013 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * based in parts on Nook zforce driver + * + * Copyright (C) 2010 Barnes & Noble, Inc. + * Author: Pieter Truter<ptruter@intrinsyc.com> + * + * 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/module.h> +#include <linux/hrtimer.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/input/mt.h> +#include <linux/platform_data/zforce_ts.h> +#include <linux/of.h> +#include <linux/of_gpio.h> + +#define WAIT_TIMEOUT		msecs_to_jiffies(1000) + +#define FRAME_START		0xee +#define FRAME_MAXSIZE		257 + +/* Offsets of the different parts of the payload the controller sends */ +#define PAYLOAD_HEADER		0 +#define PAYLOAD_LENGTH		1 +#define PAYLOAD_BODY		2 + +/* Response offsets */ +#define RESPONSE_ID		0 +#define RESPONSE_DATA		1 + +/* Commands */ +#define COMMAND_DEACTIVATE	0x00 +#define COMMAND_INITIALIZE	0x01 +#define COMMAND_RESOLUTION	0x02 +#define COMMAND_SETCONFIG	0x03 +#define COMMAND_DATAREQUEST	0x04 +#define COMMAND_SCANFREQ	0x08 +#define COMMAND_STATUS		0X1e + +/* + * Responses the controller sends as a result of + * command requests + */ +#define RESPONSE_DEACTIVATE	0x00 +#define RESPONSE_INITIALIZE	0x01 +#define RESPONSE_RESOLUTION	0x02 +#define RESPONSE_SETCONFIG	0x03 +#define RESPONSE_SCANFREQ	0x08 +#define RESPONSE_STATUS		0X1e + +/* + * Notifications are sent by the touch controller without + * being requested by the driver and include for example + * touch indications + */ +#define NOTIFICATION_TOUCH		0x04 +#define NOTIFICATION_BOOTCOMPLETE	0x07 +#define NOTIFICATION_OVERRUN		0x25 +#define NOTIFICATION_PROXIMITY		0x26 +#define NOTIFICATION_INVALID_COMMAND	0xfe + +#define ZFORCE_REPORT_POINTS		2 +#define ZFORCE_MAX_AREA			0xff + +#define STATE_DOWN			0 +#define STATE_MOVE			1 +#define STATE_UP			2 + +#define SETCONFIG_DUALTOUCH		(1 << 0) + +struct zforce_point { +	int coord_x; +	int coord_y; +	int state; +	int id; +	int area_major; +	int area_minor; +	int orientation; +	int pressure; +	int prblty; +}; + +/* + * @client		the i2c_client + * @input		the input device + * @suspending		in the process of going to suspend (don't emit wakeup + *			events for commands executed to suspend the device) + * @suspended		device suspended + * @access_mutex	serialize i2c-access, to keep multipart reads together + * @command_done	completion to wait for the command result + * @command_mutex	serialize commands sent to the ic + * @command_waiting	the id of the command that is currently waiting + *			for a result + * @command_result	returned result of the command + */ +struct zforce_ts { +	struct i2c_client	*client; +	struct input_dev	*input; +	const struct zforce_ts_platdata *pdata; +	char			phys[32]; + +	bool			suspending; +	bool			suspended; +	bool			boot_complete; + +	/* Firmware version information */ +	u16			version_major; +	u16			version_minor; +	u16			version_build; +	u16			version_rev; + +	struct mutex		access_mutex; + +	struct completion	command_done; +	struct mutex		command_mutex; +	int			command_waiting; +	int			command_result; +}; + +static int zforce_command(struct zforce_ts *ts, u8 cmd) +{ +	struct i2c_client *client = ts->client; +	char buf[3]; +	int ret; + +	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd); + +	buf[0] = FRAME_START; +	buf[1] = 1; /* data size, command only */ +	buf[2] = cmd; + +	mutex_lock(&ts->access_mutex); +	ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf)); +	mutex_unlock(&ts->access_mutex); +	if (ret < 0) { +		dev_err(&client->dev, "i2c send data request error: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len) +{ +	struct i2c_client *client = ts->client; +	int ret; + +	ret = mutex_trylock(&ts->command_mutex); +	if (!ret) { +		dev_err(&client->dev, "already waiting for a command\n"); +		return -EBUSY; +	} + +	dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n", +		buf[1], buf[2]); + +	ts->command_waiting = buf[2]; + +	mutex_lock(&ts->access_mutex); +	ret = i2c_master_send(client, buf, len); +	mutex_unlock(&ts->access_mutex); +	if (ret < 0) { +		dev_err(&client->dev, "i2c send data request error: %d\n", ret); +		goto unlock; +	} + +	dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]); + +	if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) { +		ret = -ETIME; +		goto unlock; +	} + +	ret = ts->command_result; + +unlock: +	mutex_unlock(&ts->command_mutex); +	return ret; +} + +static int zforce_command_wait(struct zforce_ts *ts, u8 cmd) +{ +	struct i2c_client *client = ts->client; +	char buf[3]; +	int ret; + +	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd); + +	buf[0] = FRAME_START; +	buf[1] = 1; /* data size, command only */ +	buf[2] = cmd; + +	ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); +	if (ret < 0) { +		dev_err(&client->dev, "i2c send data request error: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y) +{ +	struct i2c_client *client = ts->client; +	char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION, +			(x & 0xff), ((x >> 8) & 0xff), +			(y & 0xff), ((y >> 8) & 0xff) }; + +	dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y); + +	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); +} + +static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger, +				 u16 stylus) +{ +	struct i2c_client *client = ts->client; +	char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ, +			(idle & 0xff), ((idle >> 8) & 0xff), +			(finger & 0xff), ((finger >> 8) & 0xff), +			(stylus & 0xff), ((stylus >> 8) & 0xff) }; + +	dev_dbg(&client->dev, +		"set scan frequency to (idle: %d, finger: %d, stylus: %d)\n", +		idle, finger, stylus); + +	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); +} + +static int zforce_setconfig(struct zforce_ts *ts, char b1) +{ +	struct i2c_client *client = ts->client; +	char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG, +			b1, 0, 0, 0 }; + +	dev_dbg(&client->dev, "set config to (%d)\n", b1); + +	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); +} + +static int zforce_start(struct zforce_ts *ts) +{ +	struct i2c_client *client = ts->client; +	const struct zforce_ts_platdata *pdata = ts->pdata; +	int ret; + +	dev_dbg(&client->dev, "starting device\n"); + +	ret = zforce_command_wait(ts, COMMAND_INITIALIZE); +	if (ret) { +		dev_err(&client->dev, "Unable to initialize, %d\n", ret); +		return ret; +	} + +	ret = zforce_resolution(ts, pdata->x_max, pdata->y_max); +	if (ret) { +		dev_err(&client->dev, "Unable to set resolution, %d\n", ret); +		goto error; +	} + +	ret = zforce_scan_frequency(ts, 10, 50, 50); +	if (ret) { +		dev_err(&client->dev, "Unable to set scan frequency, %d\n", +			ret); +		goto error; +	} + +	ret = zforce_setconfig(ts, SETCONFIG_DUALTOUCH); +	if (ret) { +		dev_err(&client->dev, "Unable to set config\n"); +		goto error; +	} + +	/* start sending touch events */ +	ret = zforce_command(ts, COMMAND_DATAREQUEST); +	if (ret) { +		dev_err(&client->dev, "Unable to request data\n"); +		goto error; +	} + +	/* +	 * Per NN, initial cal. take max. of 200msec. +	 * Allow time to complete this calibration +	 */ +	msleep(200); + +	return 0; + +error: +	zforce_command_wait(ts, COMMAND_DEACTIVATE); +	return ret; +} + +static int zforce_stop(struct zforce_ts *ts) +{ +	struct i2c_client *client = ts->client; +	int ret; + +	dev_dbg(&client->dev, "stopping device\n"); + +	/* Deactivates touch sensing and puts the device into sleep. */ +	ret = zforce_command_wait(ts, COMMAND_DEACTIVATE); +	if (ret != 0) { +		dev_err(&client->dev, "could not deactivate device, %d\n", +			ret); +		return ret; +	} + +	return 0; +} + +static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) +{ +	struct i2c_client *client = ts->client; +	const struct zforce_ts_platdata *pdata = ts->pdata; +	struct zforce_point point; +	int count, i, num = 0; + +	count = payload[0]; +	if (count > ZFORCE_REPORT_POINTS) { +		dev_warn(&client->dev, +			 "too many coordinates %d, expected max %d\n", +			 count, ZFORCE_REPORT_POINTS); +		count = ZFORCE_REPORT_POINTS; +	} + +	for (i = 0; i < count; i++) { +		point.coord_x = +			payload[9 * i + 2] << 8 | payload[9 * i + 1]; +		point.coord_y = +			payload[9 * i + 4] << 8 | payload[9 * i + 3]; + +		if (point.coord_x > pdata->x_max || +		    point.coord_y > pdata->y_max) { +			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n", +				point.coord_x, point.coord_y); +			point.coord_x = point.coord_y = 0; +		} + +		point.state = payload[9 * i + 5] & 0x03; +		point.id = (payload[9 * i + 5] & 0xfc) >> 2; + +		/* determine touch major, minor and orientation */ +		point.area_major = max(payload[9 * i + 6], +					  payload[9 * i + 7]); +		point.area_minor = min(payload[9 * i + 6], +					  payload[9 * i + 7]); +		point.orientation = payload[9 * i + 6] > payload[9 * i + 7]; + +		point.pressure = payload[9 * i + 8]; +		point.prblty = payload[9 * i + 9]; + +		dev_dbg(&client->dev, +			"point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n", +			i, count, point.state, point.id, +			point.pressure, point.prblty, +			point.coord_x, point.coord_y, +			point.area_major, point.area_minor, +			point.orientation); + +		/* the zforce id starts with "1", so needs to be decreased */ +		input_mt_slot(ts->input, point.id - 1); + +		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, +						point.state != STATE_UP); + +		if (point.state != STATE_UP) { +			input_report_abs(ts->input, ABS_MT_POSITION_X, +					 point.coord_x); +			input_report_abs(ts->input, ABS_MT_POSITION_Y, +					 point.coord_y); +			input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, +					 point.area_major); +			input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, +					 point.area_minor); +			input_report_abs(ts->input, ABS_MT_ORIENTATION, +					 point.orientation); +			num++; +		} +	} + +	input_mt_sync_frame(ts->input); + +	input_mt_report_finger_count(ts->input, num); + +	input_sync(ts->input); + +	return 0; +} + +static int zforce_read_packet(struct zforce_ts *ts, u8 *buf) +{ +	struct i2c_client *client = ts->client; +	int ret; + +	mutex_lock(&ts->access_mutex); + +	/* read 2 byte message header */ +	ret = i2c_master_recv(client, buf, 2); +	if (ret < 0) { +		dev_err(&client->dev, "error reading header: %d\n", ret); +		goto unlock; +	} + +	if (buf[PAYLOAD_HEADER] != FRAME_START) { +		dev_err(&client->dev, "invalid frame start: %d\n", buf[0]); +		ret = -EIO; +		goto unlock; +	} + +	if (buf[PAYLOAD_LENGTH] == 0) { +		dev_err(&client->dev, "invalid payload length: %d\n", +			buf[PAYLOAD_LENGTH]); +		ret = -EIO; +		goto unlock; +	} + +	/* read the message */ +	ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]); +	if (ret < 0) { +		dev_err(&client->dev, "error reading payload: %d\n", ret); +		goto unlock; +	} + +	dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n", +		buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]); + +unlock: +	mutex_unlock(&ts->access_mutex); +	return ret; +} + +static void zforce_complete(struct zforce_ts *ts, int cmd, int result) +{ +	struct i2c_client *client = ts->client; + +	if (ts->command_waiting == cmd) { +		dev_dbg(&client->dev, "completing command 0x%x\n", cmd); +		ts->command_result = result; +		complete(&ts->command_done); +	} else { +		dev_dbg(&client->dev, "command %d not for us\n", cmd); +	} +} + +static irqreturn_t zforce_irq(int irq, void *dev_id) +{ +	struct zforce_ts *ts = dev_id; +	struct i2c_client *client = ts->client; + +	if (ts->suspended && device_may_wakeup(&client->dev)) +		pm_wakeup_event(&client->dev, 500); + +	return IRQ_WAKE_THREAD; +} + +static irqreturn_t zforce_irq_thread(int irq, void *dev_id) +{ +	struct zforce_ts *ts = dev_id; +	struct i2c_client *client = ts->client; +	const struct zforce_ts_platdata *pdata = ts->pdata; +	int ret; +	u8 payload_buffer[FRAME_MAXSIZE]; +	u8 *payload; + +	/* +	 * When still suspended, return. +	 * Due to the level-interrupt we will get re-triggered later. +	 */ +	if (ts->suspended) { +		msleep(20); +		return IRQ_HANDLED; +	} + +	dev_dbg(&client->dev, "handling interrupt\n"); + +	/* Don't emit wakeup events from commands run by zforce_suspend */ +	if (!ts->suspending && device_may_wakeup(&client->dev)) +		pm_stay_awake(&client->dev); + +	while (!gpio_get_value(pdata->gpio_int)) { +		ret = zforce_read_packet(ts, payload_buffer); +		if (ret < 0) { +			dev_err(&client->dev, +				"could not read packet, ret: %d\n", ret); +			break; +		} + +		payload =  &payload_buffer[PAYLOAD_BODY]; + +		switch (payload[RESPONSE_ID]) { +		case NOTIFICATION_TOUCH: +			/* +			 * Always report touch-events received while +			 * suspending, when being a wakeup source +			 */ +			if (ts->suspending && device_may_wakeup(&client->dev)) +				pm_wakeup_event(&client->dev, 500); +			zforce_touch_event(ts, &payload[RESPONSE_DATA]); +			break; + +		case NOTIFICATION_BOOTCOMPLETE: +			ts->boot_complete = payload[RESPONSE_DATA]; +			zforce_complete(ts, payload[RESPONSE_ID], 0); +			break; + +		case RESPONSE_INITIALIZE: +		case RESPONSE_DEACTIVATE: +		case RESPONSE_SETCONFIG: +		case RESPONSE_RESOLUTION: +		case RESPONSE_SCANFREQ: +			zforce_complete(ts, payload[RESPONSE_ID], +					payload[RESPONSE_DATA]); +			break; + +		case RESPONSE_STATUS: +			/* +			 * Version Payload Results +			 * [2:major] [2:minor] [2:build] [2:rev] +			 */ +			ts->version_major = (payload[RESPONSE_DATA + 1] << 8) | +						payload[RESPONSE_DATA]; +			ts->version_minor = (payload[RESPONSE_DATA + 3] << 8) | +						payload[RESPONSE_DATA + 2]; +			ts->version_build = (payload[RESPONSE_DATA + 5] << 8) | +						payload[RESPONSE_DATA + 4]; +			ts->version_rev   = (payload[RESPONSE_DATA + 7] << 8) | +						payload[RESPONSE_DATA + 6]; +			dev_dbg(&ts->client->dev, +				"Firmware Version %04x:%04x %04x:%04x\n", +				ts->version_major, ts->version_minor, +				ts->version_build, ts->version_rev); + +			zforce_complete(ts, payload[RESPONSE_ID], 0); +			break; + +		case NOTIFICATION_INVALID_COMMAND: +			dev_err(&ts->client->dev, "invalid command: 0x%x\n", +				payload[RESPONSE_DATA]); +			break; + +		default: +			dev_err(&ts->client->dev, +				"unrecognized response id: 0x%x\n", +				payload[RESPONSE_ID]); +			break; +		} +	} + +	if (!ts->suspending && device_may_wakeup(&client->dev)) +		pm_relax(&client->dev); + +	dev_dbg(&client->dev, "finished interrupt\n"); + +	return IRQ_HANDLED; +} + +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; +} + +static void zforce_input_close(struct input_dev *dev) +{ +	struct zforce_ts *ts = input_get_drvdata(dev); +	struct i2c_client *client = ts->client; +	int ret; + +	ret = zforce_stop(ts); +	if (ret) +		dev_warn(&client->dev, "stopping zforce failed\n"); + +	return; +} + +#ifdef CONFIG_PM_SLEEP +static int zforce_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct zforce_ts *ts = i2c_get_clientdata(client); +	struct input_dev *input = ts->input; +	int ret = 0; + +	mutex_lock(&input->mutex); +	ts->suspending = true; + +	/* +	 * When configured as a wakeup source device should always wake +	 * the system, therefore start device if necessary. +	 */ +	if (device_may_wakeup(&client->dev)) { +		dev_dbg(&client->dev, "suspend while being a wakeup source\n"); + +		/* Need to start device, if not open, to be a wakeup source. */ +		if (!input->users) { +			ret = zforce_start(ts); +			if (ret) +				goto unlock; +		} + +		enable_irq_wake(client->irq); +	} else if (input->users) { +		dev_dbg(&client->dev, +			"suspend without being a wakeup source\n"); + +		ret = zforce_stop(ts); +		if (ret) +			goto unlock; + +		disable_irq(client->irq); +	} + +	ts->suspended = true; + +unlock: +	ts->suspending = false; +	mutex_unlock(&input->mutex); + +	return ret; +} + +static int zforce_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct zforce_ts *ts = i2c_get_clientdata(client); +	struct input_dev *input = ts->input; +	int ret = 0; + +	mutex_lock(&input->mutex); + +	ts->suspended = false; + +	if (device_may_wakeup(&client->dev)) { +		dev_dbg(&client->dev, "resume from being a wakeup source\n"); + +		disable_irq_wake(client->irq); + +		/* need to stop device if it was not open on suspend */ +		if (!input->users) { +			ret = zforce_stop(ts); +			if (ret) +				goto unlock; +		} +	} else if (input->users) { +		dev_dbg(&client->dev, "resume without being a wakeup source\n"); + +		enable_irq(client->irq); + +		ret = zforce_start(ts); +		if (ret < 0) +			goto unlock; +	} + +unlock: +	mutex_unlock(&input->mutex); + +	return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume); + +static void zforce_reset(void *data) +{ +	struct zforce_ts *ts = data; + +	gpio_set_value(ts->pdata->gpio_rst, 0); +} + +static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev) +{ +	struct zforce_ts_platdata *pdata; +	struct device_node *np = dev->of_node; + +	if (!np) +		return ERR_PTR(-ENOENT); + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) { +		dev_err(dev, "failed to allocate platform data\n"); +		return ERR_PTR(-ENOMEM); +	} + +	pdata->gpio_int = of_get_gpio(np, 0); +	if (!gpio_is_valid(pdata->gpio_int)) { +		dev_err(dev, "failed to get interrupt gpio\n"); +		return ERR_PTR(-EINVAL); +	} + +	pdata->gpio_rst = of_get_gpio(np, 1); +	if (!gpio_is_valid(pdata->gpio_rst)) { +		dev_err(dev, "failed to get reset gpio\n"); +		return ERR_PTR(-EINVAL); +	} + +	if (of_property_read_u32(np, "x-size", &pdata->x_max)) { +		dev_err(dev, "failed to get x-size property\n"); +		return ERR_PTR(-EINVAL); +	} + +	if (of_property_read_u32(np, "y-size", &pdata->y_max)) { +		dev_err(dev, "failed to get y-size property\n"); +		return ERR_PTR(-EINVAL); +	} + +	return pdata; +} + +static int zforce_probe(struct i2c_client *client, +			const struct i2c_device_id *id) +{ +	const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); +	struct zforce_ts *ts; +	struct input_dev *input_dev; +	int ret; + +	if (!pdata) { +		pdata = zforce_parse_dt(&client->dev); +		if (IS_ERR(pdata)) +			return PTR_ERR(pdata); +	} + +	ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL); +	if (!ts) +		return -ENOMEM; + +	ret = devm_gpio_request_one(&client->dev, pdata->gpio_int, GPIOF_IN, +				    "zforce_ts_int"); +	if (ret) { +		dev_err(&client->dev, "request of gpio %d failed, %d\n", +			pdata->gpio_int, ret); +		return ret; +	} + +	ret = devm_gpio_request_one(&client->dev, pdata->gpio_rst, +				    GPIOF_OUT_INIT_LOW, "zforce_ts_rst"); +	if (ret) { +		dev_err(&client->dev, "request of gpio %d failed, %d\n", +			pdata->gpio_rst, ret); +		return ret; +	} + +	ret = devm_add_action(&client->dev, zforce_reset, ts); +	if (ret) { +		dev_err(&client->dev, "failed to register reset action, %d\n", +			ret); +		return ret; +	} + +	snprintf(ts->phys, sizeof(ts->phys), +		 "%s/input0", dev_name(&client->dev)); + +	input_dev = devm_input_allocate_device(&client->dev); +	if (!input_dev) { +		dev_err(&client->dev, "could not allocate input device\n"); +		return -ENOMEM; +	} + +	mutex_init(&ts->access_mutex); +	mutex_init(&ts->command_mutex); + +	ts->pdata = pdata; +	ts->client = client; +	ts->input = input_dev; + +	input_dev->name = "Neonode zForce touchscreen"; +	input_dev->phys = ts->phys; +	input_dev->id.bustype = BUS_I2C; + +	input_dev->open = zforce_input_open; +	input_dev->close = zforce_input_close; + +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(EV_SYN, input_dev->evbit); +	__set_bit(EV_ABS, input_dev->evbit); + +	/* For multi touch */ +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, +			     pdata->x_max, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, +			     pdata->y_max, 0, 0); + +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, +			     ZFORCE_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, +			     ZFORCE_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); +	input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS, INPUT_MT_DIRECT); + +	input_set_drvdata(ts->input, ts); + +	init_completion(&ts->command_done); + +	/* +	 * The zforce pulls the interrupt low when it has data ready. +	 * After it is triggered the isr thread runs until all the available +	 * packets have been read and the interrupt is high again. +	 * Therefore we can trigger the interrupt anytime it is low and do +	 * not need to limit it to the interrupt edge. +	 */ +	ret = devm_request_threaded_irq(&client->dev, client->irq, +					zforce_irq, zforce_irq_thread, +					IRQF_TRIGGER_LOW | IRQF_ONESHOT, +					input_dev->name, ts); +	if (ret) { +		dev_err(&client->dev, "irq %d request failed\n", client->irq); +		return ret; +	} + +	i2c_set_clientdata(client, ts); + +	/* let the controller boot */ +	gpio_set_value(pdata->gpio_rst, 1); + +	ts->command_waiting = NOTIFICATION_BOOTCOMPLETE; +	if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) +		dev_warn(&client->dev, "bootcomplete timed out\n"); + +	/* need to start device to get version information */ +	ret = zforce_command_wait(ts, COMMAND_INITIALIZE); +	if (ret) { +		dev_err(&client->dev, "unable to initialize, %d\n", ret); +		return ret; +	} + +	/* this gets the firmware version among other information */ +	ret = zforce_command_wait(ts, COMMAND_STATUS); +	if (ret < 0) { +		dev_err(&client->dev, "couldn't get status, %d\n", ret); +		zforce_stop(ts); +		return ret; +	} + +	/* stop device and put it into sleep until it is opened */ +	ret = zforce_stop(ts); +	if (ret < 0) +		return ret; + +	device_set_wakeup_capable(&client->dev, true); + +	ret = input_register_device(input_dev); +	if (ret) { +		dev_err(&client->dev, "could not register input device, %d\n", +			ret); +		return ret; +	} + +	return 0; +} + +static struct i2c_device_id zforce_idtable[] = { +	{ "zforce-ts", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, zforce_idtable); + +#ifdef CONFIG_OF +static const struct of_device_id zforce_dt_idtable[] = { +	{ .compatible = "neonode,zforce" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, zforce_dt_idtable); +#endif + +static struct i2c_driver zforce_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "zforce-ts", +		.pm	= &zforce_pm_ops, +		.of_match_table	= of_match_ptr(zforce_dt_idtable), +	}, +	.probe		= zforce_probe, +	.id_table	= zforce_idtable, +}; + +module_i2c_driver(zforce_driver); + +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_DESCRIPTION("zForce TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c index 04884986764..e2ccd683de6 100644 --- a/drivers/input/touchscreen/zylonite-wm97xx.c +++ b/drivers/input/touchscreen/zylonite-wm97xx.c @@ -20,8 +20,8 @@  #include <linux/module.h>  #include <linux/moduleparam.h>  #include <linux/kernel.h> -#include <linux/init.h>  #include <linux/delay.h> +#include <linux/gpio.h>  #include <linux/irq.h>  #include <linux/interrupt.h>  #include <linux/io.h> @@ -122,9 +122,9 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)  			x, y, p);  		/* are samples valid */ -		if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X || -		    (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y || -		    (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES) +		if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X || +		    (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y || +		    (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)  			goto up;  		/* coordinate is good */ @@ -192,8 +192,8 @@ static int zylonite_wm97xx_probe(struct platform_device *pdev)  	else  		gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26); -	wm->pen_irq = IRQ_GPIO(gpio_touch_irq); -	set_irq_type(IRQ_GPIO(gpio_touch_irq), IRQ_TYPE_EDGE_BOTH); +	wm->pen_irq = gpio_to_irq(gpio_touch_irq); +	irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);  	wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,  			   WM97XX_GPIO_POL_HIGH, @@ -223,19 +223,7 @@ static struct platform_driver zylonite_wm97xx_driver = {  		.name	= "wm97xx-touch",  	},  }; - -static int __init zylonite_wm97xx_init(void) -{ -	return platform_driver_register(&zylonite_wm97xx_driver); -} - -static void __exit zylonite_wm97xx_exit(void) -{ -	platform_driver_unregister(&zylonite_wm97xx_driver); -} - -module_init(zylonite_wm97xx_init); -module_exit(zylonite_wm97xx_exit); +module_platform_driver(zylonite_wm97xx_driver);  /* Module information */  MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");  | 
