From 4aa5bbeca0fe561181e8fba089cd96e449ee5fca Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 8 Jun 2010 01:01:46 -0700 Subject: Input: usbtouchscreen - reduce number fo be16_to_cpu conversions Let's perform be16_to_cpu() conversions once for each received packet, and then use cached values. Makes code a little bit easier to follow. Tested-by: Ondrej Zary Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 34 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 567d57215c28..5d6bf2a4bbad 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -849,29 +849,32 @@ static void nexio_exit(struct usbtouch_usb *usbtouch) static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) { - int x, y, begin_x, begin_y, end_x, end_y, w, h, ret; struct nexio_touch_packet *packet = (void *) pkt; struct nexio_priv *priv = usbtouch->priv; + unsigned int data_len = be16_to_cpu(packet->data_len); + unsigned int x_len = be16_to_cpu(packet->x_len); + unsigned int y_len = be16_to_cpu(packet->y_len); + int x, y, begin_x, begin_y, end_x, end_y, w, h, ret; /* got touch data? */ if ((pkt[0] & 0xe0) != 0xe0) return 0; - if (be16_to_cpu(packet->data_len) > 0xff) - packet->data_len = cpu_to_be16(be16_to_cpu(packet->data_len) - 0x100); - if (be16_to_cpu(packet->x_len) > 0xff) - packet->x_len = cpu_to_be16(be16_to_cpu(packet->x_len) - 0x80); + if (data_len > 0xff) + data_len -= 0x100; + if (x_len > 0xff) + x_len -= 0x80; /* send ACK */ ret = usb_submit_urb(priv->ack, GFP_ATOMIC); if (!usbtouch->type->max_xc) { - usbtouch->type->max_xc = 2 * be16_to_cpu(packet->x_len); - input_set_abs_params(usbtouch->input, ABS_X, 0, - 2 * be16_to_cpu(packet->x_len), 0, 0); - usbtouch->type->max_yc = 2 * be16_to_cpu(packet->y_len); - input_set_abs_params(usbtouch->input, ABS_Y, 0, - 2 * be16_to_cpu(packet->y_len), 0, 0); + usbtouch->type->max_xc = 2 * x_len; + input_set_abs_params(usbtouch->input, ABS_X, + 0, usbtouch->type->max_xc, 0, 0); + usbtouch->type->max_yc = 2 * y_len; + input_set_abs_params(usbtouch->input, ABS_Y, + 0, usbtouch->type->max_yc, 0, 0); } /* * The device reports state of IR sensors on X and Y axes. @@ -881,22 +884,21 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) * it's disabled (and untested) here as there's no X driver for that. */ begin_x = end_x = begin_y = end_y = -1; - for (x = 0; x < be16_to_cpu(packet->x_len); x++) { + for (x = 0; x < x_len; x++) { if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) { begin_x = x; continue; } if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) { end_x = x - 1; - for (y = be16_to_cpu(packet->x_len); - y < be16_to_cpu(packet->data_len); y++) { + for (y = x_len; y < data_len; y++) { if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) { - begin_y = y - be16_to_cpu(packet->x_len); + begin_y = y - x_len; continue; } if (end_y == -1 && begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) { - end_y = y - 1 - be16_to_cpu(packet->x_len); + end_y = y - 1 - x_len; w = end_x - begin_x; h = end_y - begin_y; #if 0 -- cgit v1.2.3 From 4aaf504670e38da9cb929988cdce05648fa635a7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 22 Jun 2010 11:25:03 -0700 Subject: Input: tps6507x-ts - remove unneeded NULL test Stanse found that tsc is dereferenced earlier than checked for being NULL in tps6507x_ts_remove. Remove the test because there is no way for tsc to be NULL there. Signed-off-by: Jiri Slaby Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tps6507x-ts.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 5b70a1419b4d..a644d18c04dc 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -355,9 +355,6 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev) struct tps6507x_ts *tsc = tps6507x_dev->ts; struct input_dev *input_dev = tsc->input_dev; - if (!tsc) - return 0; - cancel_delayed_work_sync(&tsc->work); destroy_workqueue(tsc->wq); -- cgit v1.2.3 From 7804302b14032d357d889e4a23e463eb6a6c5136 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 28 Jun 2010 01:25:19 -0700 Subject: Input: ads7846 - allow specifying irq trigger type in platform data On some platforms, for example with GPIO interrupts on mpc5121, it is not possible to configure falling edge interrupts. Specifying irq trigger type in platform data structure allows using ads7846 driver on such platforms. Signed-off-by: Anatolij Gustschin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index a9fdf55c0238..69210cb56c54 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1174,7 +1174,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) goto err_put_regulator; } - if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING, + if (!pdata->irq_flags) + pdata->irq_flags = IRQF_TRIGGER_FALLING; + + if (request_irq(spi->irq, ads7846_irq, pdata->irq_flags, spi->dev.driver->name, ts)) { dev_info(&spi->dev, "trying pin change workaround on irq %d\n", spi->irq); -- cgit v1.2.3 From 38771bb440e8c01d07627abc39ac28acbf450cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0tetiar?= Date: Mon, 28 Jun 2010 09:38:48 -0700 Subject: Input: usbtouchscreen - add support for ET&T TC4UM touchscreen controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the ET&T TC4UM 4-wire USB touchscreen controller and tries to reuse the bits for TC5UH controller in kernel already. Data interface is same. Tested-by: Roger Pueyo Centelles Signed-off-by: Petr Štetiar Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 4 ++-- drivers/input/touchscreen/usbtouchscreen.c | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3b9d5e2105d7..e835f04bc0d0 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -540,9 +540,9 @@ config TOUCHSCREEN_USB_ZYTRONIC bool "Zytronic controller" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE -config TOUCHSCREEN_USB_ETT_TC5UH +config TOUCHSCREEN_USB_ETT_TC45USB default y - bool "ET&T TC5UH touchscreen controler support" if EMBEDDED + bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_NEXIO diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 5d6bf2a4bbad..b9cee2738adb 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -135,7 +135,7 @@ enum { DEVTYPE_JASTEC, DEVTYPE_E2I, DEVTYPE_ZYTRONIC, - DEVTYPE_TC5UH, + DEVTYPE_TC45USB, DEVTYPE_NEXIO, }; @@ -222,8 +222,11 @@ static const struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC}, #endif -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH - {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH}, +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB + /* TC5UH */ + {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC45USB}, + /* TC4UM */ + {USB_DEVICE(0x0664, 0x0306), .driver_info = DEVTYPE_TC45USB}, #endif #ifdef CONFIG_TOUCHSCREEN_USB_NEXIO @@ -574,10 +577,10 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) #endif /***************************************************************************** - * ET&T TC5UH part + * ET&T TC5UH/TC4UM part */ -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH -static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB +static int tc45usb_read_data(struct usbtouch_usb *dev, unsigned char *pkt) { dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1]; dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; @@ -1106,14 +1109,14 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { }, #endif -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH - [DEVTYPE_TC5UH] = { +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB + [DEVTYPE_TC45USB] = { .min_xc = 0x0, .max_xc = 0x0fff, .min_yc = 0x0, .max_yc = 0x0fff, .rept_size = 5, - .read_data = tc5uh_read_data, + .read_data = tc45usb_read_data, }, #endif -- cgit v1.2.3 From 7cd7a82d16ad5a711338c1baf2316f24121d93aa Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 30 Jun 2010 01:40:52 -0700 Subject: Input: ad7879 - use threaded IRQ Tested-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 4b32fb4704cd..f947457c858b 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -131,13 +130,12 @@ typedef struct i2c_client bus_device; struct ad7879 { bus_device *bus; struct input_dev *input; - struct work_struct work; struct timer_list timer; #ifdef CONFIG_GPIOLIB struct gpio_chip gc; #endif struct mutex mutex; - unsigned disabled:1; /* P: mutex */ + bool disabled; /* P: mutex */ #if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) struct spi_message msg; @@ -196,16 +194,6 @@ static void ad7879_report(struct ad7879 *ts) } } -static void ad7879_work(struct work_struct *work) -{ - struct ad7879 *ts = container_of(work, struct ad7879, work); - - /* use keventd context to read the result registers */ - ad7879_collect(ts); - ad7879_report(ts); - mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); -} - static void ad7879_ts_event_release(struct ad7879 *ts) { struct input_dev *input_dev = ts->input; @@ -225,13 +213,10 @@ static irqreturn_t ad7879_irq(int irq, void *handle) { struct ad7879 *ts = handle; - /* The repeated conversion sequencer controlled by TMR kicked off too fast. - * We ignore the last and process the sample sequence currently in the queue. - * It can't be older than 9.4ms - */ + ad7879_collect(ts); + ad7879_report(ts); - if (!work_pending(&ts->work)) - schedule_work(&ts->work); + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); return IRQ_HANDLED; } @@ -249,11 +234,9 @@ static void ad7879_disable(struct ad7879 *ts) if (!ts->disabled) { - ts->disabled = 1; + ts->disabled = true; disable_irq(ts->bus->irq); - cancel_work_sync(&ts->work); - if (del_timer_sync(&ts->timer)) ad7879_ts_event_release(ts); @@ -270,7 +253,7 @@ static void ad7879_enable(struct ad7879 *ts) if (ts->disabled) { ad7879_setup(ts); - ts->disabled = 0; + ts->disabled = false; enable_irq(ts->bus->irq); } @@ -458,7 +441,6 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) ts->input = input_dev; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); - INIT_WORK(&ts->work, ad7879_work); mutex_init(&ts->mutex); ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; @@ -526,9 +508,9 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) ad7879_setup(ts); - err = request_irq(bus->irq, ad7879_irq, - IRQF_TRIGGER_FALLING, bus->dev.driver->name, ts); - + err = request_threaded_irq(bus->irq, NULL, ad7879_irq, + IRQF_TRIGGER_FALLING, + bus->dev.driver->name, ts); if (err) { dev_err(&bus->dev, "irq %d busy?\n", bus->irq); goto err_free_mem; -- cgit v1.2.3 From 4397c98a8a60ba029f2d0051d0cbafe600f05d8c Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 30 Jun 2010 01:40:52 -0700 Subject: Input: ad7879 - split bus logic out The ad7879 driver is using the old bus method of only supporting one at a time (I2C or SPI). So refactor it like the other input drivers that support multiple busses simultaneously. Signed-off-by: Mike Frysinger Signed-off-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 37 ++- drivers/input/touchscreen/Makefile | 2 + drivers/input/touchscreen/ad7879-i2c.c | 140 +++++++++ drivers/input/touchscreen/ad7879-spi.c | 190 +++++++++++++ drivers/input/touchscreen/ad7879.c | 500 +++++++-------------------------- drivers/input/touchscreen/ad7879.h | 30 ++ 6 files changed, 484 insertions(+), 415 deletions(-) create mode 100644 drivers/input/touchscreen/ad7879-i2c.c create mode 100644 drivers/input/touchscreen/ad7879-spi.c create mode 100644 drivers/input/touchscreen/ad7879.h (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index e835f04bc0d0..ff18d896ea6d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -55,37 +55,36 @@ config TOUCHSCREEN_AD7877 To compile this driver as a module, choose M here: the module will be called ad7877. -config TOUCHSCREEN_AD7879_I2C - tristate "AD7879 based touchscreens: AD7879-1 I2C Interface" - depends on I2C - select TOUCHSCREEN_AD7879 +config TOUCHSCREEN_AD7879 + tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface" help - Say Y here if you have a touchscreen interface using the - AD7879-1/AD7889-1 controller, and your board-specific - initialization code includes that in its table of I2C devices. + Say Y here if you want to support a touchscreen interface using + the AD7879-1/AD7889-1 controller. - If unsure, say N (but it's safe to say "Y"). + You should select a bus connection too. To compile this driver as a module, choose M here: the module will be called ad7879. +config TOUCHSCREEN_AD7879_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_AD7879 && I2C + help + Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called ad7879-i2c. + config TOUCHSCREEN_AD7879_SPI - tristate "AD7879 based touchscreens: AD7879 SPI Interface" - depends on SPI_MASTER && TOUCHSCREEN_AD7879_I2C = n - select TOUCHSCREEN_AD7879 + tristate "support SPI bus connection" + depends on TOUCHSCREEN_AD7879 && SPI_MASTER help - Say Y here if you have a touchscreen interface using the - AD7879/AD7889 controller, and your board-specific initialization - code includes that in its table of SPI devices. + Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus. If unsure, say N (but it's safe to say "Y"). To compile this driver as a module, choose M here: the - module will be called ad7879. - -config TOUCHSCREEN_AD7879 - tristate - default n + module will be called ad7879-spi. config TOUCHSCREEN_BITSY tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 497964a7a214..9efdd4424757 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -9,6 +9,8 @@ wm97xx-ts-y := wm97xx-core.o obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c new file mode 100644 index 000000000000..85dfdd27df5a --- /dev/null +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -0,0 +1,140 @@ +/* + * AD7879-1/AD7889-1 touchscreen (I2C bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_I2C */ +#include +#include +#include + +#include "ad7879.h" + +#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */ + +#ifdef CONFIG_PM +static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ + struct ad7879 *ts = i2c_get_clientdata(client); + + ad7879_disable(ts); + + return 0; +} + +static int ad7879_i2c_resume(struct i2c_client *client) +{ + struct ad7879 *ts = i2c_get_clientdata(client); + + ad7879_enable(ts); + + return 0; +} +#else +# define ad7879_i2c_suspend NULL +# define ad7879_i2c_resume NULL +#endif + +/* All registers are word-sized. + * AD7879 uses a high-byte first convention. + */ +static int ad7879_i2c_read(struct device *dev, u8 reg) +{ + struct i2c_client *client = to_i2c_client(dev); + + return swab16(i2c_smbus_read_word_data(client, reg)); +} + +static int ad7879_i2c_multi_read(struct device *dev, + u8 first_reg, u8 count, u16 *buf) +{ + u8 idx; + + for (idx = 0; idx < count; ++idx) + buf[idx] = ad7879_i2c_read(dev, first_reg + idx); + + return 0; +} + +static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_word_data(client, reg, swab16(val)); +} + +static const struct ad7879_bus_ops ad7879_i2c_bus_ops = { + .bustype = BUS_I2C, + .read = ad7879_i2c_read, + .multi_read = ad7879_i2c_multi_read, + .write = ad7879_i2c_write, +}; + +static int __devinit ad7879_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7879 *ts; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, "SMBUS Word Data not Supported\n"); + return -EIO; + } + + ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq, + &ad7879_i2c_bus_ops); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit ad7879_i2c_remove(struct i2c_client *client) +{ + struct ad7879 *ts = i2c_get_clientdata(client); + + ad7879_remove(ts); + + return 0; +} + +static const struct i2c_device_id ad7879_id[] = { + { "ad7879", 0 }, + { "ad7889", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad7879_id); + +static struct i2c_driver ad7879_i2c_driver = { + .driver = { + .name = "ad7879", + .owner = THIS_MODULE, + }, + .probe = ad7879_i2c_probe, + .remove = __devexit_p(ad7879_i2c_remove), + .suspend = ad7879_i2c_suspend, + .resume = ad7879_i2c_resume, + .id_table = ad7879_id, +}; + +static int __init ad7879_i2c_init(void) +{ + return i2c_add_driver(&ad7879_i2c_driver); +} +module_init(ad7879_i2c_init); + +static void __exit ad7879_i2c_exit(void) +{ + i2c_del_driver(&ad7879_i2c_driver); +} +module_exit(ad7879_i2c_exit); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("i2c:ad7879"); diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c new file mode 100644 index 000000000000..5d1e5a050332 --- /dev/null +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -0,0 +1,190 @@ +/* + * AD7879/AD7889 touchscreen (SPI bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include /* BUS_SPI */ +#include + +#include "ad7879.h" + +#define AD7879_DEVID 0x7A /* AD7879/AD7889 */ + +#define MAX_SPI_FREQ_HZ 5000000 +#define AD7879_CMD_MAGIC 0xE000 +#define AD7879_CMD_READ (1 << 10) +#define AD7879_CMD(reg) (AD7879_CMD_MAGIC | ((reg) & 0xF)) +#define AD7879_WRITECMD(reg) (AD7879_CMD(reg)) +#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ) + +#ifdef CONFIG_PM +static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) +{ + struct ad7879 *ts = spi_get_drvdata(spi); + + ad7879_disable(ts); + + return 0; +} + +static int ad7879_spi_resume(struct spi_device *spi) +{ + struct ad7879 *ts = spi_get_drvdata(spi); + + ad7879_enable(ts); + + return 0; +} +#else +# define ad7879_spi_suspend NULL +# define ad7879_spi_resume NULL +#endif + +/* + * ad7879_read/write are only used for initial setup and for sysfs controls. + * The main traffic is done in ad7879_collect(). + */ + +static int ad7879_spi_xfer(struct spi_device *spi, + u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf) +{ + struct spi_message msg; + struct spi_transfer *xfers; + void *spi_data; + u16 *command; + u16 *_rx_buf = _rx_buf; /* shut gcc up */ + u8 idx; + int ret; + + xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL); + if (!spi_data) + return -ENOMEM; + + spi_message_init(&msg); + + command = spi_data; + command[0] = cmd; + if (count == 1) { + /* ad7879_spi_{read,write} gave us buf on stack */ + command[1] = *tx_buf; + tx_buf = &command[1]; + _rx_buf = rx_buf; + rx_buf = &command[2]; + } + + ++xfers; + xfers[0].tx_buf = command; + xfers[0].len = 2; + spi_message_add_tail(&xfers[0], &msg); + ++xfers; + + for (idx = 0; idx < count; ++idx) { + if (rx_buf) + xfers[idx].rx_buf = &rx_buf[idx]; + if (tx_buf) + xfers[idx].tx_buf = &tx_buf[idx]; + xfers[idx].len = 2; + spi_message_add_tail(&xfers[idx], &msg); + } + + ret = spi_sync(spi, &msg); + + if (count == 1) + _rx_buf[0] = command[2]; + + kfree(spi_data); + + return ret; +} + +static int ad7879_spi_multi_read(struct device *dev, + u8 first_reg, u8 count, u16 *buf) +{ + struct spi_device *spi = to_spi_device(dev); + + return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf); +} + +static int ad7879_spi_read(struct device *dev, u8 reg) +{ + struct spi_device *spi = to_spi_device(dev); + u16 ret, dummy; + + return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret; +} + +static int ad7879_spi_write(struct device *dev, u8 reg, u16 val) +{ + struct spi_device *spi = to_spi_device(dev); + u16 dummy; + + return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy); +} + +static const struct ad7879_bus_ops ad7879_spi_bus_ops = { + .bustype = BUS_SPI, + .read = ad7879_spi_read, + .multi_read = ad7879_spi_multi_read, + .write = ad7879_spi_write, +}; + +static int __devinit ad7879_spi_probe(struct spi_device *spi) +{ + struct ad7879 *ts; + + /* don't exceed max specified SPI CLK frequency */ + if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { + dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz); + return -EINVAL; + } + + ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit ad7879_spi_remove(struct spi_device *spi) +{ + struct ad7879 *ts = spi_get_drvdata(spi); + + ad7879_remove(ts); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static struct spi_driver ad7879_spi_driver = { + .driver = { + .name = "ad7879", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .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) +{ + return spi_register_driver(&ad7879_spi_driver); +} +module_init(ad7879_spi_init); + +static void __exit ad7879_spi_exit(void) +{ + spi_unregister_driver(&ad7879_spi_driver); +} +module_exit(ad7879_spi_exit); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index f947457c858b..1de19691c4a3 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -1,25 +1,9 @@ /* - * Copyright (C) 2008-2009 Michael Hennerich, Analog Devices Inc. + * AD7879/AD7889 based touchscreen and GPIO driver * - * Description: AD7879/AD7889 based touchscreen, and GPIO driver - * (I2C/SPI Interface) + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. * * History: * Copyright (c) 2005 David Brownell @@ -49,6 +33,7 @@ #include #include +#include "ad7879.h" #define AD7879_REG_ZEROS 0 #define AD7879_REG_CTRL1 1 @@ -119,29 +104,18 @@ enum { #define MAX_12BIT ((1<<12)-1) #define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50) -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) -#define AD7879_DEVID 0x7A -typedef struct spi_device bus_device; -#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) -#define AD7879_DEVID 0x79 -typedef struct i2c_client bus_device; -#endif - struct ad7879 { - bus_device *bus; + const struct ad7879_bus_ops *bops; + + struct device *dev; struct input_dev *input; struct timer_list timer; #ifdef CONFIG_GPIOLIB struct gpio_chip gc; #endif + unsigned int irq; struct mutex mutex; bool disabled; /* P: mutex */ - -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) - struct spi_message msg; - struct spi_transfer xfer[AD7879_NR_SENSE + 1]; - u16 cmd; -#endif u16 conversion_data[AD7879_NR_SENSE]; char phys[32]; u8 first_conversion_delay; @@ -156,9 +130,20 @@ struct ad7879 { u16 cmd_crtl3; }; -static int ad7879_read(bus_device *, u8); -static int ad7879_write(bus_device *, u8, u16); -static void ad7879_collect(struct ad7879 *); +static int ad7879_read(struct ad7879 *ts, u8 reg) +{ + return ts->bops->read(ts->dev, reg); +} + +static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf) +{ + return ts->bops->multi_read(ts->dev, first_reg, count, buf); +} + +static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) +{ + return ts->bops->write(ts->dev, reg, val); +} static void ad7879_report(struct ad7879 *ts) { @@ -173,12 +158,14 @@ static void ad7879_report(struct ad7879 *ts) /* * The samples processed here are already preprocessed by the AD7879. - * The preprocessing function consists of a median and an averaging filter. - * The combination of these two techniques provides a robust solution, - * discarding the spurious noise in the signal and keeping only the data of interest. - * The size of both filters is programmable. (dev.platform_data, see linux/spi/ad7879.h) - * Other user-programmable conversion controls include variable acquisition time, - * and first conversion delay. Up to 16 averages can be taken per conversion. + * The preprocessing function consists of a median and an averaging + * filter. The combination of these two techniques provides a robust + * solution, discarding the spurious noise in the signal and keeping + * only the data of interest. The size of both filters is + * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other + * user-programmable conversion controls include variable acquisition + * time, and first conversion delay. Up to 16 averages can be taken + * per conversion. */ if (likely(x && z1)) { @@ -213,7 +200,7 @@ static irqreturn_t ad7879_irq(int irq, void *handle) { struct ad7879 *ts = handle; - ad7879_collect(ts); + ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); ad7879_report(ts); mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); @@ -223,42 +210,44 @@ static irqreturn_t ad7879_irq(int irq, void *handle) static void ad7879_setup(struct ad7879 *ts) { - ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); - ad7879_write(ts->bus, AD7879_REG_CTRL3, ts->cmd_crtl3); - ad7879_write(ts->bus, AD7879_REG_CTRL1, ts->cmd_crtl1); + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); + ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); } -static void ad7879_disable(struct ad7879 *ts) +void ad7879_disable(struct ad7879 *ts) { mutex_lock(&ts->mutex); if (!ts->disabled) { ts->disabled = true; - disable_irq(ts->bus->irq); + disable_irq(ts->irq); if (del_timer_sync(&ts->timer)) ad7879_ts_event_release(ts); - ad7879_write(ts->bus, AD7879_REG_CTRL2, + ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN)); } mutex_unlock(&ts->mutex); } +EXPORT_SYMBOL(ad7879_disable); -static void ad7879_enable(struct ad7879 *ts) +void ad7879_enable(struct ad7879 *ts) { mutex_lock(&ts->mutex); if (ts->disabled) { ad7879_setup(ts); ts->disabled = false; - enable_irq(ts->bus->irq); + enable_irq(ts->irq); } mutex_unlock(&ts->mutex); } +EXPORT_SYMBOL(ad7879_enable); static ssize_t ad7879_disable_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -308,7 +297,7 @@ static int ad7879_gpio_direction_input(struct gpio_chip *chip, mutex_lock(&ts->mutex); ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; - err = ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); mutex_unlock(&ts->mutex); return err; @@ -328,7 +317,7 @@ static int ad7879_gpio_direction_output(struct gpio_chip *chip, else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - err = ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); mutex_unlock(&ts->mutex); return err; @@ -340,7 +329,7 @@ static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) u16 val; mutex_lock(&ts->mutex); - val = ad7879_read(ts->bus, AD7879_REG_CTRL2); + val = ad7879_read(ts, AD7879_REG_CTRL2); mutex_unlock(&ts->mutex); return !!(val & AD7879_GPIO_DATA); @@ -357,14 +346,13 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip, else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); mutex_unlock(&ts->mutex); } -static int __devinit ad7879_gpio_add(struct device *dev) +static int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) { - struct ad7879 *ts = dev_get_drvdata(dev); - struct ad7879_platform_data *pdata = dev->platform_data; int ret = 0; if (pdata->gpio_export) { @@ -377,72 +365,78 @@ static int __devinit ad7879_gpio_add(struct device *dev) ts->gc.ngpio = 1; ts->gc.label = "AD7879-GPIO"; ts->gc.owner = THIS_MODULE; - ts->gc.dev = dev; + ts->gc.dev = ts->dev; ret = gpiochip_add(&ts->gc); if (ret) - dev_err(dev, "failed to register gpio %d\n", + dev_err(ts->dev, "failed to register gpio %d\n", ts->gc.base); } return ret; } -/* - * We mark ad7879_gpio_remove inline so there is a chance the code - * gets discarded when not needed. We can't do __devinit/__devexit - * markup since it is used in both probe and remove methods. - */ -static inline void ad7879_gpio_remove(struct device *dev) +static void ad7879_gpio_remove(struct ad7879 *ts) { - struct ad7879 *ts = dev_get_drvdata(dev); - struct ad7879_platform_data *pdata = dev->platform_data; + const struct ad7879_platform_data *pdata = ts->dev->platform_data; int ret; if (pdata->gpio_export) { ret = gpiochip_remove(&ts->gc); if (ret) - dev_err(dev, "failed to remove gpio %d\n", + dev_err(ts->dev, "failed to remove gpio %d\n", ts->gc.base); } } #else -static inline int ad7879_gpio_add(struct device *dev) +static inline int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) { return 0; } -static inline void ad7879_gpio_remove(struct device *dev) +static inline void ad7879_gpio_remove(struct ad7879 *ts) { } #endif -static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, + const struct ad7879_bus_ops *bops) { + struct ad7879_platform_data *pdata = dev->platform_data; + struct ad7879 *ts; struct input_dev *input_dev; - struct ad7879_platform_data *pdata = bus->dev.platform_data; int err; u16 revid; - if (!bus->irq) { - dev_err(&bus->dev, "no IRQ?\n"); - return -ENODEV; + if (!irq) { + dev_err(dev, "no IRQ?\n"); + err = -EINVAL; + goto err_out; } if (!pdata) { - dev_err(&bus->dev, "no platform data?\n"); - return -ENODEV; + dev_err(dev, "no platform data?\n"); + err = -EINVAL; + goto err_out; } + ts = kzalloc(sizeof(*ts), GFP_KERNEL); input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + ts->bops = bops; + ts->dev = dev; ts->input = input_dev; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); mutex_init(&ts->mutex); + ts->irq = irq; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->pressure_max = pdata->pressure_max ? : ~0; @@ -452,11 +446,12 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) ts->pen_down_acc_interval = pdata->pen_down_acc_interval; ts->median = pdata->median; - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&bus->dev)); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); input_dev->name = "AD7879 Touchscreen"; input_dev->phys = ts->phys; - input_dev->dev.parent = &bus->dev; + input_dev->dev.parent = dev; + input_dev->id.bustype = bops->bustype; __set_bit(EV_ABS, input_dev->evbit); __set_bit(ABS_X, input_dev->absbit); @@ -474,17 +469,18 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) input_set_abs_params(input_dev, ABS_PRESSURE, pdata->pressure_min, pdata->pressure_max, 0, 0); - err = ad7879_write(bus, AD7879_REG_CTRL2, AD7879_RESET); - + err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { - dev_err(&bus->dev, "Failed to write %s\n", input_dev->name); + dev_err(dev, "Failed to write %s\n", input_dev->name); goto err_free_mem; } - revid = ad7879_read(bus, AD7879_REG_REVID); - - if ((revid & 0xFF) != AD7879_DEVID) { - dev_err(&bus->dev, "Failed to probe %s\n", input_dev->name); + revid = ad7879_read(ts, AD7879_REG_REVID); + input_dev->id.product = (revid & 0xff); + input_dev->id.version = revid >> 8; + if (input_dev->id.product != devid) { + dev_err(dev, "Failed to probe %s (%x vs %x)\n", + input_dev->name, devid, revid); err = -ENODEV; goto err_free_mem; } @@ -508,19 +504,19 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) ad7879_setup(ts); - err = request_threaded_irq(bus->irq, NULL, ad7879_irq, + err = request_threaded_irq(ts->irq, NULL, ad7879_irq, IRQF_TRIGGER_FALLING, - bus->dev.driver->name, ts); + dev_name(dev), ts); if (err) { - dev_err(&bus->dev, "irq %d busy?\n", bus->irq); + dev_err(dev, "irq %d busy?\n", ts->irq); goto err_free_mem; } - err = sysfs_create_group(&bus->dev.kobj, &ad7879_attr_group); + err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) goto err_free_irq; - err = ad7879_gpio_add(&bus->dev); + err = ad7879_gpio_add(ts, pdata); if (err) goto err_remove_attr; @@ -528,321 +524,33 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) if (err) goto err_remove_gpio; - dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n", - revid >> 8, bus->irq); - - return 0; + return ts; err_remove_gpio: - ad7879_gpio_remove(&bus->dev); + ad7879_gpio_remove(ts); err_remove_attr: - sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group); + sysfs_remove_group(&dev->kobj, &ad7879_attr_group); err_free_irq: - free_irq(bus->irq, ts); + free_irq(ts->irq, ts); err_free_mem: input_free_device(input_dev); - - return err; + kfree(ts); +err_out: + return ERR_PTR(err); } +EXPORT_SYMBOL(ad7879_probe); -static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts) +void ad7879_remove(struct ad7879 *ts) { - ad7879_gpio_remove(&bus->dev); + ad7879_gpio_remove(ts); ad7879_disable(ts); - sysfs_remove_group(&ts->bus->dev.kobj, &ad7879_attr_group); - free_irq(ts->bus->irq, ts); + sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); + free_irq(ts->irq, ts); input_unregister_device(ts->input); - dev_dbg(&bus->dev, "unregistered touchscreen\n"); - - return 0; -} - -#ifdef CONFIG_PM -static int ad7879_suspend(bus_device *bus, pm_message_t message) -{ - struct ad7879 *ts = dev_get_drvdata(&bus->dev); - - ad7879_disable(ts); - - return 0; -} - -static int ad7879_resume(bus_device *bus) -{ - struct ad7879 *ts = dev_get_drvdata(&bus->dev); - - ad7879_enable(ts); - - return 0; -} -#else -#define ad7879_suspend NULL -#define ad7879_resume NULL -#endif - -#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) -#define MAX_SPI_FREQ_HZ 5000000 -#define AD7879_CMD_MAGIC 0xE000 -#define AD7879_CMD_READ (1 << 10) -#define AD7879_WRITECMD(reg) (AD7879_CMD_MAGIC | (reg & 0xF)) -#define AD7879_READCMD(reg) (AD7879_CMD_MAGIC | AD7879_CMD_READ | (reg & 0xF)) - -struct ser_req { - u16 command; - u16 data; - struct spi_message msg; - struct spi_transfer xfer[2]; -}; - -/* - * ad7879_read/write are only used for initial setup and for sysfs controls. - * The main traffic is done in ad7879_collect(). - */ - -static int ad7879_read(struct spi_device *spi, u8 reg) -{ - struct ser_req *req; - int status, ret; - - req = kzalloc(sizeof *req, GFP_KERNEL); - if (!req) - return -ENOMEM; - - spi_message_init(&req->msg); - - req->command = (u16) AD7879_READCMD(reg); - req->xfer[0].tx_buf = &req->command; - req->xfer[0].len = 2; - - req->xfer[1].rx_buf = &req->data; - req->xfer[1].len = 2; - - spi_message_add_tail(&req->xfer[0], &req->msg); - spi_message_add_tail(&req->xfer[1], &req->msg); - - status = spi_sync(spi, &req->msg); - ret = status ? : req->data; - - kfree(req); - - return ret; -} - -static int ad7879_write(struct spi_device *spi, u8 reg, u16 val) -{ - struct ser_req *req; - int status; - - req = kzalloc(sizeof *req, GFP_KERNEL); - if (!req) - return -ENOMEM; - - spi_message_init(&req->msg); - - req->command = (u16) AD7879_WRITECMD(reg); - req->xfer[0].tx_buf = &req->command; - req->xfer[0].len = 2; - - req->data = val; - req->xfer[1].tx_buf = &req->data; - req->xfer[1].len = 2; - - spi_message_add_tail(&req->xfer[0], &req->msg); - spi_message_add_tail(&req->xfer[1], &req->msg); - - status = spi_sync(spi, &req->msg); - - kfree(req); - - return status; -} - -static void ad7879_collect(struct ad7879 *ts) -{ - int status = spi_sync(ts->bus, &ts->msg); - - if (status) - dev_err(&ts->bus->dev, "spi_sync --> %d\n", status); -} - -static void ad7879_setup_ts_def_msg(struct ad7879 *ts) -{ - struct spi_message *m; - int i; - - ts->cmd = (u16) AD7879_READCMD(AD7879_REG_XPLUS); - - m = &ts->msg; - spi_message_init(m); - ts->xfer[0].tx_buf = &ts->cmd; - ts->xfer[0].len = 2; - - spi_message_add_tail(&ts->xfer[0], m); - - for (i = 0; i < AD7879_NR_SENSE; i++) { - ts->xfer[i + 1].rx_buf = &ts->conversion_data[i]; - ts->xfer[i + 1].len = 2; - spi_message_add_tail(&ts->xfer[i + 1], m); - } -} - -static int __devinit ad7879_probe(struct spi_device *spi) -{ - struct ad7879 *ts; - int error; - - /* don't exceed max specified SPI CLK frequency */ - if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { - dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz); - return -EINVAL; - } - - ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, ts); - ts->bus = spi; - - ad7879_setup_ts_def_msg(ts); - - error = ad7879_construct(spi, ts); - if (error) { - dev_set_drvdata(&spi->dev, NULL); - kfree(ts); - } - - return error; -} - -static int __devexit ad7879_remove(struct spi_device *spi) -{ - struct ad7879 *ts = dev_get_drvdata(&spi->dev); - - ad7879_destroy(spi, ts); - dev_set_drvdata(&spi->dev, NULL); kfree(ts); - - return 0; } - -static struct spi_driver ad7879_driver = { - .driver = { - .name = "ad7879", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = ad7879_probe, - .remove = __devexit_p(ad7879_remove), - .suspend = ad7879_suspend, - .resume = ad7879_resume, -}; - -static int __init ad7879_init(void) -{ - return spi_register_driver(&ad7879_driver); -} -module_init(ad7879_init); - -static void __exit ad7879_exit(void) -{ - spi_unregister_driver(&ad7879_driver); -} -module_exit(ad7879_exit); - -#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) - -/* All registers are word-sized. - * AD7879 uses a high-byte first convention. - */ -static int ad7879_read(struct i2c_client *client, u8 reg) -{ - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static int ad7879_write(struct i2c_client *client, u8 reg, u16 val) -{ - return i2c_smbus_write_word_data(client, reg, swab16(val)); -} - -static void ad7879_collect(struct ad7879 *ts) -{ - int i; - - for (i = 0; i < AD7879_NR_SENSE; i++) - ts->conversion_data[i] = ad7879_read(ts->bus, - AD7879_REG_XPLUS + i); -} - -static int __devinit ad7879_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ad7879 *ts; - int error; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_err(&client->dev, "SMBUS Word Data not Supported\n"); - return -EIO; - } - - ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - i2c_set_clientdata(client, ts); - ts->bus = client; - - error = ad7879_construct(client, ts); - if (error) - kfree(ts); - - return error; -} - -static int __devexit ad7879_remove(struct i2c_client *client) -{ - struct ad7879 *ts = dev_get_drvdata(&client->dev); - - ad7879_destroy(client, ts); - kfree(ts); - - return 0; -} - -static const struct i2c_device_id ad7879_id[] = { - { "ad7879", 0 }, - { "ad7889", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ad7879_id); - -static struct i2c_driver ad7879_driver = { - .driver = { - .name = "ad7879", - .owner = THIS_MODULE, - }, - .probe = ad7879_probe, - .remove = __devexit_p(ad7879_remove), - .suspend = ad7879_suspend, - .resume = ad7879_resume, - .id_table = ad7879_id, -}; - -static int __init ad7879_init(void) -{ - return i2c_add_driver(&ad7879_driver); -} -module_init(ad7879_init); - -static void __exit ad7879_exit(void) -{ - i2c_del_driver(&ad7879_driver); -} -module_exit(ad7879_exit); -#endif +EXPORT_SYMBOL(ad7879_remove); MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h new file mode 100644 index 000000000000..169f155db3b9 --- /dev/null +++ b/drivers/input/touchscreen/ad7879.h @@ -0,0 +1,30 @@ +/* + * AD7879/AD7889 touchscreen (bus interfaces) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _AD7879_H_ +#define _AD7879_H_ + +#include + +struct ad7879; +struct device; + +struct ad7879_bus_ops { + u16 bustype; + int (*read)(struct device *dev, u8 reg); + int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf); + int (*write)(struct device *dev, u8 reg, u16 val); +}; + +void ad7879_disable(struct ad7879 *); +void ad7879_enable(struct ad7879 *); +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq, + const struct ad7879_bus_ops *bops); +void ad7879_remove(struct ad7879 *); + +#endif -- cgit v1.2.3 From 14fbbc36d126d7ec7717144def386b9fc4c7fba2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 30 Jun 2010 14:50:51 -0700 Subject: Input: ad7879 - add open and close methods Tested-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879-i2c.c | 4 +- drivers/input/touchscreen/ad7879-spi.c | 4 +- drivers/input/touchscreen/ad7879.c | 113 +++++++++++++++++++++++---------- drivers/input/touchscreen/ad7879.h | 4 +- 4 files changed, 86 insertions(+), 39 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index 85dfdd27df5a..81937b21e9d2 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -20,7 +20,7 @@ static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message) { struct ad7879 *ts = i2c_get_clientdata(client); - ad7879_disable(ts); + ad7879_suspend(ts); return 0; } @@ -29,7 +29,7 @@ static int ad7879_i2c_resume(struct i2c_client *client) { struct ad7879 *ts = i2c_get_clientdata(client); - ad7879_enable(ts); + ad7879_resume(ts); return 0; } diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 5d1e5a050332..9fa8e5de5ff9 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -25,7 +25,7 @@ static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) { struct ad7879 *ts = spi_get_drvdata(spi); - ad7879_disable(ts); + ad7879_suspend(ts); return 0; } @@ -34,7 +34,7 @@ static int ad7879_spi_resume(struct spi_device *spi) { struct ad7879 *ts = spi_get_drvdata(spi); - ad7879_enable(ts); + ad7879_resume(ts); return 0; } diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 1de19691c4a3..fad65969cc72 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -112,10 +112,11 @@ struct ad7879 { struct timer_list timer; #ifdef CONFIG_GPIOLIB struct gpio_chip gc; + struct mutex mutex; #endif unsigned int irq; - struct mutex mutex; - bool disabled; /* P: mutex */ + bool disabled; /* P: input->mutex */ + bool suspended; /* P: input->mutex */ u16 conversion_data[AD7879_NR_SENSE]; char phys[32]; u8 first_conversion_delay; @@ -208,46 +209,91 @@ static irqreturn_t ad7879_irq(int irq, void *handle) return IRQ_HANDLED; } -static void ad7879_setup(struct ad7879 *ts) +static void __ad7879_enable(struct ad7879 *ts) { ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); + + enable_irq(ts->irq); } -void ad7879_disable(struct ad7879 *ts) +static void __ad7879_disable(struct ad7879 *ts) { - mutex_lock(&ts->mutex); + disable_irq(ts->irq); - if (!ts->disabled) { + if (del_timer_sync(&ts->timer)) + ad7879_ts_event_release(ts); - ts->disabled = true; - disable_irq(ts->irq); + ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN)); +} - if (del_timer_sync(&ts->timer)) - ad7879_ts_event_release(ts); - ad7879_write(ts, AD7879_REG_CTRL2, - AD7879_PM(AD7879_PM_SHUTDOWN)); - } +static int ad7879_open(struct input_dev *input) +{ + struct ad7879 *ts = input_get_drvdata(input); - mutex_unlock(&ts->mutex); + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_enable(ts); + + return 0; } -EXPORT_SYMBOL(ad7879_disable); -void ad7879_enable(struct ad7879 *ts) +static void ad7879_close(struct input_dev* input) { - mutex_lock(&ts->mutex); + struct ad7879 *ts = input_get_drvdata(input); + + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_disable(ts); +} + +void ad7879_suspend(struct ad7879 *ts) +{ + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && !ts->disabled && ts->input->users) + __ad7879_disable(ts); + + ts->suspended = true; + + mutex_unlock(&ts->input->mutex); +} +EXPORT_SYMBOL(ad7879_suspend); + +void ad7879_resume(struct ad7879 *ts) +{ + mutex_lock(&ts->input->mutex); - if (ts->disabled) { - ad7879_setup(ts); - ts->disabled = false; - enable_irq(ts->irq); + if (ts->suspended && !ts->disabled && ts->input->users) + __ad7879_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); +} +EXPORT_SYMBOL(ad7879_resume); + +static void ad7879_toggle(struct ad7879 *ts, bool disable) +{ + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && ts->input->users != 0) { + + if (disable) { + if (ts->disabled) + __ad7879_enable(ts); + } else { + if (!ts->disabled) + __ad7879_disable(ts); + } } - mutex_unlock(&ts->mutex); + ts->disabled = disable; + + mutex_unlock(&ts->input->mutex); } -EXPORT_SYMBOL(ad7879_enable); static ssize_t ad7879_disable_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -269,10 +315,7 @@ static ssize_t ad7879_disable_store(struct device *dev, if (error) return error; - if (val) - ad7879_disable(ts); - else - ad7879_enable(ts); + ad7879_toggle(ts, val); return count; } @@ -355,6 +398,8 @@ static int ad7879_gpio_add(struct ad7879 *ts, { int ret = 0; + mutex_init(&ts->mutex); + if (pdata->gpio_export) { ts->gc.direction_input = ad7879_gpio_direction_input; ts->gc.direction_output = ad7879_gpio_direction_output; @@ -431,11 +476,9 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, ts->bops = bops; ts->dev = dev; ts->input = input_dev; + ts->irq = irq; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); - mutex_init(&ts->mutex); - - ts->irq = irq; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->pressure_max = pdata->pressure_max ? : ~0; @@ -453,6 +496,11 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, input_dev->dev.parent = dev; input_dev->id.bustype = bops->bustype; + input_dev->open = ad7879_open; + input_dev->close = ad7879_close; + + input_set_drvdata(input_dev, ts); + __set_bit(EV_ABS, input_dev->evbit); __set_bit(ABS_X, input_dev->absbit); __set_bit(ABS_Y, input_dev->absbit); @@ -502,8 +550,6 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, AD7879_ACQ(ts->acquisition_time) | AD7879_TMR(ts->pen_down_acc_interval); - ad7879_setup(ts); - err = request_threaded_irq(ts->irq, NULL, ad7879_irq, IRQF_TRIGGER_FALLING, dev_name(dev), ts); @@ -512,6 +558,8 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, goto err_free_mem; } + __ad7879_disable(ts); + err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) goto err_free_irq; @@ -543,7 +591,6 @@ EXPORT_SYMBOL(ad7879_probe); void ad7879_remove(struct ad7879 *ts) { ad7879_gpio_remove(ts); - ad7879_disable(ts); sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); free_irq(ts->irq, ts); input_unregister_device(ts->input); diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h index 169f155db3b9..6b45a27236c7 100644 --- a/drivers/input/touchscreen/ad7879.h +++ b/drivers/input/touchscreen/ad7879.h @@ -21,8 +21,8 @@ struct ad7879_bus_ops { int (*write)(struct device *dev, u8 reg, u16 val); }; -void ad7879_disable(struct ad7879 *); -void ad7879_enable(struct ad7879 *); +void ad7879_suspend(struct ad7879 *); +void ad7879_resume(struct ad7879 *); struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq, const struct ad7879_bus_ops *bops); void ad7879_remove(struct ad7879 *); -- cgit v1.2.3 From 16ea10a7d557a0177cbbd716b4a06e5373d513ba Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 30 Jun 2010 14:51:09 -0700 Subject: Input: ad7879 - use i2c_smbus_read_i2c_block_data() to lower overhead Avoid additional addressing overhead incurred by word_data transfers. Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879-i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index 81937b21e9d2..d82a38ee9a3e 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -51,10 +51,13 @@ static int ad7879_i2c_read(struct device *dev, u8 reg) static int ad7879_i2c_multi_read(struct device *dev, u8 first_reg, u8 count, u16 *buf) { + struct i2c_client *client = to_i2c_client(dev); u8 idx; + i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf); + for (idx = 0; idx < count; ++idx) - buf[idx] = ad7879_i2c_read(dev, first_reg + idx); + buf[idx] = swab16(buf[idx]); return 0; } -- cgit v1.2.3 From 447b9065b418cbeb6a03ebdcd08629ac26ed8e4a Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 30 Jun 2010 14:51:09 -0700 Subject: Input: ad7879 - fix spi word size to 16 bit Signed-off-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879-spi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 9fa8e5de5ff9..59c6e68c4325 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -134,6 +134,7 @@ static const struct ad7879_bus_ops ad7879_spi_bus_ops = { static int __devinit ad7879_spi_probe(struct spi_device *spi) { struct ad7879 *ts; + int err; /* don't exceed max specified SPI CLK frequency */ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { @@ -141,6 +142,13 @@ static int __devinit ad7879_spi_probe(struct spi_device *spi) return -EINVAL; } + spi->bits_per_word = 16; + err = spi_setup(spi); + if (err) { + dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n"); + return err; + } + ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops); if (IS_ERR(ts)) return PTR_ERR(ts); -- cgit v1.2.3 From 963ce8ae6dbc7c8dffb1b117ba14673d40b22dda Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 30 Jun 2010 14:51:10 -0700 Subject: Input: ad7879 - report EV_KEY/BTN_TOUCH events Some input events users such as Android require BTN_TOUCH events. Implement EV_KEY/BTN_TOUCH and make sure that the release event is not erroneous scheduled without a preceding valid touch. Avoid duplicated BTN_TOUCH events, even though input core filters them. Signed-off-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index fad65969cc72..ba6f0bd1e762 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -146,7 +146,7 @@ static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) return ts->bops->write(ts->dev, reg, val); } -static void ad7879_report(struct ad7879 *ts) +static int ad7879_report(struct ad7879 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; @@ -175,11 +175,17 @@ static void ad7879_report(struct ad7879 *ts) Rt /= z1; Rt = (Rt + 2047) >> 12; + if (!timer_pending(&ts->timer)) + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); input_report_abs(input_dev, ABS_PRESSURE, Rt); input_sync(input_dev); + return 0; } + + return -EINVAL; } static void ad7879_ts_event_release(struct ad7879 *ts) @@ -187,6 +193,7 @@ static void ad7879_ts_event_release(struct ad7879 *ts) struct input_dev *input_dev = ts->input; input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); } @@ -202,9 +209,9 @@ static irqreturn_t ad7879_irq(int irq, void *handle) struct ad7879 *ts = handle; ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); - ad7879_report(ts); - mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); + if (!ad7879_report(ts)) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); return IRQ_HANDLED; } @@ -506,6 +513,9 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, __set_bit(ABS_Y, input_dev->absbit); __set_bit(ABS_PRESSURE, input_dev->absbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, pdata->x_min ? : 0, pdata->x_max ? : MAX_12BIT, -- cgit v1.2.3 From 0f622bf465e78c390e13c5f4a14d0b3f8fb7c7e5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 1 Jul 2010 09:01:50 -0700 Subject: Input: ads7846 - do not allow altering platform data Tested-by: Anatolij Gustschin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 69210cb56c54..a3771607ead5 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -878,14 +878,15 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) static int __devinit ads7846_probe(struct spi_device *spi) { - struct ads7846 *ts; - struct ads7846_packet *packet; - struct input_dev *input_dev; - struct ads7846_platform_data *pdata = spi->dev.platform_data; - struct spi_message *m; - struct spi_transfer *x; - int vref; - int err; + struct ads7846 *ts; + struct ads7846_packet *packet; + struct input_dev *input_dev; + const struct ads7846_platform_data *pdata = spi->dev.platform_data; + struct spi_message *m; + struct spi_transfer *x; + unsigned long irq_flags; + int vref; + int err; if (!spi->irq) { dev_dbg(&spi->dev, "no IRQ?\n"); @@ -1174,20 +1175,22 @@ static int __devinit ads7846_probe(struct spi_device *spi) goto err_put_regulator; } - if (!pdata->irq_flags) - pdata->irq_flags = IRQF_TRIGGER_FALLING; + irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; - if (request_irq(spi->irq, ads7846_irq, pdata->irq_flags, - spi->dev.driver->name, ts)) { + err = request_irq(spi->irq, ads7846_irq, irq_flags, + spi->dev.driver->name, ts); + + if (err && !pdata->irq_flags) { dev_info(&spi->dev, "trying pin change workaround on irq %d\n", spi->irq); err = request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, spi->dev.driver->name, ts); - if (err) { - dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); - goto err_disable_regulator; - } + } + + if (err) { + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + goto err_disable_regulator; } err = ads784x_hwmon_register(spi, ts); -- cgit v1.2.3 From 3eac5c7e44f35eb07f0ecb28ce60f15b2dda1932 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Thu, 1 Jul 2010 09:01:56 -0700 Subject: Input: ads7846 - extend the driver for ads7845 controller support ADS7845 is a controller for 5-wire touch screens and somewhat different from 7846. It requires three serial communications to accomplish one complete conversion. Unlike 7846 it doesn't allow Z1-/Z2- position measurement. The patch extends the ads7846 driver to also support ads7845. The packet struct is extended to contain needed command and conversion buffers. ads7846_rx() and ads7846_rx_val() now differentiate between 7845 and 7846 case. ads7846_probe() is modified to setup ads7845 specific command and conversion messages and to switch ads7845 into power-down mode, since this is needed to be prepared to respond to pendown interrupts. Signed-off-by: Anatolij Gustschin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 172 ++++++++++++++++++++++++++++-------- 1 file changed, 135 insertions(+), 37 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index a3771607ead5..16031933a8f6 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -68,6 +68,8 @@ struct ts_event { u16 y; u16 z1, z2; int ignore; + u8 x_buf[3]; + u8 y_buf[3]; }; /* @@ -79,6 +81,8 @@ struct ads7846_packet { u8 read_x, read_y, read_z1, read_z2, pwrdown; u16 dummy; /* for the pwrdown read */ struct ts_event tc; + /* for ads7845 with mpc5121 psc spi we use 3-byte buffers */ + u8 read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3]; }; struct ads7846 { @@ -207,6 +211,14 @@ struct ser_req { struct spi_transfer xfer[6]; }; +struct ads7845_ser_req { + u8 command[3]; + u8 pwrdown[3]; + u8 sample[3]; + struct spi_message msg; + struct spi_transfer xfer[2]; +}; + static void ads7846_enable(struct ads7846 *ts); static void ads7846_disable(struct ads7846 *ts); @@ -287,6 +299,41 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) return status; } +static int ads7845_read12_ser(struct device *dev, unsigned command) +{ + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7845_ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); + int status; + + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command[0] = (u8) command; + req->xfer[0].tx_buf = req->command; + req->xfer[0].rx_buf = req->sample; + req->xfer[0].len = 3; + spi_message_add_tail(&req->xfer[0], &req->msg); + + ts->irq_disabled = 1; + disable_irq(spi->irq); + status = spi_sync(spi, &req->msg); + ts->irq_disabled = 0; + enable_irq(spi->irq); + + if (status == 0) { + /* BE12 value, then padding */ + status = be16_to_cpu(*((u16 *)&req->sample[1])); + status = status >> 3; + status &= 0x0fff; + } + + kfree(req); + return status; +} + #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) #define SHOW(name, var, adjust) static ssize_t \ @@ -540,10 +587,17 @@ static void ads7846_rx(void *ads) /* ads7846_rx_val() did in-place conversion (including byteswap) from * on-the-wire format as part of debouncing to get stable readings. */ - x = packet->tc.x; - y = packet->tc.y; - z1 = packet->tc.z1; - z2 = packet->tc.z2; + if (ts->model == 7845) { + x = *(u16 *)packet->tc.x_buf; + y = *(u16 *)packet->tc.y_buf; + z1 = 0; + z2 = 0; + } else { + x = packet->tc.x; + y = packet->tc.y; + z1 = packet->tc.z1; + z2 = packet->tc.z2; + } /* range filtering */ if (x == MAX_12BIT) @@ -551,6 +605,12 @@ static void ads7846_rx(void *ads) if (ts->model == 7843) { Rt = ts->pressure_max / 2; + } else if (ts->model == 7845) { + if (get_pendown_state(ts)) + Rt = ts->pressure_max / 2; + else + Rt = 0; + dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt); } else if (likely(x && z1)) { /* compute touch pressure resistance using equation #2 */ Rt = z2; @@ -671,10 +731,14 @@ static void ads7846_rx_val(void *ads) m = &ts->msg[ts->msg_idx]; t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; - * built from two 8 bit values written msb-first. - */ - val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; + if (ts->model == 7845) { + val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; + } else { + /* adjust: on-wire is a must-ignore bit, a BE12 value, then + * padding; built from two 8 bit values written msb-first. + */ + val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; + } action = ts->filter(ts->filter_data, ts->msg_idx, &val); switch (action) { @@ -1009,16 +1073,26 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); - /* y- still on; turn on only y+ (and ADC) */ - packet->read_y = READ_Y(vref); - x->tx_buf = &packet->read_y; - x->len = 1; - spi_message_add_tail(x, m); + if (ts->model == 7845) { + packet->read_y_cmd[0] = READ_Y(vref); + packet->read_y_cmd[1] = 0; + packet->read_y_cmd[2] = 0; + x->tx_buf = &packet->read_y_cmd[0]; + x->rx_buf = &packet->tc.y_buf[0]; + x->len = 3; + spi_message_add_tail(x, m); + } else { + /* y- still on; turn on only y+ (and ADC) */ + packet->read_y = READ_Y(vref); + x->tx_buf = &packet->read_y; + x->len = 1; + spi_message_add_tail(x, m); - x++; - x->rx_buf = &packet->tc.y; - x->len = 2; - spi_message_add_tail(x, m); + x++; + x->rx_buf = &packet->tc.y; + x->len = 2; + spi_message_add_tail(x, m); + } /* the first sample after switching drivers can be low quality; * optionally discard it, using a second one after the signals @@ -1044,17 +1118,28 @@ static int __devinit ads7846_probe(struct spi_device *spi) m++; spi_message_init(m); - /* turn y- off, x+ on, then leave in lowpower */ - x++; - packet->read_x = READ_X(vref); - x->tx_buf = &packet->read_x; - x->len = 1; - spi_message_add_tail(x, m); + if (ts->model == 7845) { + x++; + packet->read_x_cmd[0] = READ_X(vref); + packet->read_x_cmd[1] = 0; + packet->read_x_cmd[2] = 0; + x->tx_buf = &packet->read_x_cmd[0]; + x->rx_buf = &packet->tc.x_buf[0]; + x->len = 3; + spi_message_add_tail(x, m); + } else { + /* turn y- off, x+ on, then leave in lowpower */ + x++; + packet->read_x = READ_X(vref); + x->tx_buf = &packet->read_x; + x->len = 1; + spi_message_add_tail(x, m); - x++; - x->rx_buf = &packet->tc.x; - x->len = 2; - spi_message_add_tail(x, m); + x++; + x->rx_buf = &packet->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + } /* ... maybe discard first sample ... */ if (pdata->settle_delay_usecs) { @@ -1145,15 +1230,25 @@ static int __devinit ads7846_probe(struct spi_device *spi) m++; spi_message_init(m); - x++; - packet->pwrdown = PWRDOWN; - x->tx_buf = &packet->pwrdown; - x->len = 1; - spi_message_add_tail(x, m); + if (ts->model == 7845) { + x++; + packet->pwrdown_cmd[0] = PWRDOWN; + packet->pwrdown_cmd[1] = 0; + packet->pwrdown_cmd[2] = 0; + x->tx_buf = &packet->pwrdown_cmd[0]; + x->len = 3; + } else { + x++; + packet->pwrdown = PWRDOWN; + x->tx_buf = &packet->pwrdown; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->dummy; + x->len = 2; + } - x++; - x->rx_buf = &packet->dummy; - x->len = 2; CS_CHANGE(*x); spi_message_add_tail(x, m); @@ -1202,8 +1297,11 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ - (void) ads7846_read12_ser(&spi->dev, - READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); + if (ts->model == 7845) + ads7845_read12_ser(&spi->dev, PWRDOWN); + else + (void) ads7846_read12_ser(&spi->dev, + READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); if (err) -- cgit v1.2.3 From 312e8e8a9e2471b0ada7366497fffb3ff1a40e2c Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Sun, 4 Jul 2010 01:21:25 -0700 Subject: Input: mcs - Add MCS touchkey driver This adds support for MELPAS MCS5000/MSC5080 touch key controllers. Signed-off-by: Joonyoung Shim Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mcs5000_ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 1fb0c2f06a44..6ee9940aaf5b 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -105,7 +105,7 @@ enum mcs5000_ts_read_offset { struct mcs5000_ts_data { struct i2c_client *client; struct input_dev *input_dev; - const struct mcs5000_ts_platform_data *platform_data; + const struct mcs_platform_data *platform_data; }; static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) @@ -164,7 +164,7 @@ static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) { - const struct mcs5000_ts_platform_data *platform_data = + const struct mcs_platform_data *platform_data = data->platform_data; struct i2c_client *client = data->client; -- cgit v1.2.3 From 4cf51c383d7a8d472a6090a0d19c371d40e823c9 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Wed, 14 Jul 2010 21:55:30 -0700 Subject: Input: Add ATMEL QT602240 touchscreen driver The chip's full name is AT42QT602240 or ATMXT224. This is a capacitive touchscreen supporting 10-contact multitouch and using I2C interface. Signed-off-by: Joonyoung Shim Acked-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/qt602240_ts.c | 1401 +++++++++++++++++++++++++++++++ 3 files changed, 1414 insertions(+) create mode 100644 drivers/input/touchscreen/qt602240_ts.c (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ff18d896ea6d..7bfcfdff6cf8 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -291,6 +291,18 @@ config TOUCHSCREEN_PENMOUNT To compile this driver as a module, choose M here: the module will be called penmount. +config TOUCHSCREEN_QT602240 + tristate "QT602240 I2C Touchscreen" + 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 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 9efdd4424757..779de0d9d413 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -32,6 +32,7 @@ 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_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c new file mode 100644 index 000000000000..66b26ad3032a --- /dev/null +++ b/drivers/input/touchscreen/qt602240_ts.c @@ -0,0 +1,1401 @@ +/* + * AT42QT602240/ATMXT224 Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Version */ +#define QT602240_VER_20 20 +#define QT602240_VER_21 21 +#define QT602240_VER_22 22 + +/* Slave addresses */ +#define QT602240_APP_LOW 0x4a +#define QT602240_APP_HIGH 0x4b +#define QT602240_BOOT_LOW 0x24 +#define QT602240_BOOT_HIGH 0x25 + +/* Firmware */ +#define QT602240_FW_NAME "qt602240.fw" + +/* Registers */ +#define QT602240_FAMILY_ID 0x00 +#define QT602240_VARIANT_ID 0x01 +#define QT602240_VERSION 0x02 +#define QT602240_BUILD 0x03 +#define QT602240_MATRIX_X_SIZE 0x04 +#define QT602240_MATRIX_Y_SIZE 0x05 +#define QT602240_OBJECT_NUM 0x06 +#define QT602240_OBJECT_START 0x07 + +#define QT602240_OBJECT_SIZE 6 + +/* Object types */ +#define QT602240_DEBUG_DIAGNOSTIC 37 +#define QT602240_GEN_MESSAGE 5 +#define QT602240_GEN_COMMAND 6 +#define QT602240_GEN_POWER 7 +#define QT602240_GEN_ACQUIRE 8 +#define QT602240_TOUCH_MULTI 9 +#define QT602240_TOUCH_KEYARRAY 15 +#define QT602240_TOUCH_PROXIMITY 23 +#define QT602240_PROCI_GRIPFACE 20 +#define QT602240_PROCG_NOISE 22 +#define QT602240_PROCI_ONETOUCH 24 +#define QT602240_PROCI_TWOTOUCH 27 +#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ +#define QT602240_SPT_GPIOPWM 19 +#define QT602240_SPT_SELFTEST 25 +#define QT602240_SPT_CTECONFIG 28 +#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */ + +/* QT602240_GEN_COMMAND field */ +#define QT602240_COMMAND_RESET 0 +#define QT602240_COMMAND_BACKUPNV 1 +#define QT602240_COMMAND_CALIBRATE 2 +#define QT602240_COMMAND_REPORTALL 3 +#define QT602240_COMMAND_DIAGNOSTIC 5 + +/* QT602240_GEN_POWER field */ +#define QT602240_POWER_IDLEACQINT 0 +#define QT602240_POWER_ACTVACQINT 1 +#define QT602240_POWER_ACTV2IDLETO 2 + +/* QT602240_GEN_ACQUIRE field */ +#define QT602240_ACQUIRE_CHRGTIME 0 +#define QT602240_ACQUIRE_TCHDRIFT 2 +#define QT602240_ACQUIRE_DRIFTST 3 +#define QT602240_ACQUIRE_TCHAUTOCAL 4 +#define QT602240_ACQUIRE_SYNC 5 +#define QT602240_ACQUIRE_ATCHCALST 6 +#define QT602240_ACQUIRE_ATCHCALSTHR 7 + +/* QT602240_TOUCH_MULTI field */ +#define QT602240_TOUCH_CTRL 0 +#define QT602240_TOUCH_XORIGIN 1 +#define QT602240_TOUCH_YORIGIN 2 +#define QT602240_TOUCH_XSIZE 3 +#define QT602240_TOUCH_YSIZE 4 +#define QT602240_TOUCH_BLEN 6 +#define QT602240_TOUCH_TCHTHR 7 +#define QT602240_TOUCH_TCHDI 8 +#define QT602240_TOUCH_ORIENT 9 +#define QT602240_TOUCH_MOVHYSTI 11 +#define QT602240_TOUCH_MOVHYSTN 12 +#define QT602240_TOUCH_NUMTOUCH 14 +#define QT602240_TOUCH_MRGHYST 15 +#define QT602240_TOUCH_MRGTHR 16 +#define QT602240_TOUCH_AMPHYST 17 +#define QT602240_TOUCH_XRANGE_LSB 18 +#define QT602240_TOUCH_XRANGE_MSB 19 +#define QT602240_TOUCH_YRANGE_LSB 20 +#define QT602240_TOUCH_YRANGE_MSB 21 +#define QT602240_TOUCH_XLOCLIP 22 +#define QT602240_TOUCH_XHICLIP 23 +#define QT602240_TOUCH_YLOCLIP 24 +#define QT602240_TOUCH_YHICLIP 25 +#define QT602240_TOUCH_XEDGECTRL 26 +#define QT602240_TOUCH_XEDGEDIST 27 +#define QT602240_TOUCH_YEDGECTRL 28 +#define QT602240_TOUCH_YEDGEDIST 29 +#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ + +/* QT602240_PROCI_GRIPFACE field */ +#define QT602240_GRIPFACE_CTRL 0 +#define QT602240_GRIPFACE_XLOGRIP 1 +#define QT602240_GRIPFACE_XHIGRIP 2 +#define QT602240_GRIPFACE_YLOGRIP 3 +#define QT602240_GRIPFACE_YHIGRIP 4 +#define QT602240_GRIPFACE_MAXTCHS 5 +#define QT602240_GRIPFACE_SZTHR1 7 +#define QT602240_GRIPFACE_SZTHR2 8 +#define QT602240_GRIPFACE_SHPTHR1 9 +#define QT602240_GRIPFACE_SHPTHR2 10 +#define QT602240_GRIPFACE_SUPEXTTO 11 + +/* QT602240_PROCI_NOISE field */ +#define QT602240_NOISE_CTRL 0 +#define QT602240_NOISE_OUTFLEN 1 +#define QT602240_NOISE_GCAFUL_LSB 3 +#define QT602240_NOISE_GCAFUL_MSB 4 +#define QT602240_NOISE_GCAFLL_LSB 5 +#define QT602240_NOISE_GCAFLL_MSB 6 +#define QT602240_NOISE_ACTVGCAFVALID 7 +#define QT602240_NOISE_NOISETHR 8 +#define QT602240_NOISE_FREQHOPSCALE 10 +#define QT602240_NOISE_FREQ0 11 +#define QT602240_NOISE_FREQ1 12 +#define QT602240_NOISE_FREQ2 13 +#define QT602240_NOISE_FREQ3 14 +#define QT602240_NOISE_FREQ4 15 +#define QT602240_NOISE_IDLEGCAFVALID 16 + +/* QT602240_SPT_COMMSCONFIG */ +#define QT602240_COMMS_CTRL 0 +#define QT602240_COMMS_CMD 1 + +/* QT602240_SPT_CTECONFIG field */ +#define QT602240_CTE_CTRL 0 +#define QT602240_CTE_CMD 1 +#define QT602240_CTE_MODE 2 +#define QT602240_CTE_IDLEGCAFDEPTH 3 +#define QT602240_CTE_ACTVGCAFDEPTH 4 +#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */ + +#define QT602240_VOLTAGE_DEFAULT 2700000 +#define QT602240_VOLTAGE_STEP 10000 + +/* Define for QT602240_GEN_COMMAND */ +#define QT602240_BOOT_VALUE 0xa5 +#define QT602240_BACKUP_VALUE 0x55 +#define QT602240_BACKUP_TIME 25 /* msec */ +#define QT602240_RESET_TIME 65 /* msec */ + +#define QT602240_FWRESET_TIME 175 /* msec */ + +/* Command to unlock bootloader */ +#define QT602240_UNLOCK_CMD_MSB 0xaa +#define QT602240_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define QT602240_FRAME_CRC_CHECK 0x02 +#define QT602240_FRAME_CRC_FAIL 0x03 +#define QT602240_FRAME_CRC_PASS 0x04 +#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define QT602240_BOOT_STATUS_MASK 0x3f + +/* Touch status */ +#define QT602240_SUPPRESS (1 << 1) +#define QT602240_AMP (1 << 2) +#define QT602240_VECTOR (1 << 3) +#define QT602240_MOVE (1 << 4) +#define QT602240_RELEASE (1 << 5) +#define QT602240_PRESS (1 << 6) +#define QT602240_DETECT (1 << 7) + +/* Touchscreen absolute values */ +#define QT602240_MAX_XC 0x3ff +#define QT602240_MAX_YC 0x3ff +#define QT602240_MAX_AREA 0xff + +#define QT602240_MAX_FINGER 10 + +/* Initial register values recommended from chip vendor */ +static const u8 init_vals_ver_20[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, + 0x1e, 0x00, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, + 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x04, 0x08, +}; + +static const u8 init_vals_ver_21[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, + 0x0f, 0x0a, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +static const u8 init_vals_ver_22[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, + 0x0f, 0x0a, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +struct qt602240_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct qt602240_object { + u8 type; + u16 start_address; + u8 size; + u8 instances; + u8 num_report_ids; + + /* to map object and message */ + u8 max_reportid; +}; + +struct qt602240_message { + u8 reportid; + u8 message[7]; + u8 checksum; +}; + +struct qt602240_finger { + int status; + int x; + int y; + int area; +}; + +/* Each client has this additional data */ +struct qt602240_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct qt602240_platform_data *pdata; + struct qt602240_object *object_table; + struct qt602240_info info; + struct qt602240_finger finger[QT602240_MAX_FINGER]; + unsigned int irq; +}; + +static bool qt602240_object_readable(unsigned int type) +{ + switch (type) { + case QT602240_GEN_MESSAGE: + case QT602240_GEN_COMMAND: + case QT602240_GEN_POWER: + case QT602240_GEN_ACQUIRE: + case QT602240_TOUCH_MULTI: + case QT602240_TOUCH_KEYARRAY: + case QT602240_TOUCH_PROXIMITY: + case QT602240_PROCI_GRIPFACE: + case QT602240_PROCG_NOISE: + case QT602240_PROCI_ONETOUCH: + case QT602240_PROCI_TWOTOUCH: + case QT602240_SPT_COMMSCONFIG: + case QT602240_SPT_GPIOPWM: + case QT602240_SPT_SELFTEST: + case QT602240_SPT_CTECONFIG: + case QT602240_SPT_USERDATA: + return true; + default: + return false; + } +} + +static bool qt602240_object_writable(unsigned int type) +{ + switch (type) { + case QT602240_GEN_COMMAND: + case QT602240_GEN_POWER: + case QT602240_GEN_ACQUIRE: + case QT602240_TOUCH_MULTI: + case QT602240_TOUCH_KEYARRAY: + case QT602240_TOUCH_PROXIMITY: + case QT602240_PROCI_GRIPFACE: + case QT602240_PROCG_NOISE: + case QT602240_PROCI_ONETOUCH: + case QT602240_PROCI_TWOTOUCH: + case QT602240_SPT_GPIOPWM: + case QT602240_SPT_SELFTEST: + case QT602240_SPT_CTECONFIG: + return true; + default: + return false; + } +} + +static void qt602240_dump_message(struct device *dev, + struct qt602240_message *message) +{ + dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); + dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); + dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); + dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); + dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); + dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); + dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); + dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); + dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); +} + +static int qt602240_check_bootloader(struct i2c_client *client, + unsigned int state) +{ + u8 val; + +recheck: + if (i2c_master_recv(client, &val, 1) != 1) { + dev_err(&client->dev, "%s: i2c recv failed\n", __func__); + return -EIO; + } + + switch (state) { + case QT602240_WAITING_BOOTLOAD_CMD: + case QT602240_WAITING_FRAME_DATA: + val &= ~QT602240_BOOT_STATUS_MASK; + break; + case QT602240_FRAME_CRC_PASS: + if (val == QT602240_FRAME_CRC_CHECK) + goto recheck; + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(&client->dev, "Unvalid bootloader mode state\n"); + return -EINVAL; + } + + return 0; +} + +static int qt602240_unlock_bootloader(struct i2c_client *client) +{ + u8 buf[2]; + + buf[0] = QT602240_UNLOCK_CMD_LSB; + buf[1] = QT602240_UNLOCK_CMD_MSB; + + if (i2c_master_send(client, buf, 2) != 2) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_fw_write(struct i2c_client *client, + const u8 *data, unsigned int frame_size) +{ + if (i2c_master_send(client, data, frame_size) != frame_size) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int __qt602240_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + u8 buf[2]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + + if (i2c_transfer(client->adapter, xfer, 2) != 2) { + dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) +{ + return __qt602240_read_reg(client, reg, 1, val); +} + +static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + u8 buf[3]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = val; + + if (i2c_master_send(client, buf, 3) != 3) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_read_object_table(struct i2c_client *client, + u16 reg, u8 *object_buf) +{ + return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, + object_buf); +} + +static struct qt602240_object * +qt602240_get_object(struct qt602240_data *data, u8 type) +{ + struct qt602240_object *object; + int i; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_err(&data->client->dev, "Invalid object type\n"); + return NULL; +} + +static int qt602240_read_message(struct qt602240_data *data, + struct qt602240_message *message) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, QT602240_GEN_MESSAGE); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __qt602240_read_reg(data->client, reg, + sizeof(struct qt602240_message), message); +} + +static int qt602240_read_object(struct qt602240_data *data, + u8 type, u8 offset, u8 *val) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __qt602240_read_reg(data->client, reg + offset, 1, val); +} + +static int qt602240_write_object(struct qt602240_data *data, + u8 type, u8 offset, u8 val) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return qt602240_write_reg(data->client, reg + offset, val); +} + +static void qt602240_input_report(struct qt602240_data *data, int single_id) +{ + struct qt602240_finger *finger = data->finger; + struct input_dev *input_dev = data->input_dev; + int status = finger[single_id].status; + int finger_num = 0; + int id; + + for (id = 0; id < QT602240_MAX_FINGER; id++) { + if (!finger[id].status) + continue; + + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[id].status != QT602240_RELEASE ? + finger[id].area : 0); + input_report_abs(input_dev, ABS_MT_POSITION_X, + finger[id].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + finger[id].y); + input_mt_sync(input_dev); + + if (finger[id].status == QT602240_RELEASE) + finger[id].status = 0; + else + finger_num++; + } + + input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + + if (status != QT602240_RELEASE) { + input_report_abs(input_dev, ABS_X, finger[single_id].x); + input_report_abs(input_dev, ABS_Y, finger[single_id].y); + } + + input_sync(input_dev); +} + +static void qt602240_input_touchevent(struct qt602240_data *data, + struct qt602240_message *message, int id) +{ + struct qt602240_finger *finger = data->finger; + struct device *dev = &data->client->dev; + u8 status = message->message[0]; + int x; + int y; + int area; + + /* Check the touch is present on the screen */ + if (!(status & QT602240_DETECT)) { + if (status & QT602240_RELEASE) { + dev_dbg(dev, "[%d] released\n", id); + + finger[id].status = QT602240_RELEASE; + qt602240_input_report(data, id); + } + return; + } + + /* Check only AMP detection */ + if (!(status & (QT602240_PRESS | QT602240_MOVE))) + return; + + x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); + y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); + area = message->message[4]; + + dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, + status & QT602240_MOVE ? "moved" : "pressed", + x, y, area); + + finger[id].status = status & QT602240_MOVE ? + QT602240_MOVE : QT602240_PRESS; + finger[id].x = x; + finger[id].y = y; + finger[id].area = area; + + qt602240_input_report(data, id); +} + +static irqreturn_t qt602240_interrupt(int irq, void *dev_id) +{ + struct qt602240_data *data = dev_id; + struct qt602240_message message; + struct qt602240_object *object; + struct device *dev = &data->client->dev; + int id; + u8 reportid; + u8 max_reportid; + u8 min_reportid; + + do { + if (qt602240_read_message(data, &message)) { + dev_err(dev, "Failed to read message\n"); + goto end; + } + + reportid = message.reportid; + + /* whether reportid is thing of QT602240_TOUCH_MULTI */ + object = qt602240_get_object(data, QT602240_TOUCH_MULTI); + if (!object) + goto end; + + max_reportid = object->max_reportid; + min_reportid = max_reportid - object->num_report_ids + 1; + id = reportid - min_reportid; + + if (reportid >= min_reportid && reportid <= max_reportid) + qt602240_input_touchevent(data, &message, id); + else + qt602240_dump_message(dev, &message); + } while (reportid != 0xff); + +end: + return IRQ_HANDLED; +} + +static int qt602240_check_reg_init(struct qt602240_data *data) +{ + struct qt602240_object *object; + struct device *dev = &data->client->dev; + int index = 0; + int i, j; + u8 version = data->info.version; + u8 *init_vals; + + switch (version) { + case QT602240_VER_20: + init_vals = (u8 *)init_vals_ver_20; + break; + case QT602240_VER_21: + init_vals = (u8 *)init_vals_ver_21; + break; + case QT602240_VER_22: + init_vals = (u8 *)init_vals_ver_22; + break; + default: + dev_err(dev, "Firmware version %d doesn't support\n", version); + return -EINVAL; + } + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + if (!qt602240_object_writable(object->type)) + continue; + + for (j = 0; j < object->size + 1; j++) + qt602240_write_object(data, object->type, j, + init_vals[index + j]); + + index += object->size + 1; + } + + return 0; +} + +static int qt602240_check_matrix_size(struct qt602240_data *data) +{ + const struct qt602240_platform_data *pdata = data->pdata; + struct device *dev = &data->client->dev; + int mode = -1; + int error; + u8 val; + + dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); + dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); + + switch (pdata->x_line) { + case 0 ... 15: + if (pdata->y_line <= 14) + mode = 0; + break; + case 16: + if (pdata->y_line <= 12) + mode = 1; + if (pdata->y_line == 13 || pdata->y_line == 14) + mode = 0; + break; + case 17: + if (pdata->y_line <= 11) + mode = 2; + if (pdata->y_line == 12 || pdata->y_line == 13) + mode = 1; + break; + case 18: + if (pdata->y_line <= 10) + mode = 3; + if (pdata->y_line == 11 || pdata->y_line == 12) + mode = 2; + break; + case 19: + if (pdata->y_line <= 9) + mode = 4; + if (pdata->y_line == 10 || pdata->y_line == 11) + mode = 3; + break; + case 20: + mode = 4; + } + + if (mode < 0) { + dev_err(dev, "Invalid X/Y lines\n"); + return -EINVAL; + } + + error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_MODE, &val); + if (error) + return error; + + if (mode == val) + return 0; + + /* Change the CTE configuration */ + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_CTRL, 1); + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_MODE, mode); + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_CTRL, 0); + + return 0; +} + +static int qt602240_make_highchg(struct qt602240_data *data) +{ + struct device *dev = &data->client->dev; + int count = 10; + int error; + u8 val; + + /* Read dummy message to make high CHG pin */ + do { + error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); + if (error) + return error; + } while ((val != 0xff) && --count); + + if (!count) { + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; + } + + return 0; +} + +static void qt602240_handle_pdata(struct qt602240_data *data) +{ + const struct qt602240_platform_data *pdata = data->pdata; + u8 voltage; + + /* Set touchscreen lines */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, + pdata->x_line); + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, + pdata->y_line); + + /* Set touchscreen orient */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, + pdata->orient); + + /* Set touchscreen burst length */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_BLEN, pdata->blen); + + /* Set touchscreen threshold */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_TCHTHR, pdata->threshold); + + /* Set touchscreen resolution */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + + /* Set touchscreen voltage */ + if (data->info.version >= QT602240_VER_21 && pdata->voltage) { + if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { + voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / + QT602240_VOLTAGE_STEP; + voltage = 0xff - voltage + 1; + } else + voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / + QT602240_VOLTAGE_STEP; + + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_VOLTAGE, voltage); + } +} + +static int qt602240_get_info(struct qt602240_data *data) +{ + struct i2c_client *client = data->client; + struct qt602240_info *info = &data->info; + int error; + u8 val; + + error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); + if (error) + return error; + info->family_id = val; + + error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); + if (error) + return error; + info->variant_id = val; + + error = qt602240_read_reg(client, QT602240_VERSION, &val); + if (error) + return error; + info->version = val; + + error = qt602240_read_reg(client, QT602240_BUILD, &val); + if (error) + return error; + info->build = val; + + error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); + if (error) + return error; + info->object_num = val; + + return 0; +} + +static int qt602240_get_object_table(struct qt602240_data *data) +{ + int error; + int i; + u16 reg; + u8 reportid = 0; + u8 buf[QT602240_OBJECT_SIZE]; + + for (i = 0; i < data->info.object_num; i++) { + struct qt602240_object *object = data->object_table + i; + + reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; + error = qt602240_read_object_table(data->client, reg, buf); + if (error) + return error; + + object->type = buf[0]; + object->start_address = (buf[2] << 8) | buf[1]; + object->size = buf[3]; + object->instances = buf[4]; + object->num_report_ids = buf[5]; + + if (object->num_report_ids) { + reportid += object->num_report_ids * + (object->instances + 1); + object->max_reportid = reportid; + } + } + + return 0; +} + +static int qt602240_initialize(struct qt602240_data *data) +{ + struct i2c_client *client = data->client; + struct qt602240_info *info = &data->info; + int error; + u8 val; + + error = qt602240_get_info(data); + if (error) + return error; + + data->object_table = kcalloc(info->object_num, + sizeof(struct qt602240_data), + GFP_KERNEL); + if (!data->object_table) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Get object table information */ + error = qt602240_get_object_table(data); + if (error) + return error; + + /* Check register init values */ + error = qt602240_check_reg_init(data); + if (error) + return error; + + /* Check X/Y matrix size */ + error = qt602240_check_matrix_size(data); + if (error) + return error; + + error = qt602240_make_highchg(data); + if (error) + return error; + + qt602240_handle_pdata(data); + + /* Backup to memory */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_BACKUPNV, + QT602240_BACKUP_VALUE); + msleep(QT602240_BACKUP_TIME); + + /* Soft reset */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, 1); + msleep(QT602240_RESET_TIME); + + /* Update matrix size at info struct */ + error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); + if (error) + return error; + info->matrix_xsize = val; + + error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); + if (error) + return error; + info->matrix_ysize = val; + + dev_info(&client->dev, + "Family ID: %d Variant ID: %d Version: %d Build: %d\n", + info->family_id, info->variant_id, info->version, + info->build); + + dev_info(&client->dev, + "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", + info->matrix_xsize, info->matrix_ysize, + info->object_num); + + return 0; +} + +static ssize_t qt602240_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + struct qt602240_object *object; + int count = 0; + int i, j; + int error; + u8 val; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + count += sprintf(buf + count, + "Object Table Element %d(Type %d)\n", + i + 1, object->type); + + if (!qt602240_object_readable(object->type)) { + count += sprintf(buf + count, "\n"); + continue; + } + + for (j = 0; j < object->size + 1; j++) { + error = qt602240_read_object(data, + object->type, j, &val); + if (error) + return error; + + count += sprintf(buf + count, + " Byte %d: 0x%x (%d)\n", j, val, val); + } + + count += sprintf(buf + count, "\n"); + } + + return count; +} + +static int qt602240_load_fw(struct device *dev, const char *fn) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + + ret = request_firmware(&fw, fn, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return ret; + } + + /* Change to the bootloader mode */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); + msleep(QT602240_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == QT602240_APP_LOW) + client->addr = QT602240_BOOT_LOW; + else + client->addr = QT602240_BOOT_HIGH; + + ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); + if (ret) + goto out; + + /* Unlock bootloader */ + qt602240_unlock_bootloader(client); + + while (pos < fw->size) { + ret = qt602240_check_bootloader(client, + QT602240_WAITING_FRAME_DATA); + if (ret) + goto out; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* We should add 2 at frame size as the the firmware data is not + * included the CRC bytes. + */ + frame_size += 2; + + /* Write one frame to device */ + qt602240_fw_write(client, fw->data + pos, frame_size); + + ret = qt602240_check_bootloader(client, + QT602240_FRAME_CRC_PASS); + if (ret) + goto out; + + pos += frame_size; + + dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); + } + +out: + release_firmware(fw); + + /* Change to slave address of application */ + if (client->addr == QT602240_BOOT_LOW) + client->addr = QT602240_APP_LOW; + else + client->addr = QT602240_APP_HIGH; + + return ret; +} + +static ssize_t qt602240_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + unsigned int version; + int error; + + if (sscanf(buf, "%u", &version) != 1) { + dev_err(dev, "Invalid values\n"); + return -EINVAL; + } + + if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { + dev_err(dev, "FW update supported starting with version 21\n"); + return -EINVAL; + } + + disable_irq(data->irq); + + error = qt602240_load_fw(dev, QT602240_FW_NAME); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_dbg(dev, "The firmware update succeeded\n"); + + /* Wait for reset */ + msleep(QT602240_FWRESET_TIME); + + kfree(data->object_table); + data->object_table = NULL; + + qt602240_initialize(data); + } + + enable_irq(data->irq); + + return count; +} + +static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); + +static struct attribute *qt602240_attrs[] = { + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group qt602240_attr_group = { + .attrs = qt602240_attrs, +}; + +static void qt602240_start(struct qt602240_data *data) +{ + /* Touch enable */ + qt602240_write_object(data, + QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); +} + +static void qt602240_stop(struct qt602240_data *data) +{ + /* Touch disable */ + qt602240_write_object(data, + QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); +} + +static int qt602240_input_open(struct input_dev *dev) +{ + struct qt602240_data *data = input_get_drvdata(dev); + + qt602240_start(data); + + return 0; +} + +static void qt602240_input_close(struct input_dev *dev) +{ + struct qt602240_data *data = input_get_drvdata(dev); + + qt602240_stop(data); +} + +static int __devinit qt602240_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt602240_data *data; + struct input_dev *input_dev; + int error; + + if (!client->dev.platform_data) + return -EINVAL; + + data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = qt602240_input_open; + input_dev->close = qt602240_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, QT602240_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, QT602240_MAX_YC, 0, 0); + + /* For multi touch */ + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, QT602240_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, QT602240_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, QT602240_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + data->client = client; + data->input_dev = input_dev; + data->pdata = client->dev.platform_data; + data->irq = client->irq; + + i2c_set_clientdata(client, data); + + error = qt602240_initialize(data); + if (error) + goto err_free_object; + + error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_object; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); + if (error) + goto err_unregister_device; + + return 0; + +err_unregister_device: + input_unregister_device(input_dev); + input_dev = NULL; +err_free_irq: + free_irq(client->irq, data); +err_free_object: + kfree(data->object_table); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit qt602240_remove(struct i2c_client *client) +{ + struct qt602240_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); + free_irq(data->irq, data); + input_unregister_device(data->input_dev); + kfree(data->object_table); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct qt602240_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + qt602240_stop(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int qt602240_resume(struct i2c_client *client) +{ + struct qt602240_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + /* Soft reset */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, 1); + + msleep(QT602240_RESET_TIME); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + qt602240_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#else +#define qt602240_suspend NULL +#define qt602240_resume NULL +#endif + +static const struct i2c_device_id qt602240_id[] = { + { "qt602240_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qt602240_id); + +static struct i2c_driver qt602240_driver = { + .driver = { + .name = "qt602240_ts", + .owner = THIS_MODULE, + }, + .probe = qt602240_probe, + .remove = __devexit_p(qt602240_remove), + .suspend = qt602240_suspend, + .resume = qt602240_resume, + .id_table = qt602240_id, +}; + +static int __init qt602240_init(void) +{ + return i2c_add_driver(&qt602240_driver); +} + +static void __exit qt602240_exit(void) +{ + i2c_del_driver(&qt602240_driver); +} + +module_init(qt602240_init); +module_exit(qt602240_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ed4299e1b173f111ac0c40d6617e47fbff02b52f Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 15 Jul 2010 09:16:39 -0700 Subject: Input: usbtouchscreen - implement basic suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements basic support for suspend & resume. Signed-off-by: Oliver Neukum Tested-by: Petr Štetiar Tested-by: Ondrej Zary Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index b9cee2738adb..9cda660ee7bb 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -1296,6 +1296,29 @@ static void usbtouch_close(struct input_dev *input) usb_kill_urb(usbtouch->irq); } +static int usbtouch_suspend +(struct usb_interface *intf, pm_message_t message) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + + usb_kill_urb(usbtouch->irq); + + return 0; +} + +static int usbtouch_resume(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct input_dev *input = usbtouch->input; + int result = 0; + + mutex_lock(&input->mutex); + if (input->users || usbtouch->type->irq_always) + result = usb_submit_urb(usbtouch->irq, GFP_NOIO); + mutex_unlock(&input->mutex); + + return result; +} static void usbtouch_free_buffers(struct usb_device *udev, struct usbtouch_usb *usbtouch) @@ -1486,6 +1509,8 @@ static struct usb_driver usbtouch_driver = { .name = "usbtouchscreen", .probe = usbtouch_probe, .disconnect = usbtouch_disconnect, + .suspend = usbtouch_suspend, + .resume = usbtouch_resume, .id_table = usbtouch_devices, }; -- cgit v1.2.3 From 5d9efc59e689445f1f8c4eceb125c1a12898e65c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 15 Jul 2010 09:19:51 -0700 Subject: Input: usbtouchscreen - implement runtime power management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implement USB autosuspend while the device is opened for devices that do remote wakeup with a fallback to open/close for those devices that don't. Devices that require the host to constantly poll them are never autosuspended. Signed-off-by: Oliver Neukum Tested-by: Petr Štetiar Tested-by: Ondrej Zary Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 9cda660ee7bb..77e671f8f1a4 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -1268,6 +1268,7 @@ static void usbtouch_irq(struct urb *urb) usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length); exit: + usb_mark_last_busy(interface_to_usbdev(usbtouch->interface)); retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) err("%s - usb_submit_urb failed with result: %d", @@ -1277,23 +1278,39 @@ exit: static int usbtouch_open(struct input_dev *input) { struct usbtouch_usb *usbtouch = input_get_drvdata(input); + int r; usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface); + r = usb_autopm_get_interface(usbtouch->interface) ? -EIO : 0; + if (r < 0) + goto out; + if (!usbtouch->type->irq_always) { - if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) - return -EIO; + if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) { + r = -EIO; + goto out_put; + } } - return 0; + usbtouch->interface->needs_remote_wakeup = 1; +out_put: + usb_autopm_put_interface(usbtouch->interface); +out: + return r; } static void usbtouch_close(struct input_dev *input) { struct usbtouch_usb *usbtouch = input_get_drvdata(input); + int r; if (!usbtouch->type->irq_always) usb_kill_urb(usbtouch->irq); + r = usb_autopm_get_interface(usbtouch->interface); + usbtouch->interface->needs_remote_wakeup = 0; + if (!r) + usb_autopm_put_interface(usbtouch->interface); } static int usbtouch_suspend @@ -1457,8 +1474,11 @@ static int usbtouch_probe(struct usb_interface *intf, usb_set_intfdata(intf, usbtouch); if (usbtouch->type->irq_always) { + /* this can't fail */ + usb_autopm_get_interface(intf); err = usb_submit_urb(usbtouch->irq, GFP_KERNEL); if (err) { + usb_autopm_put_interface(intf); err("%s - usb_submit_urb failed with result: %d", __func__, err); goto out_unregister_input; @@ -1512,6 +1532,7 @@ static struct usb_driver usbtouch_driver = { .suspend = usbtouch_suspend, .resume = usbtouch_resume, .id_table = usbtouch_devices, + .supports_autosuspend = 1, }; static int __init usbtouch_init(void) -- cgit v1.2.3 From a8aef622929bbba4d89498fb41dd445c14fae1f7 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 15 Jul 2010 09:21:40 -0700 Subject: Input: usbtouchscreen - implement reset_resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements reset_resume() by splitting init into allocations of private data structures and device initializations. Device initializations are repeated upon reset_resume. Signed-off-by: Oliver Neukum Tested-by: Petr Štetiar Tested-by: Ondrej Zary Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 108 ++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 32 deletions(-) (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 77e671f8f1a4..f45f80f6d336 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -95,6 +95,7 @@ struct usbtouch_device_info { int (*get_pkt_len) (unsigned char *pkt, int len); int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt); + int (*alloc) (struct usbtouch_usb *usbtouch); int (*init) (struct usbtouch_usb *usbtouch); void (*exit) (struct usbtouch_usb *usbtouch); }; @@ -510,7 +511,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) int ret = -ENOMEM; unsigned char *buf; - buf = kmalloc(2, GFP_KERNEL); + buf = kmalloc(2, GFP_NOIO); if (!buf) goto err_nobuf; /* reset */ @@ -735,11 +736,43 @@ static void nexio_ack_complete(struct urb *urb) { } +static int nexio_alloc(struct usbtouch_usb *usbtouch) +{ + struct nexio_priv *priv; + int ret = -ENOMEM; + + usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL); + if (!usbtouch->priv) + goto out_buf; + + priv = usbtouch->priv; + + priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), + GFP_KERNEL); + if (!priv->ack_buf) + goto err_priv; + + priv->ack = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->ack) { + dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); + goto err_ack_buf; + } + + return 0; + +err_ack_buf: + kfree(priv->ack_buf); +err_priv: + kfree(priv); +out_buf: + return ret; +} + static int nexio_init(struct usbtouch_usb *usbtouch) { struct usb_device *dev = interface_to_usbdev(usbtouch->interface); struct usb_host_interface *interface = usbtouch->interface->cur_altsetting; - struct nexio_priv *priv; + struct nexio_priv *priv = usbtouch->priv; int ret = -ENOMEM; int actual_len, i; unsigned char *buf; @@ -758,7 +791,7 @@ static int nexio_init(struct usbtouch_usb *usbtouch) if (!input_ep || !output_ep) return -ENXIO; - buf = kmalloc(NEXIO_BUFSIZE, GFP_KERNEL); + buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO); if (!buf) goto out_buf; @@ -790,11 +823,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch) switch (buf[0]) { case 0x83: /* firmware version */ if (!firmware_ver) - firmware_ver = kstrdup(&buf[2], GFP_KERNEL); + firmware_ver = kstrdup(&buf[2], GFP_NOIO); break; case 0x84: /* device name */ if (!device_name) - device_name = kstrdup(&buf[2], GFP_KERNEL); + device_name = kstrdup(&buf[2], GFP_NOIO); break; } } @@ -805,36 +838,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch) kfree(firmware_ver); kfree(device_name); - /* prepare ACK URB */ - ret = -ENOMEM; - - usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL); - if (!usbtouch->priv) - goto out_buf; - - priv = usbtouch->priv; - - priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), - GFP_KERNEL); - if (!priv->ack_buf) - goto err_priv; - - priv->ack = usb_alloc_urb(0, GFP_KERNEL); - if (!priv->ack) { - dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); - goto err_ack_buf; - } - usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep), priv->ack_buf, sizeof(nexio_ack_pkt), nexio_ack_complete, usbtouch); ret = 0; - goto out_buf; -err_ack_buf: - kfree(priv->ack_buf); -err_priv: - kfree(priv); out_buf: kfree(buf); return ret; @@ -1125,6 +1133,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .rept_size = 1024, .irq_always = true, .read_data = nexio_read_data, + .alloc = nexio_alloc, .init = nexio_init, .exit = nexio_exit, }, @@ -1337,6 +1346,31 @@ static int usbtouch_resume(struct usb_interface *intf) return result; } +static int usbtouch_reset_resume(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct input_dev *input = usbtouch->input; + int err = 0; + + /* reinit the device */ + if (usbtouch->type->init) { + err = usbtouch->type->init(usbtouch); + if (err) { + dbg("%s - type->init() failed, err: %d", + __func__, err); + return err; + } + } + + /* restart IO if needed */ + mutex_lock(&input->mutex); + if (input->users) + err = usb_submit_urb(usbtouch->irq, GFP_NOIO); + mutex_unlock(&input->mutex); + + return err; +} + static void usbtouch_free_buffers(struct usb_device *udev, struct usbtouch_usb *usbtouch) { @@ -1456,12 +1490,21 @@ static int usbtouch_probe(struct usb_interface *intf, usbtouch->irq->transfer_dma = usbtouch->data_dma; usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - /* device specific init */ + /* device specific allocations */ + if (type->alloc) { + err = type->alloc(usbtouch); + if (err) { + dbg("%s - type->alloc() failed, err: %d", __func__, err); + goto out_free_urb; + } + } + + /* device specific initialisation*/ if (type->init) { err = type->init(usbtouch); if (err) { dbg("%s - type->init() failed, err: %d", __func__, err); - goto out_free_urb; + goto out_do_exit; } } @@ -1531,6 +1574,7 @@ static struct usb_driver usbtouch_driver = { .disconnect = usbtouch_disconnect, .suspend = usbtouch_suspend, .resume = usbtouch_resume, + .reset_resume = usbtouch_reset_resume, .id_table = usbtouch_devices, .supports_autosuspend = 1, }; -- cgit v1.2.3 From 60347c194acec7ff1b4291ac8e62a5345244c2ee Mon Sep 17 00:00:00 2001 From: Samuli Konttila Date: Fri, 30 Jul 2010 09:02:43 -0700 Subject: Input: cy8ctmg110 - capacitive touchscreen support Add support for the cy8ctmg110 capacitive touchscreen used on some embedded devices. (Some clean up by Alan Cox) Signed-off-by: Alan Cox Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 14 ++ drivers/input/touchscreen/Makefile | 3 +- drivers/input/touchscreen/cy8ctmg110_ts.c | 363 ++++++++++++++++++++++++++++++ 3 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 drivers/input/touchscreen/cy8ctmg110_ts.c (limited to 'drivers/input/touchscreen') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 7bfcfdff6cf8..61f35184f76c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -98,6 +98,20 @@ config TOUCHSCREEN_BITSY To compile this driver as a module, choose M here: the module will be called h3600_ts_input. +config TOUCHSCREEN_CY8CTMG110 + tristate "cy8ctmg110 touchscreen" + depends on I2C + depends on GPIOLIB + + help + Say Y here if you have a cy8ctmg110 capacitive touchscreen on + an AAVA device. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cy8ctmg110_ts. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 779de0d9d413..bd6f30b4ff70 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o @@ -41,7 +43,6 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o -obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c new file mode 100644 index 000000000000..4eb7df0b7f87 --- /dev/null +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -0,0 +1,363 @@ +/* + * Driver for cypress touch screen controller + * + * Copyright (c) 2009 Aava Mobile + * + * Some cleanups by Alan Cox + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CY8CTMG110_DRIVER_NAME "cy8ctmg110" + +/* Touch coordinates */ +#define CY8CTMG110_X_MIN 0 +#define CY8CTMG110_Y_MIN 0 +#define CY8CTMG110_X_MAX 759 +#define CY8CTMG110_Y_MAX 465 + + +/* cy8ctmg110 register definitions */ +#define CY8CTMG110_TOUCH_WAKEUP_TIME 0 +#define CY8CTMG110_TOUCH_SLEEP_TIME 2 +#define CY8CTMG110_TOUCH_X1 3 +#define CY8CTMG110_TOUCH_Y1 5 +#define CY8CTMG110_TOUCH_X2 7 +#define CY8CTMG110_TOUCH_Y2 9 +#define CY8CTMG110_FINGERS 11 +#define CY8CTMG110_GESTURE 12 +#define CY8CTMG110_REG_MAX 13 + + +/* + * The touch driver structure. + */ +struct cy8ctmg110 { + struct input_dev *input; + char phys[32]; + struct i2c_client *client; + int reset_pin; + int irq_pin; +}; + +/* + * cy8ctmg110_power is the routine that is called when touch hardware + * will powered off or on. + */ +static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron) +{ + if (ts->reset_pin) + gpio_direction_output(ts->reset_pin, 1 - poweron); +} + +static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg, + unsigned char len, unsigned char *value) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + unsigned char i2c_data[6]; + + BUG_ON(len > 5); + + i2c_data[0] = reg; + memcpy(i2c_data + 1, value, len); + + ret = i2c_master_send(client, i2c_data, len + 1); + if (ret != 1) { + dev_err(&client->dev, "i2c write data cmd failed\n"); + return ret; + } + + return 0; +} + +static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc, + unsigned char *data, unsigned char len, unsigned char cmd) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + struct i2c_msg msg[2] = { + /* first write slave position to i2c devices */ + { client->addr, 0, 1, &cmd }, + /* Second read data from position */ + { client->addr, I2C_M_RD, len, data } + }; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + return ret; + + return 0; +} + +static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc) +{ + struct input_dev *input = tsc->input; + unsigned char reg_p[CY8CTMG110_REG_MAX]; + int x, y; + + memset(reg_p, 0, CY8CTMG110_REG_MAX); + + /* Reading coordinates */ + if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0) + return -EIO; + + y = reg_p[2] << 8 | reg_p[3]; + x = reg_p[0] << 8 | reg_p[1]; + + /* Number of touch */ + if (reg_p[8] == 0) { + input_report_key(input, BTN_TOUCH, 0); + } else { + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + + input_sync(input); + + return 0; +} + +static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep) +{ + unsigned char reg_p[3]; + + if (sleep) { + reg_p[0] = 0x00; + reg_p[1] = 0xff; + reg_p[2] = 5; + } else { + reg_p[0] = 0x10; + reg_p[1] = 0xff; + reg_p[2] = 0; + } + + return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p); +} + +static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id) +{ + struct cy8ctmg110 *tsc = dev_id; + + cy8ctmg110_touch_pos(tsc); + + return IRQ_HANDLED; +} + +static int __devinit cy8ctmg110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct cy8ctmg110_pdata *pdata = client->dev.platform_data; + struct cy8ctmg110 *ts; + struct input_dev *input_dev; + int err; + + /* No pdata no way forward */ + if (pdata == NULL) { + dev_err(&client->dev, "no pdata\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->input = input_dev; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, + CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); + + if (ts->reset_pin) { + err = gpio_request(ts->reset_pin, NULL); + if (err) { + dev_err(&client->dev, + "Unable to request GPIO pin %d.\n", + ts->reset_pin); + goto err_free_mem; + } + } + + cy8ctmg110_power(ts, true); + cy8ctmg110_set_sleepmode(ts, false); + + err = gpio_request(ts->irq_pin, "touch_irq_key"); + if (err < 0) { + dev_err(&client->dev, + "Failed to request GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_shutoff_device; + } + + err = gpio_direction_input(ts->irq_pin); + if (err < 0) { + dev_err(&client->dev, + "Failed to configure input direction for GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_free_irq_gpio; + } + + client->irq = gpio_to_irq(ts->irq_pin); + if (client->irq < 0) { + err = client->irq; + dev_err(&client->dev, + "Unable to get irq number for GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_free_irq_gpio; + } + + err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread, + IRQF_TRIGGER_RISING, "touch_reset_key", ts); + if (err < 0) { + dev_err(&client->dev, + "irq %d busy? error %d\n", client->irq, err); + goto err_free_irq_gpio; + } + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + i2c_set_clientdata(client, ts); + device_init_wakeup(&client->dev, 1); + return 0; + +err_free_irq: + free_irq(client->irq, ts); +err_free_irq_gpio: + gpio_free(ts->irq_pin); +err_shutoff_device: + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + if (ts->reset_pin) + gpio_free(ts->reset_pin); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +#ifdef CONFIG_PM +static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + else { + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + } + return 0; +} + +static int cy8ctmg110_resume(struct i2c_client *client) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + else { + cy8ctmg110_power(ts, true); + cy8ctmg110_set_sleepmode(ts, false); + } + return 0; +} +#endif + +static int __devexit cy8ctmg110_remove(struct i2c_client *client) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + + free_irq(client->irq, ts); + input_unregister_device(ts->input); + gpio_free(ts->irq_pin); + if (ts->reset_pin) + gpio_free(ts->reset_pin); + kfree(ts); + + return 0; +} + +static struct i2c_device_id cy8ctmg110_idtable[] = { + { CY8CTMG110_DRIVER_NAME, 1 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); + +static struct i2c_driver cy8ctmg110_driver = { + .driver = { + .owner = THIS_MODULE, + .name = CY8CTMG110_DRIVER_NAME, + }, + .id_table = cy8ctmg110_idtable, + .probe = cy8ctmg110_probe, + .remove = __devexit_p(cy8ctmg110_remove), +#ifdef CONFIG_PM + .suspend = cy8ctmg110_suspend, + .resume = cy8ctmg110_resume, +#endif +}; + +static int __init cy8ctmg110_init(void) +{ + return i2c_add_driver(&cy8ctmg110_driver); +} + +static void __exit cy8ctmg110_exit(void) +{ + i2c_del_driver(&cy8ctmg110_driver); +} + +module_init(cy8ctmg110_init); +module_exit(cy8ctmg110_exit); + +MODULE_AUTHOR("Samuli Konttila "); +MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3