diff options
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 45 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/ad7877.c | 19 | ||||
-rw-r--r-- | drivers/input/touchscreen/ad7879-spi.c | 17 | ||||
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 16 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 1211 | ||||
-rw-r--r-- | drivers/input/touchscreen/qt602240_ts.c | 1406 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2005.c | 756 | ||||
-rw-r--r-- | drivers/input/touchscreen/wm831x-ts.c | 368 |
9 files changed, 2399 insertions, 1443 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 61834ae282e1..112ec55f2939 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI To compile this driver as a module, choose M here: the module will be called ad7879-spi. +config TOUCHSCREEN_ATMEL_MXT + tristate "Atmel mXT I2C Touchscreen" + depends on I2C + 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_BITSY tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" depends on SA1100_BITSY @@ -339,18 +351,6 @@ 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" - depends on I2C - help - Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen - 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. - config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on SH_MIGOR && I2C @@ -423,6 +423,16 @@ config TOUCHSCREEN_UCB1400 To compile this driver as a module, choose M here: the module will be called ucb1400_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 @@ -629,6 +639,17 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +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 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 718bcc814952..ca94098d4c92 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,6 +12,7 @@ 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_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o @@ -37,7 +38,6 @@ 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_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o @@ -45,9 +45,11 @@ obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.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_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 diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index a1952fcc083e..714d4e0f9f95 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -41,6 +41,7 @@ #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> @@ -826,39 +827,37 @@ static int __devexit ad7877_remove(struct spi_device *spi) 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, }; static int __init ad7877_init(void) diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 59c6e68c4325..ddf732f3cafc 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -7,6 +7,7 @@ */ #include <linux/input.h> /* BUS_SPI */ +#include <linux/pm.h> #include <linux/spi/spi.h> #include "ad7879.h" @@ -20,9 +21,10 @@ #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) +#ifdef CONFIG_PM_SLEEP +static int ad7879_spi_suspend(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct ad7879 *ts = spi_get_drvdata(spi); ad7879_suspend(ts); @@ -30,19 +32,19 @@ static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) return 0; } -static int ad7879_spi_resume(struct spi_device *spi) +static int ad7879_spi_resume(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct ad7879 *ts = spi_get_drvdata(spi); ad7879_resume(ts); return 0; } -#else -# define ad7879_spi_suspend NULL -# define ad7879_spi_resume NULL #endif +static SIMPLE_DEV_PM_OPS(ad7879_spi_pm, ad7879_spi_suspend, ad7879_spi_resume); + /* * ad7879_read/write are only used for initial setup and for sysfs controls. * The main traffic is done in ad7879_collect(). @@ -173,11 +175,10 @@ static struct spi_driver ad7879_spi_driver = { .name = "ad7879", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &ad7879_spi_pm, }, .probe = ad7879_spi_probe, .remove = __devexit_p(ad7879_spi_remove), - .suspend = ad7879_spi_suspend, - .resume = ad7879_spi_resume, }; static int __init ad7879_spi_init(void) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 4bf2316e3284..c24946f51256 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -26,6 +26,7 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/pm.h> #include <linux/gpio.h> #include <linux/spi/spi.h> #include <linux/spi/ads7846.h> @@ -892,9 +893,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 +916,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,6 +937,9 @@ static int ads7846_resume(struct spi_device *spi) return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume); static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) { @@ -1408,11 +1413,10 @@ static struct spi_driver ads7846_driver = { .name = "ads7846", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &ads7846_pm, }, .probe = ads7846_probe, .remove = __devexit_p(ads7846_remove), - .suspend = ads7846_suspend, - .resume = ads7846_resume, }; static int __init ads7846_init(void) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c new file mode 100644 index 000000000000..4012436633b1 --- /dev/null +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -0,0 +1,1211 @@ +/* + * Atmel maXTouch 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/atmel_mxt_ts.h> +#include <linux/input.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 + +/* Slave addresses */ +#define MXT_APP_LOW 0x4a +#define MXT_APP_HIGH 0x4b +#define MXT_BOOT_LOW 0x24 +#define MXT_BOOT_HIGH 0x25 + +/* Firmware */ +#define MXT_FW_NAME "maxtouch.fw" + +/* Registers */ +#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 37 +#define MXT_GEN_MESSAGE 5 +#define MXT_GEN_COMMAND 6 +#define MXT_GEN_POWER 7 +#define MXT_GEN_ACQUIRE 8 +#define MXT_TOUCH_MULTI 9 +#define MXT_TOUCH_KEYARRAY 15 +#define MXT_TOUCH_PROXIMITY 23 +#define MXT_PROCI_GRIPFACE 20 +#define MXT_PROCG_NOISE 22 +#define MXT_PROCI_ONETOUCH 24 +#define MXT_PROCI_TWOTOUCH 27 +#define MXT_PROCI_GRIP 40 +#define MXT_PROCI_PALM 41 +#define MXT_SPT_COMMSCONFIG 18 +#define MXT_SPT_GPIOPWM 19 +#define MXT_SPT_SELFTEST 25 +#define MXT_SPT_CTECONFIG 28 +#define MXT_SPT_USERDATA 38 +#define MXT_SPT_DIGITIZER 43 +#define MXT_SPT_MESSAGECOUNT 44 + +/* MXT_GEN_COMMAND 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 + +/* MXT_GEN_POWER field */ +#define MXT_POWER_IDLEACQINT 0 +#define MXT_POWER_ACTVACQINT 1 +#define MXT_POWER_ACTV2IDLETO 2 + +/* MXT_GEN_ACQUIRE 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 field */ +#define MXT_TOUCH_CTRL 0 +#define MXT_TOUCH_XORIGIN 1 +#define MXT_TOUCH_YORIGIN 2 +#define MXT_TOUCH_XSIZE 3 +#define MXT_TOUCH_YSIZE 4 +#define MXT_TOUCH_BLEN 6 +#define MXT_TOUCH_TCHTHR 7 +#define MXT_TOUCH_TCHDI 8 +#define MXT_TOUCH_ORIENT 9 +#define MXT_TOUCH_MOVHYSTI 11 +#define MXT_TOUCH_MOVHYSTN 12 +#define MXT_TOUCH_NUMTOUCH 14 +#define MXT_TOUCH_MRGHYST 15 +#define MXT_TOUCH_MRGTHR 16 +#define MXT_TOUCH_AMPHYST 17 +#define MXT_TOUCH_XRANGE_LSB 18 +#define MXT_TOUCH_XRANGE_MSB 19 +#define MXT_TOUCH_YRANGE_LSB 20 +#define MXT_TOUCH_YRANGE_MSB 21 +#define MXT_TOUCH_XLOCLIP 22 +#define MXT_TOUCH_XHICLIP 23 +#define MXT_TOUCH_YLOCLIP 24 +#define MXT_TOUCH_YHICLIP 25 +#define MXT_TOUCH_XEDGECTRL 26 +#define MXT_TOUCH_XEDGEDIST 27 +#define MXT_TOUCH_YEDGECTRL 28 +#define MXT_TOUCH_YEDGEDIST 29 +#define MXT_TOUCH_JUMPLIMIT 30 + +/* MXT_PROCI_GRIPFACE 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 */ +#define MXT_COMMS_CTRL 0 +#define MXT_COMMS_CMD 1 + +/* MXT_SPT_CTECONFIG 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 */ +#define MXT_BOOT_VALUE 0xa5 +#define MXT_BACKUP_VALUE 0x55 +#define MXT_BACKUP_TIME 25 /* msec */ +#define MXT_RESET_TIME 65 /* msec */ + +#define MXT_FWRESET_TIME 175 /* 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 + +/* Touch status */ +#define MXT_SUPPRESS (1 << 1) +#define MXT_AMP (1 << 2) +#define MXT_VECTOR (1 << 3) +#define MXT_MOVE (1 << 4) +#define MXT_RELEASE (1 << 5) +#define MXT_PRESS (1 << 6) +#define MXT_DETECT (1 << 7) + +/* Touchscreen absolute values */ +#define MXT_MAX_XC 0x3ff +#define MXT_MAX_YC 0x3ff +#define MXT_MAX_AREA 0xff + +#define MXT_MAX_FINGER 10 + +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; + u8 instances; + u8 num_report_ids; + + /* to map object and message */ + u8 max_reportid; +}; + +struct mxt_message { + u8 reportid; + u8 message[7]; + u8 checksum; +}; + +struct mxt_finger { + int status; + int x; + int y; + int area; +}; + +/* Each client has this additional data */ +struct mxt_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct mxt_platform_data *pdata; + struct mxt_object *object_table; + struct mxt_info info; + struct mxt_finger finger[MXT_MAX_FINGER]; + unsigned int irq; +}; + +static bool mxt_object_readable(unsigned int type) +{ + switch (type) { + case MXT_GEN_MESSAGE: + case MXT_GEN_COMMAND: + case MXT_GEN_POWER: + case MXT_GEN_ACQUIRE: + case MXT_TOUCH_MULTI: + case MXT_TOUCH_KEYARRAY: + case MXT_TOUCH_PROXIMITY: + case MXT_PROCI_GRIPFACE: + case MXT_PROCG_NOISE: + case MXT_PROCI_ONETOUCH: + case MXT_PROCI_TWOTOUCH: + case MXT_PROCI_GRIP: + case MXT_PROCI_PALM: + case MXT_SPT_COMMSCONFIG: + case MXT_SPT_GPIOPWM: + case MXT_SPT_SELFTEST: + case MXT_SPT_CTECONFIG: + case MXT_SPT_USERDATA: + return true; + default: + return false; + } +} + +static bool mxt_object_writable(unsigned int type) +{ + switch (type) { + case MXT_GEN_COMMAND: + case MXT_GEN_POWER: + case MXT_GEN_ACQUIRE: + case MXT_TOUCH_MULTI: + case MXT_TOUCH_KEYARRAY: + case MXT_TOUCH_PROXIMITY: + case MXT_PROCI_GRIPFACE: + case MXT_PROCG_NOISE: + case MXT_PROCI_ONETOUCH: + case MXT_PROCI_TWOTOUCH: + case MXT_PROCI_GRIP: + case MXT_PROCI_PALM: + case MXT_SPT_GPIOPWM: + case MXT_SPT_SELFTEST: + case MXT_SPT_CTECONFIG: + return true; + default: + return false; + } +} + +static void mxt_dump_message(struct device *dev, + struct mxt_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 mxt_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 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; + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(&client->dev, "Unvalid bootloader mode state\n"); + return -EINVAL; + } + + return 0; +} + +static int mxt_unlock_bootloader(struct i2c_client *client) +{ + u8 buf[2]; + + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_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 mxt_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 __mxt_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 mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val) +{ + return __mxt_read_reg(client, reg, 1, val); +} + +static int mxt_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 mxt_read_object_table(struct i2c_client *client, + u16 reg, u8 *object_buf) +{ + return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE, + object_buf); +} + +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\n"); + 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); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __mxt_read_reg(data->client, reg, + sizeof(struct mxt_message), message); +} + +static int mxt_read_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) + return -EINVAL; + + reg = object->start_address; + return __mxt_read_reg(data->client, reg + offset, 1, val); +} + +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) + return -EINVAL; + + reg = object->start_address; + return mxt_write_reg(data->client, reg + offset, val); +} + +static void mxt_input_report(struct mxt_data *data, int single_id) +{ + struct mxt_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 < MXT_MAX_FINGER; id++) { + if (!finger[id].status) + continue; + + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[id].status != MXT_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 == MXT_RELEASE) + finger[id].status = 0; + else + finger_num++; + } + + input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + + if (status != MXT_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 mxt_input_touchevent(struct mxt_data *data, + struct mxt_message *message, int id) +{ + struct mxt_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 & MXT_DETECT)) { + if (status & MXT_RELEASE) { + dev_dbg(dev, "[%d] released\n", id); + + finger[id].status = MXT_RELEASE; + mxt_input_report(data, id); + } + return; + } + + /* Check only AMP detection */ + if (!(status & (MXT_PRESS | MXT_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 & MXT_MOVE ? "moved" : "pressed", + x, y, area); + + finger[id].status = status & MXT_MOVE ? + MXT_MOVE : MXT_PRESS; + finger[id].x = x; + finger[id].y = y; + finger[id].area = area; + + mxt_input_report(data, id); +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + struct mxt_message message; + struct mxt_object *object; + struct device *dev = &data->client->dev; + int id; + u8 reportid; + u8 max_reportid; + u8 min_reportid; + + do { + if (mxt_read_message(data, &message)) { + dev_err(dev, "Failed to read message\n"); + goto end; + } + + reportid = message.reportid; + + /* whether reportid is thing of MXT_TOUCH_MULTI */ + object = mxt_get_object(data, MXT_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) + mxt_input_touchevent(data, &message, id); + else + mxt_dump_message(dev, &message); + } while (reportid != 0xff); + +end: + return IRQ_HANDLED; +} + +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, j, config_offset; + + if (!pdata->config) { + dev_dbg(dev, "No cfg data defined, skipping reg init\n"); + return 0; + } + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + if (!mxt_object_writable(object->type)) + continue; + + for (j = 0; j < object->size + 1; j++) { + config_offset = index + j; + if (config_offset > pdata->config_length) { + dev_err(dev, "Not enough config data!\n"); + return -EINVAL; + } + mxt_write_object(data, object->type, j, + pdata->config[config_offset]); + } + index += object->size + 1; + } + + 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 void mxt_handle_pdata(struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + u8 voltage; + + /* Set touchscreen lines */ + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_XSIZE, + pdata->x_line); + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_YSIZE, + pdata->y_line); + + /* Set touchscreen orient */ + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_ORIENT, + pdata->orient); + + /* Set touchscreen burst length */ + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_BLEN, pdata->blen); + + /* Set touchscreen threshold */ + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_TCHTHR, pdata->threshold); + + /* Set touchscreen resolution */ + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + + /* Set touchscreen voltage */ + if (pdata->voltage) { + if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { + voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / + MXT_VOLTAGE_STEP; + voltage = 0xff - voltage + 1; + } else + voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / + MXT_VOLTAGE_STEP; + + mxt_write_object(data, MXT_SPT_CTECONFIG, + MXT_CTE_VOLTAGE, voltage); + } +} + +static int mxt_get_info(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + struct mxt_info *info = &data->info; + int error; + u8 val; + + error = mxt_read_reg(client, MXT_FAMILY_ID, &val); + if (error) + return error; + info->family_id = val; + + error = mxt_read_reg(client, MXT_VARIANT_ID, &val); + if (error) + return error; + info->variant_id = val; + + error = mxt_read_reg(client, MXT_VERSION, &val); + if (error) + return error; + info->version = val; + + error = mxt_read_reg(client, MXT_BUILD, &val); + if (error) + return error; + info->build = val; + + error = mxt_read_reg(client, MXT_OBJECT_NUM, &val); + if (error) + return error; + info->object_num = val; + + return 0; +} + +static int mxt_get_object_table(struct mxt_data *data) +{ + int error; + int i; + u16 reg; + u8 reportid = 0; + u8 buf[MXT_OBJECT_SIZE]; + + for (i = 0; i < data->info.object_num; i++) { + struct mxt_object *object = data->object_table + i; + + reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i; + error = mxt_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 mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + struct mxt_info *info = &data->info; + int error; + u8 val; + + 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) + return error; + + /* Check register init values */ + error = mxt_check_reg_init(data); + if (error) + return error; + + error = mxt_make_highchg(data); + if (error) + return error; + + mxt_handle_pdata(data); + + /* Backup to memory */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_BACKUPNV, + MXT_BACKUP_VALUE); + msleep(MXT_BACKUP_TIME); + + /* Soft reset */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, 1); + msleep(MXT_RESET_TIME); + + /* Update matrix size at info struct */ + error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); + if (error) + return error; + info->matrix_xsize = val; + + error = mxt_read_reg(client, MXT_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 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 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 (!mxt_object_readable(object->type)) { + count += sprintf(buf + count, "\n"); + continue; + } + + for (j = 0; j < object->size + 1; j++) { + error = mxt_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 mxt_load_fw(struct device *dev, const char *fn) +{ + struct mxt_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 */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, MXT_BOOT_VALUE); + msleep(MXT_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == MXT_APP_LOW) + client->addr = MXT_BOOT_LOW; + else + client->addr = MXT_BOOT_HIGH; + + ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + if (ret) + goto out; + + /* Unlock bootloader */ + mxt_unlock_bootloader(client); + + while (pos < fw->size) { + ret = mxt_check_bootloader(client, + MXT_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 */ + mxt_fw_write(client, fw->data + pos, frame_size); + + ret = mxt_check_bootloader(client, + MXT_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 == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; + else + client->addr = MXT_APP_HIGH; + + 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; + + disable_irq(data->irq); + + error = mxt_load_fw(dev, MXT_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(MXT_FWRESET_TIME); + + kfree(data->object_table); + data->object_table = NULL; + + mxt_initialize(data); + } + + enable_irq(data->irq); + + return count; +} + +static DEVICE_ATTR(object, 0444, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store); + +static struct attribute *mxt_attrs[] = { + &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, MXT_TOUCH_CTRL, 0x83); +} + +static void mxt_stop(struct mxt_data *data) +{ + /* Touch disable */ + mxt_write_object(data, + MXT_TOUCH_MULTI, 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 __devinit mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mxt_platform_data *pdata = client->dev.platform_data; + struct mxt_data *data; + struct input_dev *input_dev; + int error; + + 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"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_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, MXT_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, MXT_MAX_YC, 0, 0); + + /* For multi touch */ + 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, MXT_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, MXT_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + data->client = client; + data->input_dev = input_dev; + data->pdata = pdata; + data->irq = client->irq; + + i2c_set_clientdata(client, data); + + error = mxt_initialize(data); + if (error) + goto err_free_object; + + error = request_threaded_irq(client->irq, NULL, mxt_interrupt, + pdata->irqflags, 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, &mxt_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 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 +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; + + /* Soft reset */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, 1); + + msleep(MXT_RESET_TIME); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops mxt_pm_ops = { + .suspend = mxt_suspend, + .resume = mxt_resume, +}; +#endif + +static const struct i2c_device_id mxt_id[] = { + { "qt602240_ts", 0 }, + { "atmel_mxt_ts", 0 }, + { "mXT224", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mxt_id); + +static struct i2c_driver mxt_driver = { + .driver = { + .name = "atmel_mxt_ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &mxt_pm_ops, +#endif + }, + .probe = mxt_probe, + .remove = __devexit_p(mxt_remove), + .id_table = mxt_id, +}; + +static int __init mxt_init(void) +{ + return i2c_add_driver(&mxt_driver); +} + +static void __exit mxt_exit(void) +{ + i2c_del_driver(&mxt_driver); +} + +module_init(mxt_init); +module_exit(mxt_exit); + +/* 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/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c deleted file mode 100644 index 4dcb0e872f6a..000000000000 --- 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/tsc2005.c b/drivers/input/touchscreen/tsc2005.c new file mode 100644 index 000000000000..87420616efa4 --- /dev/null +++ b/drivers/input/touchscreen/tsc2005.c @@ -0,0 +1,756 @@ +/* + * 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/interrupt.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/spi/spi.h> +#include <linux/spi/tsc2005.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_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; + + 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); +} + +/* 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->last_valid_interrupt = jiffies; + schedule_delayed_work(&ts->esd_work, + round_jiffies(jiffies + + 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 */ + ts->set_reset(false); + usleep_range(100, 500); /* only 10us required */ + ts->set_reset(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 mode_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); + mode_t mode = attr->mode; + + if (attr == &dev_attr_selftest.attr) { + if (!ts->set_reset) + 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; + + mutex_lock(&ts->mutex); + + 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); + + ts->set_reset(false); + usleep_range(100, 500); /* only 10us required */ + ts->set_reset(true); + + enable_irq(ts->spi->irq); + tsc2005_start_scan(ts); + +out: + /* re-arm the watchdog */ + schedule_delayed_work(&ts->esd_work, + round_jiffies(jiffies + + msecs_to_jiffies(ts->esd_timeout))); + mutex_unlock(&ts->mutex); +} + +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 __devinit 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 __devinit tsc2005_probe(struct spi_device *spi) +{ + const struct tsc2005_platform_data *pdata = spi->dev.platform_data; + struct tsc2005 *ts; + struct input_dev *input_dev; + unsigned int max_x, max_y, max_p; + unsigned int fudge_x, fudge_y, fudge_p; + int error; + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data\n"); + return -ENODEV; + } + + fudge_x = pdata->ts_x_fudge ? : 4; + fudge_y = pdata->ts_y_fudge ? : 8; + fudge_p = pdata->ts_pressure_fudge ? : 2; + max_x = pdata->ts_x_max ? : MAX_12BIT; + max_y = pdata->ts_y_max ? : MAX_12BIT; + max_p = pdata->ts_pressure_max ? : MAX_12BIT; + + if (spi->irq <= 0) { + dev_dbg(&spi->dev, "no irq\n"); + return -ENODEV; + } + + 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 = kzalloc(sizeof(*ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->spi = spi; + ts->idev = input_dev; + + ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280; + ts->esd_timeout = pdata->esd_timeout_ms; + 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); + + 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 = request_threaded_irq(spi->irq, NULL, tsc2005_irq_thread, + IRQF_TRIGGER_RISING, "tsc2005", ts); + if (error) { + dev_err(&spi->dev, "Failed to request irq, err: %d\n", error); + goto err_free_mem; + } + + 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 err_clear_drvdata; + } + + 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; + } + + set_irq_wake(spi->irq, 1); + return 0; + +err_remove_sysfs: + sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); +err_clear_drvdata: + spi_set_drvdata(spi, NULL); + free_irq(spi->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return error; +} + +static int __devexit tsc2005_remove(struct spi_device *spi) +{ + struct tsc2005 *ts = spi_get_drvdata(spi); + + sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group); + + free_irq(ts->spi->irq, ts); + input_unregister_device(ts->idev); + kfree(ts); + + spi_set_drvdata(spi, NULL); + 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 = __devexit_p(tsc2005_remove), +}; + +static int __init tsc2005_init(void) +{ + return spi_register_driver(&tsc2005_driver); +} +module_init(tsc2005_init); + +static void __exit tsc2005_exit(void) +{ + spi_unregister_driver(&tsc2005_driver); +} +module_exit(tsc2005_exit); + +MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); +MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c new file mode 100644 index 000000000000..6ae054f8e0aa --- /dev/null +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -0,0 +1,368 @@ +/* + * 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/init.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; +}; + +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) { + 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); + } + + 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; + + /* 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); + + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); + input_sync(wm831x_ts->input_dev); + + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); + + wm831x_ts->pen_down = true; + enable_irq(wm831x_ts->data_irq); + + 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; + + 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, 0); + + if (wm831x_ts->pen_down) + disable_irq(wm831x_ts->data_irq); +} + +static __devinit 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; + + if (core_pdata) + pdata = core_pdata->touch; + + wm831x_ts = kzalloc(sizeof(struct wm831x_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wm831x_ts || !input_dev) { + error = -ENOMEM; + goto err_alloc; + } + + wm831x_ts->wm831x = wm831x; + wm831x_ts->input_dev = input_dev; + + /* + * If we have a direct IRQ use it, otherwise use the interrupt + * from the WM831x IRQ controller. + */ + if (pdata && pdata->data_irq) + wm831x_ts->data_irq = pdata->data_irq; + else + wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA"); + + if (pdata && pdata->pd_irq) + wm831x_ts->pd_irq = pdata->pd_irq; + else + wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD"); + + 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); + + error = request_threaded_irq(wm831x_ts->data_irq, + NULL, wm831x_ts_data_irq, + 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); + + error = request_threaded_irq(wm831x_ts->pd_irq, + NULL, wm831x_ts_pen_down_irq, + 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: + input_free_device(input_dev); + kfree(wm831x_ts); + + return error; +} + +static __devexit 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); + input_unregister_device(wm831x_ts->input_dev); + kfree(wm831x_ts); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver wm831x_ts_driver = { + .driver = { + .name = "wm831x-touch", + .owner = THIS_MODULE, + }, + .probe = wm831x_ts_probe, + .remove = __devexit_p(wm831x_ts_remove), +}; + +static int __init wm831x_ts_init(void) +{ + return platform_driver_register(&wm831x_ts_driver); +} +module_init(wm831x_ts_init); + +static void __exit wm831x_ts_exit(void) +{ + platform_driver_unregister(&wm831x_ts_driver); +} +module_exit(wm831x_ts_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("WM831x PMIC touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-touch"); |