diff options
Diffstat (limited to 'drivers/input/touchscreen')
80 files changed, 16019 insertions, 4484 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 61834ae282e..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 @@ -598,9 +771,14 @@ config TOUCHSCREEN_USB_JASTEC 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 @@ -618,6 +796,14 @@ config TOUCHSCREEN_USB_NEXIO 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" select SERIO @@ -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. @@ -681,9 +890,32 @@ 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. @@ -693,4 +925,17 @@ config TOUCHSCREEN_TPS6507X 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 718bcc81495..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,17 +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 @@ -57,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 4e4e58cec6c..dcf39077154 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -16,30 +16,6 @@ #define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */ -#ifdef CONFIG_PM -static int ad7879_i2c_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ad7879 *ts = i2c_get_clientdata(client); - - ad7879_suspend(ts); - - return 0; -} - -static int ad7879_i2c_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ad7879 *ts = i2c_get_clientdata(client); - - ad7879_resume(ts); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume); -#endif - /* All registers are word-sized. * AD7879 uses a high-byte first convention. */ @@ -47,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, @@ -68,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 = { @@ -78,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; @@ -99,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); @@ -119,28 +95,15 @@ static struct i2c_driver ad7879_i2c_driver = { .driver = { .name = "ad7879", .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &ad7879_i2c_pm, -#endif + .pm = &ad7879_pm_ops, }, .probe = ad7879_i2c_probe, - .remove = __devexit_p(ad7879_i2c_remove), + .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 4bf2316e328..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; - - 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_device_register_with_groups(&spi->dev, spi->modalias, + ts, ads7846_attr_groups); + if (IS_ERR(ts->hwmon)) + return PTR_ERR(ts->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,10 +927,14 @@ 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; /* @@ -951,15 +947,20 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784 ts->get_pendown_state = pdata->get_pendown_state; } else if (gpio_is_valid(pdata->gpio_pendown)) { - err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); + err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN, + "ads7846_pendown"); if (err) { - dev_err(&spi->dev, "failed to request pendown GPIO%d\n", - pdata->gpio_pendown); + dev_err(&spi->dev, + "failed to request/setup pendown GPIO%d: %d\n", + pdata->gpio_pendown, err); return err; } 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; @@ -972,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; @@ -1171,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. */ @@ -1215,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); @@ -1251,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; @@ -1328,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) @@ -1341,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: @@ -1366,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); @@ -1406,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 1507ce108d5..b9b5ddad665 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -13,6 +13,10 @@ #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 @@ -147,11 +151,12 @@ 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; - unsigned int intr_pin; struct regulator *regulator; + unsigned int irq; + unsigned int intr_pin; + bool touch_stopped; }; /** @@ -261,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)); @@ -417,10 +422,72 @@ 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 * @client: i2c client structure pointer * @id: i2c device id pointer @@ -428,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, @@ -444,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; } @@ -459,8 +532,9 @@ 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, "V-TOUCH"); + 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); @@ -477,12 +551,11 @@ static int __devinit bu21013_probe(struct i2c_client *client, 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_disable_regulator; - } + 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 */ @@ -507,11 +580,13 @@ static int __devinit bu21013_probe(struct i2c_client *client, 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; } @@ -529,7 +604,7 @@ 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: @@ -547,13 +622,13 @@ 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); @@ -582,9 +657,9 @@ 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); @@ -619,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; } @@ -647,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 a93c5c26ab3..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,7 +291,7 @@ err_free_mem: return err; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int cy8ctmg110_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -307,11 +319,11 @@ static int cy8ctmg110_resume(struct device *dev) } return 0; } +#endif static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume); -#endif -static int __devexit cy8ctmg110_remove(struct i2c_client *client) +static int cy8ctmg110_remove(struct i2c_client *client) { struct cy8ctmg110 *ts = i2c_get_clientdata(client); @@ -328,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 }, { } }; @@ -339,27 +351,14 @@ static struct i2c_driver cy8ctmg110_driver = { .driver = { .owner = THIS_MODULE, .name = CY8CTMG110_DRIVER_NAME, -#ifdef CONFIG_PM .pm = &cy8ctmg110_pm, -#endif }, .id_table = cy8ctmg110_idtable, .probe = cy8ctmg110_probe, - .remove = __devexit_p(cy8ctmg110_remove), + .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 7f8f538a980..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,7 +264,7 @@ static int __devexit eeti_ts_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int eeti_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -298,9 +302,9 @@ static int eeti_ts_resume(struct device *dev) return 0; } +#endif static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume); -#endif static const struct i2c_device_id eeti_ts_id[] = { { "eeti_ts", 0 }, @@ -311,29 +315,15 @@ MODULE_DEVICE_TABLE(i2c, eeti_ts_id); static struct i2c_driver eeti_ts_driver = { .driver = { .name = "eeti_ts", -#ifdef CONFIG_PM .pm = &eeti_ts_pm, -#endif }, .probe = eeti_ts_probe, - .remove = __devexit_p(eeti_ts_remove), + .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 2d84c80ceb6..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,44 +222,30 @@ 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 @@ -275,14 +263,15 @@ 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; } +#endif static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); -#endif static const struct i2c_device_id mcs5000_ts_id[] = { { "mcs5000_ts", 0 }, @@ -292,28 +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), .driver = { .name = "mcs5000_ts", -#ifdef CONFIG_PM .pm = &mcs5000_ts_pm, -#endif }, .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 5803bd0c1cc..c038db93e2c 100644 --- a/drivers/input/touchscreen/migor_ts.c +++ b/drivers/input/touchscreen/migor_ts.c @@ -36,7 +36,6 @@ struct migor_ts_priv { struct i2c_client *client; struct input_dev *input; - struct delayed_work work; int irq; }; @@ -44,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 */ @@ -72,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); @@ -131,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)); @@ -154,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); @@ -184,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); @@ -230,7 +205,7 @@ static int migor_ts_remove(struct i2c_client *client) static int migor_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct migor_ts_priv *priv = dev_get_drvdata(&client->dev); + struct migor_ts_priv *priv = i2c_get_clientdata(client); if (device_may_wakeup(&client->dev)) enable_irq_wake(priv->irq); @@ -241,7 +216,7 @@ static int migor_ts_suspend(struct device *dev) static int migor_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct migor_ts_priv *priv = dev_get_drvdata(&client->dev); + struct migor_ts_priv *priv = i2c_get_clientdata(client); if (device_may_wakeup(&client->dev)) disable_irq_wake(priv->irq); @@ -267,19 +242,8 @@ static struct i2c_driver migor_ts_driver = { .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 4dcb0e872f6..00000000000 --- a/drivers/input/touchscreen/qt602240_ts.c +++ /dev/null @@ -1,1406 +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_object), - 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 device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - 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 device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - 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; -} - -static const struct dev_pm_ops qt602240_pm_ops = { - .suspend = qt602240_suspend, - .resume = qt602240_resume, -}; -#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, -#ifdef CONFIG_PM - .pm = &qt602240_pm_ops, -#endif - }, - .probe = qt602240_probe, - .remove = __devexit_p(qt602240_remove), - .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 index 4ab371358b3..3c0f57efe7b 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -19,12 +19,17 @@ */ #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" @@ -46,6 +51,8 @@ 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) @@ -118,8 +125,18 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) } /* SYN_MT_REPORT only if no contact */ - if (!count) + 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); @@ -128,10 +145,17 @@ end: return IRQ_HANDLED; } -static int __devinit st1232_ts_probe(struct i2c_client *client, +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; @@ -145,17 +169,36 @@ static int __devinit st1232_ts_probe(struct i2c_client *client, return -EINVAL; } + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; - ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - error = -ENOMEM; - goto err_free_mem; - } + 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; @@ -168,54 +211,50 @@ static int __devinit st1232_ts_probe(struct i2c_client *client, 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 = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler, - IRQF_ONESHOT, client->name, ts); + 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"); - goto err_free_mem; + 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); - goto err_free_irq; + return error; } i2c_set_clientdata(client, ts); device_init_wakeup(&client->dev, 1); return 0; - -err_free_irq: - free_irq(client->irq, ts); -err_free_mem: - input_free_device(input_dev); - kfree(ts); - return error; } -static int __devexit st1232_ts_remove(struct i2c_client *client) +static int st1232_ts_remove(struct i2c_client *client) { struct st1232_ts_data *ts = i2c_get_clientdata(client); device_init_wakeup(&client->dev, 0); - free_irq(client->irq, ts); - input_unregister_device(ts->input_dev); - kfree(ts); + st1232_ts_power(ts, false); return 0; } -#ifdef CONFIG_PM +#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)) + if (device_may_wakeup(&client->dev)) { enable_irq_wake(client->irq); - else + } else { disable_irq(client->irq); + st1232_ts_power(ts, false); + } return 0; } @@ -223,51 +262,50 @@ static int st1232_ts_suspend(struct device *dev) 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)) + if (device_may_wakeup(&client->dev)) { disable_irq_wake(client->irq); - else + } else { + st1232_ts_power(ts, true); enable_irq(client->irq); + } return 0; } -static const struct dev_pm_ops st1232_ts_pm_ops = { - .suspend = st1232_ts_suspend, - .resume = st1232_ts_resume, -}; #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 = __devexit_p(st1232_ts_remove), + .remove = st1232_ts_remove, .id_table = st1232_ts_id, .driver = { .name = ST1232_TS_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_PM + .of_match_table = of_match_ptr(st1232_ts_dt_ids), .pm = &st1232_ts_pm_ops, -#endif }, }; -static int __init st1232_ts_init(void) -{ - return i2c_add_driver(&st1232_ts_driver); -} -module_init(st1232_ts_init); - -static void __exit st1232_ts_exit(void) -{ - i2c_del_driver(&st1232_ts_driver); -} -module_exit(st1232_ts_exit); +module_i2c_driver(st1232_ts_driver); MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>"); MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); 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 22a3411e93c..00000000000 --- a/drivers/input/touchscreen/tnetv107x-ts.c +++ /dev/null @@ -1,397 +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/err.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 (IS_ERR(ts->clk)) { - dev_err(dev, "cannot claim device clock\n"); - error = PTR_ERR(ts->clk); - 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 73fd6642b68..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 @@ -239,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 + {} }; @@ -257,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; } @@ -291,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) @@ -320,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 @@ -368,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); @@ -379,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) @@ -680,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; } @@ -755,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; } @@ -945,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 @@ -953,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, @@ -963,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 @@ -1139,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 }; @@ -1250,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) { @@ -1258,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; } @@ -1281,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) @@ -1357,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; } } @@ -1375,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); } @@ -1420,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; @@ -1433,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; } @@ -1479,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; @@ -1495,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; } } @@ -1504,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; } @@ -1523,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; } } @@ -1551,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); @@ -1580,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 c14412ef464..2792ca397dd 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -18,7 +18,6 @@ #include <linux/slab.h> #include <linux/input/mt.h> #include <linux/serio.h> -#include <linux/init.h> #include <linux/ctype.h> #include <linux/delay.h> @@ -367,6 +366,20 @@ 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; @@ -383,6 +396,8 @@ static int w8001_setup(struct w8001 *w8001) dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); + __set_bit(INPUT_PROP_DIRECT, dev->propbit); + /* penabled? */ error = w8001_command(w8001, W8001_CMD_QUERY, true); if (!error) { @@ -455,7 +470,7 @@ static int w8001_setup(struct w8001 *w8001) case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - input_mt_init_slots(dev, 2); + 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, @@ -474,7 +489,7 @@ static int w8001_setup(struct w8001 *w8001) strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); - return w8001_command(w8001, W8001_CMD_START, false); + return 0; } /* @@ -485,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); } /* @@ -534,6 +549,11 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) 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; @@ -573,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>"); |
