summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/88pm860x-ts.c226
-rw-r--r--drivers/input/touchscreen/Kconfig878
-rw-r--r--drivers/input/touchscreen/Makefile88
-rw-r--r--drivers/input/touchscreen/ad7877.c868
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c109
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c165
-rw-r--r--drivers/input/touchscreen/ad7879.c649
-rw-r--r--drivers/input/touchscreen/ad7879.h30
-rw-r--r--drivers/input/touchscreen/ads7846.c1440
-rw-r--r--drivers/input/touchscreen/atmel-wm97xx.c449
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c1275
-rw-r--r--drivers/input/touchscreen/atmel_tsadcc.c359
-rw-r--r--drivers/input/touchscreen/auo-pixcir-ts.c642
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_Base.bbin0 -> 41612 bytes
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_Clb.bbin0 -> 32804 bytes
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_Drv.bbin0 -> 114476 bytes
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_Drv.h158
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_Reg.h187
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_ts.c1614
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_userpara.c196
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/AW5306_userpara.h99
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/Kconfig11
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/Makefile35
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/irq_gpio.c149
-rwxr-xr-xdrivers/input/touchscreen/aw5306_ts/irq_gpio.h13
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c659
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c357
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/Kconfig16
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/Makefile33
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/cyp140_i2c.c1412
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/cyttsp.h696
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/cyttsp_fw_upgrade.c993
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/debug.txt3
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/wmt_ts.c1094
-rwxr-xr-xdrivers/input/touchscreen/cyp140_ts/wmt_ts.h120
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c625
-rw-r--r--drivers/input/touchscreen/cyttsp_core.h149
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c.c136
-rw-r--r--drivers/input/touchscreen/cyttsp_spi.c200
-rw-r--r--drivers/input/touchscreen/da9034-ts.c387
-rw-r--r--drivers/input/touchscreen/dynapro.c206
-rw-r--r--drivers/input/touchscreen/eeti_ts.c327
-rw-r--r--drivers/input/touchscreen/egalax_ts.c292
-rw-r--r--drivers/input/touchscreen/elo.c423
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/Kconfig11
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/Makefile34
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ft5402_config.c2295
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ft5402_config.h71
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ft5402_ini_config.h411
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ft5x0x.c937
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ft5x0x.h207
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ft5x0x_upg.c506
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ini.c406
-rwxr-xr-xdrivers/input/touchscreen/ft5x0x/ini.h43
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/Kconfig11
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/Makefile34
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/focaltech_ctl.h27
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft5402_config.c2295
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft5402_config.h71
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft5402_ini_config.h411
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft5x0x.c896
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft5x0x.h205
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft5x0x_upg.c506
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.c1021
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.h79
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft6x06_ts.c511
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ft6x06_ts.h52
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ini.c406
-rwxr-xr-xdrivers/input/touchscreen/ft6x0x/ini.h43
-rw-r--r--drivers/input/touchscreen/fujitsu_ts.c189
-rwxr-xr-xdrivers/input/touchscreen/gsl1680_ts/Kconfig16
-rwxr-xr-xdrivers/input/touchscreen/gsl1680_ts/Makefile33
-rwxr-xr-xdrivers/input/touchscreen/gsl1680_ts/gslX680.c1416
-rwxr-xr-xdrivers/input/touchscreen/gsl1680_ts/gslX680.h35
-rwxr-xr-xdrivers/input/touchscreen/gsl1680_ts/gsl_point_id.bbin0 -> 38321 bytes
-rwxr-xr-xdrivers/input/touchscreen/gsl1680_ts/wmt_ts.c1102
-rwxr-xr-xdrivers/input/touchscreen/gsl1680_ts/wmt_ts.h117
-rwxr-xr-xdrivers/input/touchscreen/gt9xx_ts/Kconfig11
-rwxr-xr-xdrivers/input/touchscreen/gt9xx_ts/Makefile34
-rwxr-xr-xdrivers/input/touchscreen/gt9xx_ts/goodix_tool.c615
-rwxr-xr-xdrivers/input/touchscreen/gt9xx_ts/gt9xx.c2163
-rwxr-xr-xdrivers/input/touchscreen/gt9xx_ts/gt9xx.h278
-rwxr-xr-xdrivers/input/touchscreen/gt9xx_ts/gt9xx_firmware.h6
-rwxr-xr-xdrivers/input/touchscreen/gt9xx_ts/gt9xx_update.c1939
-rw-r--r--drivers/input/touchscreen/gunze.c204
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c494
-rw-r--r--drivers/input/touchscreen/hampshire.c205
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c127
-rw-r--r--drivers/input/touchscreen/htcpen.c250
-rwxr-xr-xdrivers/input/touchscreen/icn83xx_ts/Kconfig16
-rwxr-xr-xdrivers/input/touchscreen/icn83xx_ts/Makefile32
-rwxr-xr-xdrivers/input/touchscreen/icn83xx_ts/flash.c973
-rwxr-xr-xdrivers/input/touchscreen/icn83xx_ts/icn83xx.c2034
-rwxr-xr-xdrivers/input/touchscreen/icn83xx_ts/icn83xx.h434
-rwxr-xr-xdrivers/input/touchscreen/icn83xx_ts/icn83xx_fw.h3
-rwxr-xr-xdrivers/input/touchscreen/icn85xx_ts/Kconfig16
-rwxr-xr-xdrivers/input/touchscreen/icn85xx_ts/Makefile32
-rwxr-xr-xdrivers/input/touchscreen/icn85xx_ts/icn85xx.c2431
-rwxr-xr-xdrivers/input/touchscreen/icn85xx_ts/icn85xx.h500
-rwxr-xr-xdrivers/input/touchscreen/icn85xx_ts/icn85xx_flash.c1050
-rwxr-xr-xdrivers/input/touchscreen/icn85xx_ts/icn85xx_fw.h2517
-rw-r--r--drivers/input/touchscreen/ili210x.c360
-rw-r--r--drivers/input/touchscreen/inexio.c207
-rw-r--r--drivers/input/touchscreen/intel-mid-touch.c671
-rw-r--r--drivers/input/touchscreen/jornada720_ts.c176
-rw-r--r--drivers/input/touchscreen/lpc32xx_ts.c400
-rwxr-xr-xdrivers/input/touchscreen/lw86x0_ts/Kconfig11
-rwxr-xr-xdrivers/input/touchscreen/lw86x0_ts/Makefile34
-rwxr-xr-xdrivers/input/touchscreen/lw86x0_ts/lw86x0_ts.c1321
-rwxr-xr-xdrivers/input/touchscreen/lw86x0_ts/lw86x0_ts.h53
-rwxr-xr-xdrivers/input/touchscreen/lw86x0_ts/wmt_ts.c1165
-rwxr-xr-xdrivers/input/touchscreen/lw86x0_ts/wmt_ts.h98
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c310
-rw-r--r--drivers/input/touchscreen/max11801_ts.c262
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c268
-rw-r--r--drivers/input/touchscreen/mcs5000_ts.c310
-rwxr-xr-xdrivers/input/touchscreen/metusb/Makefile33
-rwxr-xr-xdrivers/input/touchscreen/metusb/metusb.c856
-rw-r--r--drivers/input/touchscreen/migor_ts.c249
-rw-r--r--drivers/input/touchscreen/mk712.c219
-rw-r--r--drivers/input/touchscreen/mtouch.c220
-rw-r--r--drivers/input/touchscreen/pcap_ts.c260
-rw-r--r--drivers/input/touchscreen/penmount.c335
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c229
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c441
-rwxr-xr-xdrivers/input/touchscreen/semisens/Makefile33
-rwxr-xr-xdrivers/input/touchscreen/semisens/sn310m-touch-pdata.h201
-rwxr-xr-xdrivers/input/touchscreen/semisens/sn310m-touch.c332
-rwxr-xr-xdrivers/input/touchscreen/semisens/sn310m-touch.h97
-rwxr-xr-xdrivers/input/touchscreen/semisens/touch.c1121
-rwxr-xr-xdrivers/input/touchscreen/semisens/touch.h54
-rwxr-xr-xdrivers/input/touchscreen/sis_usbhid_ts/Kconfig16
-rwxr-xr-xdrivers/input/touchscreen/sis_usbhid_ts/Makefile32
-rwxr-xr-xdrivers/input/touchscreen/sis_usbhid_ts/hid-sis.c1104
-rwxr-xr-xdrivers/input/touchscreen/sitronix/Kconfig11
-rwxr-xr-xdrivers/input/touchscreen/sitronix/Makefile34
-rwxr-xr-xdrivers/input/touchscreen/sitronix/irq_gpio.c148
-rwxr-xr-xdrivers/input/touchscreen/sitronix/irq_gpio.h13
-rwxr-xr-xdrivers/input/touchscreen/sitronix/sitronix_i2c.c817
-rwxr-xr-xdrivers/input/touchscreen/sitronix/sitronix_i2c.h137
-rwxr-xr-xdrivers/input/touchscreen/ssd253x_ts/Kconfig16
-rwxr-xr-xdrivers/input/touchscreen/ssd253x_ts/Makefile32
-rwxr-xr-xdrivers/input/touchscreen/ssd253x_ts/ssd253x-ts.c1827
-rwxr-xr-xdrivers/input/touchscreen/ssd253x_ts/ssd253x-ts.h28
-rwxr-xr-xdrivers/input/touchscreen/ssd253x_ts/wmt_ts.c810
-rwxr-xr-xdrivers/input/touchscreen/ssd253x_ts/wmt_ts.h116
-rw-r--r--drivers/input/touchscreen/st1232.c275
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c387
-rw-r--r--drivers/input/touchscreen/synaptics_i2c_rmi.c675
-rw-r--r--drivers/input/touchscreen/ti_tscadc.c486
-rw-r--r--drivers/input/touchscreen/tnetv107x-ts.c386
-rw-r--r--drivers/input/touchscreen/touchit213.c234
-rw-r--r--drivers/input/touchscreen/touchright.c194
-rw-r--r--drivers/input/touchscreen/touchwin.c201
-rw-r--r--drivers/input/touchscreen/tps6507x-ts.c377
-rw-r--r--drivers/input/touchscreen/tsc2005.c754
-rw-r--r--drivers/input/touchscreen/tsc2007.c406
-rw-r--r--drivers/input/touchscreen/tsc40.c184
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c467
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c1690
-rwxr-xr-xdrivers/input/touchscreen/vt1609_ts/Makefile4
-rwxr-xr-xdrivers/input/touchscreen/vt1609_ts/vt1609_dual.c692
-rwxr-xr-xdrivers/input/touchscreen/vt1609_ts/vt1609_ts.c1481
-rwxr-xr-xdrivers/input/touchscreen/vt1609_ts/vt1609_ts.h301
-rw-r--r--drivers/input/touchscreen/w90p910_ts.c339
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c608
-rw-r--r--drivers/input/touchscreen/wm831x-ts.c410
-rw-r--r--drivers/input/touchscreen/wm9705.c350
-rw-r--r--drivers/input/touchscreen/wm9712.c467
-rw-r--r--drivers/input/touchscreen/wm9713.c481
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c848
-rwxr-xr-xdrivers/input/touchscreen/zet6221_ts/Kconfig16
-rwxr-xr-xdrivers/input/touchscreen/zet6221_ts/Makefile32
-rwxr-xr-xdrivers/input/touchscreen/zet6221_ts/wmt_ts.c833
-rwxr-xr-xdrivers/input/touchscreen/zet6221_ts/wmt_ts.h149
-rwxr-xr-xdrivers/input/touchscreen/zet6221_ts/zet6221_downloader.c1209
-rwxr-xr-xdrivers/input/touchscreen/zet6221_ts/zet6221_i2c.c2786
-rwxr-xr-xdrivers/input/touchscreen/zet6221_ts/zet6221_ts.h6
-rw-r--r--drivers/input/touchscreen/zylonite-wm97xx.c232
179 files changed, 81956 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c
new file mode 100644
index 00000000..05f30b73
--- /dev/null
+++ b/drivers/input/touchscreen/88pm860x-ts.c
@@ -0,0 +1,226 @@
+/*
+ * Touchscreen driver for Marvell 88PM860x
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/slab.h>
+
+#define MEAS_LEN (8)
+#define ACCURATE_BIT (12)
+
+/* touch register */
+#define MEAS_EN3 (0x52)
+
+#define MEAS_TSIX_1 (0x8D)
+#define MEAS_TSIX_2 (0x8E)
+#define MEAS_TSIY_1 (0x8F)
+#define MEAS_TSIY_2 (0x90)
+#define MEAS_TSIZ1_1 (0x91)
+#define MEAS_TSIZ1_2 (0x92)
+#define MEAS_TSIZ2_1 (0x93)
+#define MEAS_TSIZ2_2 (0x94)
+
+/* bit definitions of touch */
+#define MEAS_PD_EN (1 << 3)
+#define MEAS_TSIX_EN (1 << 4)
+#define MEAS_TSIY_EN (1 << 5)
+#define MEAS_TSIZ1_EN (1 << 6)
+#define MEAS_TSIZ2_EN (1 << 7)
+
+struct pm860x_touch {
+ struct input_dev *idev;
+ struct i2c_client *i2c;
+ struct pm860x_chip *chip;
+ int irq;
+ int res_x; /* resistor of Xplate */
+};
+
+static irqreturn_t pm860x_touch_handler(int irq, void *data)
+{
+ struct pm860x_touch *touch = data;
+ struct pm860x_chip *chip = touch->chip;
+ unsigned char buf[MEAS_LEN];
+ int x, y, pen_down;
+ int z1, z2, rt = 0;
+ int ret;
+
+ ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf);
+ if (ret < 0)
+ goto out;
+
+ pen_down = buf[1] & (1 << 6);
+ x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F);
+ y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F);
+ z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F);
+ z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F);
+
+ if (pen_down) {
+ if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) {
+ rt = z2 / z1 - 1;
+ rt = (rt * touch->res_x * x) >> ACCURATE_BIT;
+ dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n",
+ z1, z2, rt);
+ }
+ input_report_abs(touch->idev, ABS_X, x);
+ input_report_abs(touch->idev, ABS_Y, y);
+ input_report_abs(touch->idev, ABS_PRESSURE, rt);
+ input_report_key(touch->idev, BTN_TOUCH, 1);
+ dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y);
+ } else {
+ input_report_abs(touch->idev, ABS_PRESSURE, 0);
+ input_report_key(touch->idev, BTN_TOUCH, 0);
+ dev_dbg(chip->dev, "pen release\n");
+ }
+ input_sync(touch->idev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int pm860x_touch_open(struct input_dev *dev)
+{
+ struct pm860x_touch *touch = input_get_drvdata(dev);
+ int data, ret;
+
+ data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
+ | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
+ ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data);
+ if (ret < 0)
+ goto out;
+ return 0;
+out:
+ return ret;
+}
+
+static void pm860x_touch_close(struct input_dev *dev)
+{
+ struct pm860x_touch *touch = input_get_drvdata(dev);
+ int data;
+
+ data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
+ | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
+ pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);
+}
+
+static int __devinit pm860x_touch_probe(struct platform_device *pdev)
+{
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm860x_platform_data *pm860x_pdata = \
+ pdev->dev.parent->platform_data;
+ struct pm860x_touch_pdata *pdata = NULL;
+ struct pm860x_touch *touch;
+ int irq, ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ return -EINVAL;
+ }
+
+ if (!pm860x_pdata) {
+ dev_err(&pdev->dev, "platform data is missing\n");
+ return -EINVAL;
+ }
+
+ pdata = pm860x_pdata->touch;
+ if (!pdata) {
+ dev_err(&pdev->dev, "touchscreen data is missing\n");
+ return -EINVAL;
+ }
+
+ touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL);
+ if (touch == NULL)
+ return -ENOMEM;
+ dev_set_drvdata(&pdev->dev, touch);
+
+ touch->idev = input_allocate_device();
+ if (touch->idev == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate input device!\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ touch->idev->name = "88pm860x-touch";
+ touch->idev->phys = "88pm860x/input0";
+ touch->idev->id.bustype = BUS_I2C;
+ touch->idev->dev.parent = &pdev->dev;
+ touch->idev->open = pm860x_touch_open;
+ touch->idev->close = pm860x_touch_close;
+ touch->chip = chip;
+ touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+ touch->irq = irq + chip->irq_base;
+ touch->res_x = pdata->res_x;
+ input_set_drvdata(touch->idev, touch);
+
+ ret = request_threaded_irq(touch->irq, NULL, pm860x_touch_handler,
+ IRQF_ONESHOT, "touch", touch);
+ if (ret < 0)
+ goto out_irq;
+
+ __set_bit(EV_ABS, touch->idev->evbit);
+ __set_bit(ABS_X, touch->idev->absbit);
+ __set_bit(ABS_Y, touch->idev->absbit);
+ __set_bit(ABS_PRESSURE, touch->idev->absbit);
+ __set_bit(EV_SYN, touch->idev->evbit);
+ __set_bit(EV_KEY, touch->idev->evbit);
+ __set_bit(BTN_TOUCH, touch->idev->keybit);
+
+ input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0);
+ input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0);
+ input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT,
+ 0, 0);
+
+ ret = input_register_device(touch->idev);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to register touch!\n");
+ goto out_rg;
+ }
+
+ platform_set_drvdata(pdev, touch);
+ return 0;
+out_rg:
+ free_irq(touch->irq, touch);
+out_irq:
+ input_free_device(touch->idev);
+out:
+ kfree(touch);
+ return ret;
+}
+
+static int __devexit pm860x_touch_remove(struct platform_device *pdev)
+{
+ struct pm860x_touch *touch = platform_get_drvdata(pdev);
+
+ input_unregister_device(touch->idev);
+ free_irq(touch->irq, touch);
+ platform_set_drvdata(pdev, NULL);
+ kfree(touch);
+ return 0;
+}
+
+static struct platform_driver pm860x_touch_driver = {
+ .driver = {
+ .name = "88pm860x-touch",
+ .owner = THIS_MODULE,
+ },
+ .probe = pm860x_touch_probe,
+ .remove = __devexit_p(pm860x_touch_remove),
+};
+module_platform_driver(pm860x_touch_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:88pm860x-touch");
+
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
new file mode 100644
index 00000000..873ca6c3
--- /dev/null
+++ b/drivers/input/touchscreen/Kconfig
@@ -0,0 +1,878 @@
+#
+# Touchscreen driver configuration
+#
+menuconfig INPUT_TOUCHSCREEN
+ bool "Touchscreens"
+ help
+ Say Y here, and a list of supported touchscreens will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_TOUCHSCREEN
+
+config TOUCHSCREEN_88PM860X
+ tristate "Marvell 88PM860x touchscreen"
+ depends on MFD_88PM860X
+ help
+ Say Y here if you have a 88PM860x PMIC and want to enable
+ support for the built-in touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called 88pm860x-ts.
+
+config TOUCHSCREEN_ADS7846
+ tristate "ADS7846/TSC2046/AD7873 and AD(S)7843 based touchscreens"
+ depends on SPI_MASTER
+ depends on HWMON = n || HWMON
+ help
+ Say Y here if you have a touchscreen interface using the
+ ADS7846/TSC2046/AD7873 or ADS7843/AD7843 controller,
+ and your board-specific setup code includes that in its
+ table of SPI devices.
+
+ If HWMON is selected, and the driver is told the reference voltage
+ on your board, you will also get hwmon interfaces for the voltage
+ (and on ads7846/tsc2046/ad7873, temperature) sensors of this chip.
+
+ 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 ads7846.
+
+config TOUCHSCREEN_AD7877
+ tristate "AD7877 based touchscreens"
+ depends on SPI_MASTER
+ help
+ Say Y here if you have a touchscreen interface using the
+ AD7877 controller, and your board-specific initialization
+ code includes that in its table of SPI devices.
+
+ 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 ad7877.
+
+config TOUCHSCREEN_AD7879
+ tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface"
+ help
+ Say Y here if you want to support a touchscreen interface using
+ the AD7879-1/AD7889-1 controller.
+
+ 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 "support SPI bus connection"
+ depends on TOUCHSCREEN_AD7879 && SPI_MASTER
+ help
+ 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-spi.
+
+config TOUCHSCREEN_ATMEL_MXT
+ tristate "Atmel mXT I2C Touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have Atmel mXT series I2C touchscreen,
+ such as AT42QT602240/ATMXT224, connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atmel_mxt_ts.
+
+config TOUCHSCREEN_AUO_PIXCIR
+ tristate "AUO in-cell touchscreen using Pixcir ICs"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Say Y here if you have a AUO display with in-cell touchscreen
+ using Pixcir ICs.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called auo-pixcir-ts.
+
+config TOUCHSCREEN_BITSY
+ tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
+ depends on SA1100_BITSY
+ select SERIO
+ help
+ Say Y here if you have the h3600 (Bitsy) touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called h3600_ts_input.
+
+config TOUCHSCREEN_BU21013
+ tristate "BU21013 based touch panel controllers"
+ depends on I2C
+ help
+ Say Y here if you have a bu21013 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bu21013_ts.
+
+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_CYTTSP_CORE
+ tristate "Cypress TTSP touchscreen"
+ help
+ Say Y here if you have a touchscreen using controller from
+ the Cypress TrueTouch(tm) Standard Product family connected
+ to your system. You will also need to select appropriate
+ bus connection below.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_core.
+
+config TOUCHSCREEN_CYTTSP_I2C
+ tristate "support I2C bus connection"
+ depends on TOUCHSCREEN_CYTTSP_CORE && I2C
+ help
+ Say Y here if the touchscreen is connected via I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_i2c.
+
+config TOUCHSCREEN_CYTTSP_SPI
+ tristate "support SPI bus connection"
+ depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER
+ help
+ Say Y here if the touchscreen is connected via SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_spi.
+
+config TOUCHSCREEN_DA9034
+ tristate "Touchscreen support for Dialog Semiconductor DA9034"
+ depends on PMIC_DA903X
+ default y
+ help
+ Say Y here to enable the support for the touchscreen found
+ on Dialog Semiconductor DA9034 PMIC.
+
+config TOUCHSCREEN_DYNAPRO
+ tristate "Dynapro serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Dynapro serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dynapro.
+
+config TOUCHSCREEN_HAMPSHIRE
+ tristate "Hampshire serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Hampshire serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hampshire.
+
+config TOUCHSCREEN_EETI
+ tristate "EETI touchscreen panel support"
+ depends on I2C
+ help
+ Say Y here to enable support for I2C connected EETI touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called eeti_ts.
+
+config TOUCHSCREEN_EGALAX
+ tristate "EETI eGalax multi-touch panel support"
+ depends on I2C
+ help
+ Say Y here to enable support for I2C connected EETI
+ eGalax multi-touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called egalax_ts.
+
+config TOUCHSCREEN_FUJITSU
+ tristate "Fujitsu serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have the Fujitsu touchscreen (such as one
+ installed in Lifebook P series laptop) connected to your
+ system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fujitsu-ts.
+
+config TOUCHSCREEN_ILI210X
+ tristate "Ilitek ILI210X based touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a ILI210X based touchscreen
+ controller. This driver supports models ILI2102,
+ ILI2102s, ILI2103, ILI2103s and ILI2105.
+ Such kind of chipsets can be found in Amazon Kindle Fire
+ touchscreens.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ili210x.
+
+config TOUCHSCREEN_S3C2410
+ tristate "Samsung S3C2410/generic touchscreen input driver"
+ depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
+ select S3C_ADC
+ help
+ Say Y here if you have the s3c2410 touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s3c2410_ts.
+
+config TOUCHSCREEN_GUNZE
+ tristate "Gunze AHL-51S touchscreen"
+ select SERIO
+ help
+ Say Y here if you have the Gunze AHL-51 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gunze.
+
+config TOUCHSCREEN_ELO
+ tristate "Elo serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have an Elo serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called elo.
+
+config TOUCHSCREEN_WACOM_W8001
+ tristate "Wacom W8001 penabled serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have an Wacom W8001 penabled serial touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wacom_w8001.
+
+config TOUCHSCREEN_LPC32XX
+ tristate "LPC32XX touchscreen controller"
+ depends on ARCH_LPC32XX
+ help
+ Say Y here if you have a LPC32XX device and want
+ to support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lpc32xx_ts.
+
+config TOUCHSCREEN_MAX11801
+ tristate "MAX11801 based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a MAX11801 based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max11801_ts.
+
+config TOUCHSCREEN_MCS5000
+ tristate "MELFAS MCS-5000 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the MELFAS MCS-5000 touchscreen controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mcs5000_ts.
+
+config TOUCHSCREEN_MTOUCH
+ tristate "MicroTouch serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have a MicroTouch (3M) serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mtouch.
+
+config TOUCHSCREEN_INEXIO
+ tristate "iNexio serial touchscreens"
+ select SERIO
+ help
+ Say Y here if you have an iNexio serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called inexio.
+
+config TOUCHSCREEN_INTEL_MID
+ tristate "Intel MID platform resistive touchscreen"
+ depends on INTEL_SCU_IPC
+ help
+ Say Y here if you have a Intel MID based touchscreen in
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called intel_mid_touch.
+
+config TOUCHSCREEN_MK712
+ tristate "ICS MicroClock MK712 touchscreen"
+ help
+ Say Y here if you have the ICS MicroClock MK712 touchscreen
+ controller chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mk712.
+
+config TOUCHSCREEN_HP600
+ tristate "HP Jornada 6xx touchscreen"
+ depends on SH_HP6XX && SH_ADC
+ help
+ Say Y here if you have a HP Jornada 620/660/680/690 and want to
+ support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hp680_ts_input.
+
+config TOUCHSCREEN_HP7XX
+ tristate "HP Jornada 7xx touchscreen"
+ depends on SA1100_JORNADA720_SSP
+ help
+ Say Y here if you have a HP Jornada 710/720/728 and want
+ to support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called jornada720_ts.
+
+config TOUCHSCREEN_HTCPEN
+ tristate "HTC Shift X9500 touchscreen"
+ depends on ISA
+ help
+ Say Y here if you have an HTC Shift UMPC also known as HTC X9500
+ Clio / Shangrila and want to support the built-in touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called htcpen.
+
+config TOUCHSCREEN_PENMOUNT
+ tristate "Penmount serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Penmount serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called penmount.
+
+config TOUCHSCREEN_MIGOR
+ tristate "Renesas MIGO-R touchscreen"
+ depends on SH_MIGOR && I2C
+ help
+ Say Y here to enable MIGO-R touchscreen support.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called migor_ts.
+
+config TOUCHSCREEN_TNETV107X
+ tristate "TI TNETV107X touchscreen support"
+ depends on ARCH_DAVINCI_TNETV107X
+ help
+ Say Y here if you want to use the TNETV107X touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tnetv107x-ts.
+
+config TOUCHSCREEN_SYNAPTICS_I2C_RMI
+ tristate "Synaptics i2c touchscreen"
+ depends on I2C
+ help
+ This enables support for Synaptics RMI over I2C based touchscreens.
+
+config TOUCHSCREEN_TOUCHRIGHT
+ tristate "Touchright serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchright serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchright.
+
+config TOUCHSCREEN_TOUCHWIN
+ tristate "Touchwin serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchwin serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchwin.
+
+config TOUCHSCREEN_TI_TSCADC
+ tristate "TI Touchscreen Interface"
+ depends on ARCH_OMAP2PLUS
+ help
+ Say Y here if you have 4/5/8 wire touchscreen controller
+ to be connected to the ADC controller on your TI AM335x SoC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ti_tscadc.
+
+config TOUCHSCREEN_ATMEL_TSADCC
+ tristate "Atmel Touchscreen Interface"
+ depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
+ help
+ Say Y here if you have a 4-wire touchscreen connected to the
+ ADC Controller on your Atmel SoC (such as the AT91SAM9RL).
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atmel_tsadcc.
+
+config TOUCHSCREEN_UCB1400
+ tristate "Philips UCB1400 touchscreen"
+ depends on AC97_BUS
+ depends on UCB1400_CORE
+ help
+ This enables support for the Philips UCB1400 touchscreen interface.
+ The UCB1400 is an AC97 audio codec. The touchscreen interface
+ will be initialized only after the ALSA subsystem has been
+ brought up and the UCB1400 detected. You therefore have to
+ configure ALSA support as well (either built-in or modular,
+ independently of whether this driver is itself built-in or
+ modular) for this driver to work.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ucb1400_ts.
+
+config TOUCHSCREEN_PIXCIR
+ tristate "PIXCIR I2C touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a pixcir i2c touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pixcir_i2c_ts.
+
+config TOUCHSCREEN_WM831X
+ tristate "Support for WM831x touchscreen controllers"
+ depends on MFD_WM831X
+ help
+ This enables support for the touchscreen controller on the WM831x
+ series of PMICs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm831x-ts.
+
+config TOUCHSCREEN_WM97XX
+ tristate "Support for WM97xx AC97 touchscreen controllers"
+ depends on AC97_BUS
+ help
+ Say Y here if you have a Wolfson Microelectronics WM97xx
+ touchscreen connected to your system. Note that this option
+ only enables core driver, you will also need to select
+ support for appropriate chip below.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm97xx-ts.
+
+config TOUCHSCREEN_WM9705
+ bool "WM9705 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9705 touchscreen controller.
+
+config TOUCHSCREEN_WM9712
+ bool "WM9712 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9712 touchscreen controller.
+
+config TOUCHSCREEN_WM9713
+ bool "WM9713 Touchscreen interface support"
+ depends on TOUCHSCREEN_WM97XX
+ default y
+ help
+ Say Y here to enable support for the Wolfson Microelectronics
+ WM9713 touchscreen controller.
+
+config TOUCHSCREEN_WM97XX_ATMEL
+ tristate "WM97xx Atmel accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91)
+ help
+ Say Y here for support for streaming mode with WM97xx touchscreens
+ on Atmel AT91 or AVR32 systems with an AC97C module.
+
+ Be aware that this will use channel B in the controller for
+ streaming data, this must not conflict with other AC97C drivers.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will
+ be called atmel-wm97xx.
+
+config TOUCHSCREEN_WM97XX_MAINSTONE
+ tristate "WM97xx Mainstone/Palm accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && ARCH_PXA
+ help
+ Say Y here for support for streaming mode with WM97xx touchscreens
+ on Mainstone, Palm Tungsten T5, TX and LifeDrive systems.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mainstone-wm97xx.
+
+config TOUCHSCREEN_WM97XX_ZYLONITE
+ tristate "Zylonite accelerated touch"
+ depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE
+ select TOUCHSCREEN_WM9713
+ help
+ Say Y here for support for streaming mode with the touchscreen
+ on Zylonite systems.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zylonite-wm97xx.
+
+config TOUCHSCREEN_USB_COMPOSITE
+ tristate "USB Touchscreen Driver"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ USB Touchscreen driver for:
+ - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
+ - PanJit TouchSet USB
+ - 3M MicroTouch USB (EX II series)
+ - ITM
+ - some other eTurboTouch
+ - Gunze AHL61
+ - DMC TSC-10/25
+ - IRTOUCHSYSTEMS/UNITOP
+ - IdealTEK URTC1000
+ - GoTop Super_Q2/GogoPen/PenPower tablets
+ - JASTEC USB Touch Controller/DigiTech DTR-02U
+ - Zytronic controllers
+ - Elo TouchSystems 2700 IntelliTouch
+ - EasyTouch USB Touch Controller from Data Modul
+
+ Have a look at <http://linux.chapter7.ch/touchkit/> for
+ a usage description and the required user-space stuff.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbtouchscreen.
+
+config TOUCHSCREEN_MC13783
+ tristate "Freescale MC13783 touchscreen input driver"
+ depends on MFD_MC13783
+ help
+ Say Y here if you have an Freescale MC13783 PMIC on your
+ board and want to use its touchscreen
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mc13783_ts.
+
+config TOUCHSCREEN_USB_EGALAX
+ default y
+ bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_PANJIT
+ default y
+ bool "PanJit device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_3M
+ default y
+ bool "3M/Microtouch EX II series device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ITM
+ default y
+ bool "ITM device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETURBO
+ default y
+ bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GUNZE
+ default y
+ bool "Gunze AHL61 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_DMC_TSC10
+ default y
+ bool "DMC TSC-10/25 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IRTOUCH
+ default y
+ bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IDEALTEK
+ default y
+ bool "IdealTEK URTC1000 device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GENERAL_TOUCH
+ default y
+ bool "GeneralTouch Touchscreen device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GOTOP
+ default y
+ bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_JASTEC
+ default y
+ bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ELO
+ default y
+ bool "Elo TouchSystems 2700 IntelliTouch controller device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_E2I
+ default y
+ bool "e2i Touchscreen controller (e.g. from Mimo 740)"
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ZYTRONIC
+ default y
+ bool "Zytronic controller" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETT_TC45USB
+ default y
+ bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_NEXIO
+ default y
+ bool "NEXIO/iNexio device support" if EXPERT
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_EASYTOUCH
+ default y
+ bool "EasyTouch USB Touch controller device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+ help
+ Say Y here if you have a EasyTouch USB Touch controller device support.
+ If unsure, say N.
+
+config TOUCHSCREEN_TOUCHIT213
+ tristate "Sahara TouchIT-213 touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Sahara TouchIT-213 Tablet PC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchit213.
+
+config TOUCHSCREEN_TSC_SERIO
+ tristate "TSC-10/25/40 serial touchscreen support"
+ select SERIO
+ help
+ Say Y here if you have a TSC-10, 25 or 40 serial touchscreen connected
+ to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc40.
+
+config TOUCHSCREEN_TSC2005
+ tristate "TSC2005 based touchscreens"
+ depends on SPI_MASTER && GENERIC_HARDIRQS
+ help
+ Say Y here if you have a TSC2005 based touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc2005.
+
+config TOUCHSCREEN_TSC2007
+ tristate "TSC2007 based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TSC2007 based touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc2007.
+
+config TOUCHSCREEN_W90X900
+ tristate "W90P910 touchscreen driver"
+ depends on HAVE_CLK
+ help
+ Say Y here if you have a W90P910 based touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w90p910_ts.
+
+config TOUCHSCREEN_PCAP
+ tristate "Motorola PCAP touchscreen"
+ depends on EZX_PCAP
+ help
+ Say Y here if you have a Motorola EZX telephone and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcap_ts.
+
+config TOUCHSCREEN_ST1232
+ tristate "Sitronix ST1232 touchscreen controllers"
+ depends on I2C
+ help
+ Say Y here if you want to support Sitronix ST1232
+ touchscreen controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st1232_ts.
+
+config TOUCHSCREEN_STMPE
+ tristate "STMicroelectronics STMPE touchscreens"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want support for STMicroelectronics
+ STMPE touchscreen controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmpe-ts.
+
+config TOUCHSCREEN_TPS6507X
+ tristate "TPS6507x based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TPS6507x based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tps6507x_ts.
+
+config TOUCHSCREEN_VT1609
+ tristate "Vt1609 Dual-touch Support,Compatible with Vt1603a Single-touch"
+ default y
+ depends on ARCH_WMT
+ help
+ Say Y here if you have a vt1603a single or vt1609 dual touchscreen and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vt1609_dual_ts.
+
+source "drivers/input/touchscreen/gsl1680_ts/Kconfig"
+source "drivers/input/touchscreen/sitronix/Kconfig"
+source "drivers/input/touchscreen/zet6221_ts/Kconfig"
+source "drivers/input/touchscreen/cyp140_ts/Kconfig"
+source "drivers/input/touchscreen/ft5x0x/Kconfig"
+source "drivers/input/touchscreen/ft6x0x/Kconfig"
+source "drivers/input/touchscreen/aw5306_ts/Kconfig"
+source "drivers/input/touchscreen/ssd253x_ts/Kconfig"
+source "drivers/input/touchscreen/lw86x0_ts/Kconfig"
+source "drivers/input/touchscreen/gt9xx_ts/Kconfig"
+source "drivers/input/touchscreen/icn83xx_ts/Kconfig"
+source "drivers/input/touchscreen/icn85xx_ts/Kconfig"
+source "drivers/input/touchscreen/sis_usbhid_ts/Kconfig"
+endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
new file mode 100644
index 00000000..b632eb48
--- /dev/null
+++ b/drivers/input/touchscreen/Makefile
@@ -0,0 +1,88 @@
+#
+# Makefile for the touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+
+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_MXT) += atmel_mxt_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
+obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
+obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.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
+obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
+obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
+obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
+obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
+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_PIXCIR) += pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
+obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o
+obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
+obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
+obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
+
+obj-$(CONFIG_TOUCHSCREEN_VT1609) += vt1609_ts/
+#GSL1680 touchscreen
+obj-$(CONFIG_TOUCHSCREEN_GSL1680) += gsl1680_ts/
+obj-$(CONFIG_TOUCHSCREEN_SITRONIX) += sitronix/
+obj-$(CONFIG_TOUCHSCREEN_ZET6221) += zet6221_ts/
+obj-$(CONFIG_TOUCHSCREEN_CYP140) += cyp140_ts/
+obj-$(CONFIG_TOUCHSCREEN_FT5X0X) += ft5x0x/
+obj-$(CONFIG_TOUCHSCREEN_FT6X0X) += ft6x0x/
+obj-$(CONFIG_TOUCHSCREEN_AW5306) += aw5306_ts/
+obj-$(CONFIG_TOUCHSCREEN_SSD253X) += ssd253x_ts/
+obj-$(CONFIG_TOUCHSCREEN_LW86X0) += lw86x0_ts/
+obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx_ts/
+obj-$(CONFIG_TOUCHSCREEN_ICN83XX) += icn83xx_ts/
+obj-$(CONFIG_TOUCHSCREEN_ICN85XX) += icn85xx_ts/
+obj-$(CONFIG_TOUCHSCREEN_SIS) += sis_usbhid_ts/
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
new file mode 100644
index 00000000..2c769210
--- /dev/null
+++ b/drivers/input/touchscreen/ad7877.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (C) 2006-2008 Michael Hennerich, Analog Devices Inc.
+ *
+ * Description: AD7877 based touchscreen, sensor (ADCs), DAC and GPIO driver
+ * Based on: ads7846.c
+ *
+ * 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
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ */
+
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ad7877.h>
+#include <linux/module.h>
+#include <asm/irq.h>
+
+#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(100)
+
+#define MAX_SPI_FREQ_HZ 20000000
+#define MAX_12BIT ((1<<12)-1)
+
+#define AD7877_REG_ZEROS 0
+#define AD7877_REG_CTRL1 1
+#define AD7877_REG_CTRL2 2
+#define AD7877_REG_ALERT 3
+#define AD7877_REG_AUX1HIGH 4
+#define AD7877_REG_AUX1LOW 5
+#define AD7877_REG_BAT1HIGH 6
+#define AD7877_REG_BAT1LOW 7
+#define AD7877_REG_BAT2HIGH 8
+#define AD7877_REG_BAT2LOW 9
+#define AD7877_REG_TEMP1HIGH 10
+#define AD7877_REG_TEMP1LOW 11
+#define AD7877_REG_SEQ0 12
+#define AD7877_REG_SEQ1 13
+#define AD7877_REG_DAC 14
+#define AD7877_REG_NONE1 15
+#define AD7877_REG_EXTWRITE 15
+#define AD7877_REG_XPLUS 16
+#define AD7877_REG_YPLUS 17
+#define AD7877_REG_Z2 18
+#define AD7877_REG_aux1 19
+#define AD7877_REG_aux2 20
+#define AD7877_REG_aux3 21
+#define AD7877_REG_bat1 22
+#define AD7877_REG_bat2 23
+#define AD7877_REG_temp1 24
+#define AD7877_REG_temp2 25
+#define AD7877_REG_Z1 26
+#define AD7877_REG_GPIOCTRL1 27
+#define AD7877_REG_GPIOCTRL2 28
+#define AD7877_REG_GPIODATA 29
+#define AD7877_REG_NONE2 30
+#define AD7877_REG_NONE3 31
+
+#define AD7877_SEQ_YPLUS_BIT (1<<11)
+#define AD7877_SEQ_XPLUS_BIT (1<<10)
+#define AD7877_SEQ_Z2_BIT (1<<9)
+#define AD7877_SEQ_AUX1_BIT (1<<8)
+#define AD7877_SEQ_AUX2_BIT (1<<7)
+#define AD7877_SEQ_AUX3_BIT (1<<6)
+#define AD7877_SEQ_BAT1_BIT (1<<5)
+#define AD7877_SEQ_BAT2_BIT (1<<4)
+#define AD7877_SEQ_TEMP1_BIT (1<<3)
+#define AD7877_SEQ_TEMP2_BIT (1<<2)
+#define AD7877_SEQ_Z1_BIT (1<<1)
+
+enum {
+ AD7877_SEQ_YPOS = 0,
+ AD7877_SEQ_XPOS = 1,
+ AD7877_SEQ_Z2 = 2,
+ AD7877_SEQ_AUX1 = 3,
+ AD7877_SEQ_AUX2 = 4,
+ AD7877_SEQ_AUX3 = 5,
+ AD7877_SEQ_BAT1 = 6,
+ AD7877_SEQ_BAT2 = 7,
+ AD7877_SEQ_TEMP1 = 8,
+ AD7877_SEQ_TEMP2 = 9,
+ AD7877_SEQ_Z1 = 10,
+ AD7877_NR_SENSE = 11,
+};
+
+/* DAC Register Default RANGE 0 to Vcc, Volatge Mode, DAC On */
+#define AD7877_DAC_CONF 0x1
+
+/* If gpio3 is set AUX3/GPIO3 acts as GPIO Output */
+#define AD7877_EXTW_GPIO_3_CONF 0x1C4
+#define AD7877_EXTW_GPIO_DATA 0x200
+
+/* Control REG 2 */
+#define AD7877_TMR(x) ((x & 0x3) << 0)
+#define AD7877_REF(x) ((x & 0x1) << 2)
+#define AD7877_POL(x) ((x & 0x1) << 3)
+#define AD7877_FCD(x) ((x & 0x3) << 4)
+#define AD7877_PM(x) ((x & 0x3) << 6)
+#define AD7877_ACQ(x) ((x & 0x3) << 8)
+#define AD7877_AVG(x) ((x & 0x3) << 10)
+
+/* Control REG 1 */
+#define AD7877_SER (1 << 11) /* non-differential */
+#define AD7877_DFR (0 << 11) /* differential */
+
+#define AD7877_MODE_NOC (0) /* Do not convert */
+#define AD7877_MODE_SCC (1) /* Single channel conversion */
+#define AD7877_MODE_SEQ0 (2) /* Sequence 0 in Slave Mode */
+#define AD7877_MODE_SEQ1 (3) /* Sequence 1 in Master Mode */
+
+#define AD7877_CHANADD(x) ((x&0xF)<<7)
+#define AD7877_READADD(x) ((x)<<2)
+#define AD7877_WRITEADD(x) ((x)<<12)
+
+#define AD7877_READ_CHAN(x) (AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_SER | \
+ AD7877_MODE_SCC | AD7877_CHANADD(AD7877_REG_ ## x) | \
+ AD7877_READADD(AD7877_REG_ ## x))
+
+#define AD7877_MM_SEQUENCE (AD7877_SEQ_YPLUS_BIT | AD7877_SEQ_XPLUS_BIT | \
+ AD7877_SEQ_Z2_BIT | AD7877_SEQ_Z1_BIT)
+
+/*
+ * Non-touchscreen sensors only use single-ended conversions.
+ */
+
+struct ser_req {
+ u16 reset;
+ u16 ref_on;
+ u16 command;
+ struct spi_message msg;
+ struct spi_transfer xfer[6];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u16 sample ____cacheline_aligned;
+};
+
+struct ad7877 {
+ struct input_dev *input;
+ char phys[32];
+
+ struct spi_device *spi;
+ u16 model;
+ u16 vref_delay_usecs;
+ u16 x_plate_ohms;
+ u16 pressure_max;
+
+ u16 cmd_crtl1;
+ u16 cmd_crtl2;
+ u16 cmd_dummy;
+ u16 dac;
+
+ u8 stopacq_polarity;
+ u8 first_conversion_delay;
+ u8 acquisition_time;
+ u8 averaging;
+ u8 pen_down_acc_interval;
+
+ struct spi_transfer xfer[AD7877_NR_SENSE + 2];
+ struct spi_message msg;
+
+ struct mutex mutex;
+ bool disabled; /* P: mutex */
+ bool gpio3; /* P: mutex */
+ bool gpio4; /* P: mutex */
+
+ spinlock_t lock;
+ struct timer_list timer; /* P: lock */
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned;
+};
+
+static bool gpio3;
+module_param(gpio3, bool, 0);
+MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
+
+/*
+ * ad7877_read/write are only used for initial setup and for sysfs controls.
+ * The main traffic is done using spi_async() in the interrupt handler.
+ */
+
+static int ad7877_read(struct spi_device *spi, u16 reg)
+{
+ struct ser_req *req;
+ int status, ret;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ req->command = (u16) (AD7877_WRITEADD(AD7877_REG_CTRL1) |
+ AD7877_READADD(reg));
+ req->xfer[0].tx_buf = &req->command;
+ req->xfer[0].len = 2;
+ req->xfer[0].cs_change = 1;
+
+ req->xfer[1].rx_buf = &req->sample;
+ 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->sample;
+
+ kfree(req);
+
+ return ret;
+}
+
+static int ad7877_write(struct spi_device *spi, u16 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) (AD7877_WRITEADD(reg) | (val & MAX_12BIT));
+ req->xfer[0].tx_buf = &req->command;
+ req->xfer[0].len = 2;
+
+ spi_message_add_tail(&req->xfer[0], &req->msg);
+
+ status = spi_sync(spi, &req->msg);
+
+ kfree(req);
+
+ return status;
+}
+
+static int ad7877_read_adc(struct spi_device *spi, unsigned command)
+{
+ struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+ struct ser_req *req;
+ int status;
+ int sample;
+ int i;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ /* activate reference, so it has time to settle; */
+ req->ref_on = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+ AD7877_POL(ts->stopacq_polarity) |
+ AD7877_AVG(0) | AD7877_PM(2) | AD7877_TMR(0) |
+ AD7877_ACQ(ts->acquisition_time) | AD7877_FCD(0);
+
+ req->reset = AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_MODE_NOC;
+
+ req->command = (u16) command;
+
+ req->xfer[0].tx_buf = &req->reset;
+ req->xfer[0].len = 2;
+ req->xfer[0].cs_change = 1;
+
+ req->xfer[1].tx_buf = &req->ref_on;
+ req->xfer[1].len = 2;
+ req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+ req->xfer[1].cs_change = 1;
+
+ req->xfer[2].tx_buf = &req->command;
+ req->xfer[2].len = 2;
+ req->xfer[2].delay_usecs = ts->vref_delay_usecs;
+ req->xfer[2].cs_change = 1;
+
+ req->xfer[3].rx_buf = &req->sample;
+ req->xfer[3].len = 2;
+ req->xfer[3].cs_change = 1;
+
+ req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/
+ req->xfer[4].len = 2;
+ req->xfer[4].cs_change = 1;
+
+ req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/
+ req->xfer[5].len = 2;
+
+ /* group all the transfers together, so we can't interfere with
+ * reading touchscreen state; disable penirq while sampling
+ */
+ for (i = 0; i < 6; i++)
+ spi_message_add_tail(&req->xfer[i], &req->msg);
+
+ status = spi_sync(spi, &req->msg);
+ sample = req->sample;
+
+ kfree(req);
+
+ return status ? : sample;
+}
+
+static int ad7877_process_data(struct ad7877 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+ unsigned Rt;
+ u16 x, y, z1, z2;
+
+ x = ts->conversion_data[AD7877_SEQ_XPOS] & MAX_12BIT;
+ y = ts->conversion_data[AD7877_SEQ_YPOS] & MAX_12BIT;
+ z1 = ts->conversion_data[AD7877_SEQ_Z1] & MAX_12BIT;
+ z2 = ts->conversion_data[AD7877_SEQ_Z2] & MAX_12BIT;
+
+ /*
+ * The samples processed here are already preprocessed by the AD7877.
+ * The preprocessing function consists of an averaging filter.
+ * The combination of 'first conversion delay' and averaging provides a robust solution,
+ * discarding the spurious noise in the signal and keeping only the data of interest.
+ * The size of the averaging filter is programmable. (dev.platform_data, see linux/spi/ad7877.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)) {
+ /* compute touch pressure resistance using equation #1 */
+ Rt = (z2 - z1) * x * ts->x_plate_ohms;
+ Rt /= z1;
+ Rt = (Rt + 2047) >> 12;
+
+ /*
+ * Sample found inconsistent, pressure is beyond
+ * the maximum. Don't report it to user space.
+ */
+ if (Rt > ts->pressure_max)
+ return -EINVAL;
+
+ 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 inline void ad7877_ts_event_release(struct ad7877 *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);
+}
+
+static void ad7877_timer(unsigned long handle)
+{
+ struct ad7877 *ts = (void *)handle;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+ ad7877_ts_event_release(ts);
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static irqreturn_t ad7877_irq(int irq, void *handle)
+{
+ struct ad7877 *ts = handle;
+ unsigned long flags;
+ int error;
+
+ error = spi_sync(ts->spi, &ts->msg);
+ if (error) {
+ dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
+ goto out;
+ }
+
+ spin_lock_irqsave(&ts->lock, flags);
+ error = ad7877_process_data(ts);
+ if (!error)
+ mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static void ad7877_disable(struct ad7877 *ts)
+{
+ mutex_lock(&ts->mutex);
+
+ if (!ts->disabled) {
+ ts->disabled = true;
+ disable_irq(ts->spi->irq);
+
+ if (del_timer_sync(&ts->timer))
+ ad7877_ts_event_release(ts);
+ }
+
+ /*
+ * We know the chip's in lowpower mode since we always
+ * leave it that way after every request
+ */
+
+ mutex_unlock(&ts->mutex);
+}
+
+static void ad7877_enable(struct ad7877 *ts)
+{
+ mutex_lock(&ts->mutex);
+
+ if (ts->disabled) {
+ ts->disabled = false;
+ enable_irq(ts->spi->irq);
+ }
+
+ mutex_unlock(&ts->mutex);
+}
+
+#define SHOW(name) static ssize_t \
+name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct ad7877 *ts = dev_get_drvdata(dev); \
+ ssize_t v = ad7877_read_adc(ts->spi, \
+ AD7877_READ_CHAN(name)); \
+ if (v < 0) \
+ return v; \
+ return sprintf(buf, "%u\n", (unsigned) v); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
+
+SHOW(aux1)
+SHOW(aux2)
+SHOW(aux3)
+SHOW(bat1)
+SHOW(bat2)
+SHOW(temp1)
+SHOW(temp2)
+
+static ssize_t ad7877_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7877_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ if (val)
+ ad7877_disable(ts);
+ else
+ ad7877_enable(ts);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
+
+static ssize_t ad7877_dac_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->dac);
+}
+
+static ssize_t ad7877_dac_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ts->mutex);
+ ts->dac = val & 0xFF;
+ ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF);
+ mutex_unlock(&ts->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
+
+static ssize_t ad7877_gpio3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->gpio3);
+}
+
+static ssize_t ad7877_gpio3_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ts->mutex);
+ ts->gpio3 = !!val;
+ ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+ (ts->gpio4 << 4) | (ts->gpio3 << 5));
+ mutex_unlock(&ts->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
+
+static ssize_t ad7877_gpio4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->gpio4);
+}
+
+static ssize_t ad7877_gpio4_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ts->mutex);
+ ts->gpio4 = !!val;
+ ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+ (ts->gpio4 << 4) | (ts->gpio3 << 5));
+ mutex_unlock(&ts->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(gpio4, 0664, ad7877_gpio4_show, ad7877_gpio4_store);
+
+static struct attribute *ad7877_attributes[] = {
+ &dev_attr_temp1.attr,
+ &dev_attr_temp2.attr,
+ &dev_attr_aux1.attr,
+ &dev_attr_aux2.attr,
+ &dev_attr_aux3.attr,
+ &dev_attr_bat1.attr,
+ &dev_attr_bat2.attr,
+ &dev_attr_disable.attr,
+ &dev_attr_dac.attr,
+ &dev_attr_gpio3.attr,
+ &dev_attr_gpio4.attr,
+ NULL
+};
+
+static umode_t ad7877_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_aux3.attr) {
+ if (gpio3)
+ mode = 0;
+ } else if (attr == &dev_attr_gpio3.attr) {
+ if (!gpio3)
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static const struct attribute_group ad7877_attr_group = {
+ .is_visible = ad7877_attr_is_visible,
+ .attrs = ad7877_attributes,
+};
+
+static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
+{
+ struct spi_message *m;
+ int i;
+
+ ts->cmd_crtl2 = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+ AD7877_POL(ts->stopacq_polarity) |
+ AD7877_AVG(ts->averaging) | AD7877_PM(1) |
+ AD7877_TMR(ts->pen_down_acc_interval) |
+ AD7877_ACQ(ts->acquisition_time) |
+ AD7877_FCD(ts->first_conversion_delay);
+
+ ad7877_write(spi, AD7877_REG_CTRL2, ts->cmd_crtl2);
+
+ ts->cmd_crtl1 = AD7877_WRITEADD(AD7877_REG_CTRL1) |
+ AD7877_READADD(AD7877_REG_XPLUS-1) |
+ AD7877_MODE_SEQ1 | AD7877_DFR;
+
+ ad7877_write(spi, AD7877_REG_CTRL1, ts->cmd_crtl1);
+
+ ts->cmd_dummy = 0;
+
+ m = &ts->msg;
+
+ spi_message_init(m);
+
+ m->context = ts;
+
+ ts->xfer[0].tx_buf = &ts->cmd_crtl1;
+ ts->xfer[0].len = 2;
+ ts->xfer[0].cs_change = 1;
+
+ spi_message_add_tail(&ts->xfer[0], m);
+
+ ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
+ ts->xfer[1].len = 2;
+ ts->xfer[1].cs_change = 1;
+
+ spi_message_add_tail(&ts->xfer[1], m);
+
+ for (i = 0; i < AD7877_NR_SENSE; i++) {
+ ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
+ ts->xfer[i + 2].len = 2;
+ if (i < (AD7877_NR_SENSE - 1))
+ ts->xfer[i + 2].cs_change = 1;
+ spi_message_add_tail(&ts->xfer[i + 2], m);
+ }
+}
+
+static int __devinit ad7877_probe(struct spi_device *spi)
+{
+ struct ad7877 *ts;
+ struct input_dev *input_dev;
+ struct ad7877_platform_data *pdata = spi->dev.platform_data;
+ int err;
+ u16 verify;
+
+ if (!spi->irq) {
+ dev_dbg(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ /* don't exceed max specified SPI CLK frequency */
+ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+ dev_dbg(&spi->dev, "SPI CLK %d Hz?\n",spi->max_speed_hz);
+ 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 = kzalloc(sizeof(struct ad7877), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ dev_set_drvdata(&spi->dev, ts);
+ ts->spi = spi;
+ ts->input = input_dev;
+
+ setup_timer(&ts->timer, ad7877_timer, (unsigned long) ts);
+ mutex_init(&ts->mutex);
+ spin_lock_init(&ts->lock);
+
+ ts->model = pdata->model ? : 7877;
+ ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+ ts->pressure_max = pdata->pressure_max ? : ~0;
+
+ ts->stopacq_polarity = pdata->stopacq_polarity;
+ ts->first_conversion_delay = pdata->first_conversion_delay;
+ ts->acquisition_time = pdata->acquisition_time;
+ ts->averaging = pdata->averaging;
+ ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+
+ input_dev->name = "AD7877 Touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->dev.parent = &spi->dev;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_PRESSURE, input_dev->absbit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ pdata->x_min ? : 0,
+ pdata->x_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+ ad7877_write(spi, AD7877_REG_SEQ1, AD7877_MM_SEQUENCE);
+
+ verify = ad7877_read(spi, AD7877_REG_SEQ1);
+
+ if (verify != AD7877_MM_SEQUENCE){
+ dev_err(&spi->dev, "%s: Failed to probe %s\n",
+ dev_name(&spi->dev), input_dev->name);
+ err = -ENODEV;
+ goto err_free_mem;
+ }
+
+ if (gpio3)
+ ad7877_write(spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_3_CONF);
+
+ ad7877_setup_ts_def_msg(spi, ts);
+
+ /* Request AD7877 /DAV GPIO interrupt */
+
+ err = request_threaded_irq(spi->irq, NULL, ad7877_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ spi->dev.driver->name, ts);
+ if (err) {
+ dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+ goto err_free_mem;
+ }
+
+ err = sysfs_create_group(&spi->dev.kobj, &ad7877_attr_group);
+ if (err)
+ goto err_free_irq;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_attr_group;
+
+ return 0;
+
+err_remove_attr_group:
+ sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
+err_free_irq:
+ free_irq(spi->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ dev_set_drvdata(&spi->dev, NULL);
+ return err;
+}
+
+static int __devexit ad7877_remove(struct spi_device *spi)
+{
+ struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+
+ sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
+
+ ad7877_disable(ts);
+ free_irq(ts->spi->irq, ts);
+
+ input_unregister_device(ts->input);
+ kfree(ts);
+
+ dev_dbg(&spi->dev, "unregistered touchscreen\n");
+ dev_set_drvdata(&spi->dev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ad7877_suspend(struct device *dev)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ ad7877_disable(ts);
+
+ return 0;
+}
+
+static int ad7877_resume(struct device *dev)
+{
+ struct ad7877 *ts = dev_get_drvdata(dev);
+
+ ad7877_enable(ts);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume);
+
+static struct spi_driver ad7877_driver = {
+ .driver = {
+ .name = "ad7877",
+ .owner = THIS_MODULE,
+ .pm = &ad7877_pm,
+ },
+ .probe = ad7877_probe,
+ .remove = __devexit_p(ad7877_remove),
+};
+
+module_spi_driver(ad7877_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7877 touchscreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ad7877");
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
new file mode 100644
index 00000000..3054354d
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -0,0 +1,109 @@
+/*
+ * AD7879-1/AD7889-1 touchscreen (I2C bus)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+
+#include "ad7879.h"
+
+#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
+
+/* 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 i2c_smbus_read_word_swapped(client, 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] = swab16(buf[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_swapped(client, reg, 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,
+ .pm = &ad7879_pm_ops,
+ },
+ .probe = ad7879_i2c_probe,
+ .remove = __devexit_p(ad7879_i2c_remove),
+ .id_table = ad7879_id,
+};
+
+module_i2c_driver(ad7879_i2c_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
new file mode 100644
index 00000000..db49abf0
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -0,0 +1,165 @@
+/*
+ * AD7879/AD7889 touchscreen (SPI bus)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_SPI */
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#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)
+
+/*
+ * 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;
+ int err;
+
+ /* 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;
+ }
+
+ 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);
+
+ 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",
+ .owner = THIS_MODULE,
+ .pm = &ad7879_pm_ops,
+ },
+ .probe = ad7879_spi_probe,
+ .remove = __devexit_p(ad7879_spi_remove),
+};
+
+module_spi_driver(ad7879_spi_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+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
new file mode 100644
index 00000000..e2482b40
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879.c
@@ -0,0 +1,649 @@
+/*
+ * AD7879/AD7889 based touchscreen and GPIO driver
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ * - ad7877.c
+ * Copyright (C) 2006-2008 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#include <linux/spi/ad7879.h>
+#include <linux/module.h>
+#include "ad7879.h"
+
+#define AD7879_REG_ZEROS 0
+#define AD7879_REG_CTRL1 1
+#define AD7879_REG_CTRL2 2
+#define AD7879_REG_CTRL3 3
+#define AD7879_REG_AUX1HIGH 4
+#define AD7879_REG_AUX1LOW 5
+#define AD7879_REG_TEMP1HIGH 6
+#define AD7879_REG_TEMP1LOW 7
+#define AD7879_REG_XPLUS 8
+#define AD7879_REG_YPLUS 9
+#define AD7879_REG_Z1 10
+#define AD7879_REG_Z2 11
+#define AD7879_REG_AUXVBAT 12
+#define AD7879_REG_TEMP 13
+#define AD7879_REG_REVID 14
+
+/* Control REG 1 */
+#define AD7879_TMR(x) ((x & 0xFF) << 0)
+#define AD7879_ACQ(x) ((x & 0x3) << 8)
+#define AD7879_MODE_NOC (0 << 10) /* Do not convert */
+#define AD7879_MODE_SCC (1 << 10) /* Single channel conversion */
+#define AD7879_MODE_SEQ0 (2 << 10) /* Sequence 0 in Slave Mode */
+#define AD7879_MODE_SEQ1 (3 << 10) /* Sequence 1 in Master Mode */
+#define AD7879_MODE_INT (1 << 15) /* PENIRQ disabled INT enabled */
+
+/* Control REG 2 */
+#define AD7879_FCD(x) ((x & 0x3) << 0)
+#define AD7879_RESET (1 << 4)
+#define AD7879_MFS(x) ((x & 0x3) << 5)
+#define AD7879_AVG(x) ((x & 0x3) << 7)
+#define AD7879_SER (1 << 9) /* non-differential */
+#define AD7879_DFR (0 << 9) /* differential */
+#define AD7879_GPIOPOL (1 << 10)
+#define AD7879_GPIODIR (1 << 11)
+#define AD7879_GPIO_DATA (1 << 12)
+#define AD7879_GPIO_EN (1 << 13)
+#define AD7879_PM(x) ((x & 0x3) << 14)
+#define AD7879_PM_SHUTDOWN (0)
+#define AD7879_PM_DYN (1)
+#define AD7879_PM_FULLON (2)
+
+/* Control REG 3 */
+#define AD7879_TEMPMASK_BIT (1<<15)
+#define AD7879_AUXVBATMASK_BIT (1<<14)
+#define AD7879_INTMODE_BIT (1<<13)
+#define AD7879_GPIOALERTMASK_BIT (1<<12)
+#define AD7879_AUXLOW_BIT (1<<11)
+#define AD7879_AUXHIGH_BIT (1<<10)
+#define AD7879_TEMPLOW_BIT (1<<9)
+#define AD7879_TEMPHIGH_BIT (1<<8)
+#define AD7879_YPLUS_BIT (1<<7)
+#define AD7879_XPLUS_BIT (1<<6)
+#define AD7879_Z1_BIT (1<<5)
+#define AD7879_Z2_BIT (1<<4)
+#define AD7879_AUX_BIT (1<<3)
+#define AD7879_VBAT_BIT (1<<2)
+#define AD7879_TEMP_BIT (1<<1)
+
+enum {
+ AD7879_SEQ_XPOS = 0,
+ AD7879_SEQ_YPOS = 1,
+ AD7879_SEQ_Z1 = 2,
+ AD7879_SEQ_Z2 = 3,
+ AD7879_NR_SENSE = 4,
+};
+
+#define MAX_12BIT ((1<<12)-1)
+#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
+
+struct ad7879 {
+ const struct ad7879_bus_ops *bops;
+
+ struct device *dev;
+ struct input_dev *input;
+ struct timer_list timer;
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gc;
+ struct mutex mutex;
+#endif
+ unsigned int irq;
+ bool disabled; /* P: input->mutex */
+ bool suspended; /* P: input->mutex */
+ u16 conversion_data[AD7879_NR_SENSE];
+ char phys[32];
+ u8 first_conversion_delay;
+ u8 acquisition_time;
+ u8 averaging;
+ u8 pen_down_acc_interval;
+ u8 median;
+ u16 x_plate_ohms;
+ u16 pressure_max;
+ u16 cmd_crtl1;
+ u16 cmd_crtl2;
+ u16 cmd_crtl3;
+ int x;
+ int y;
+ int Rt;
+};
+
+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 int ad7879_report(struct ad7879 *ts)
+{
+ struct input_dev *input_dev = ts->input;
+ unsigned Rt;
+ u16 x, y, z1, z2;
+
+ x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT;
+ y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT;
+ z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT;
+ z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT;
+
+ /*
+ * 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.
+ */
+
+ if (likely(x && z1)) {
+ /* compute touch pressure resistance using equation #1 */
+ Rt = (z2 - z1) * x * ts->x_plate_ohms;
+ Rt /= z1;
+ Rt = (Rt + 2047) >> 12;
+
+ /*
+ * Sample found inconsistent, pressure is beyond
+ * the maximum. Don't report it to user space.
+ */
+ if (Rt > ts->pressure_max)
+ return -EINVAL;
+
+ /*
+ * Note that we delay reporting events by one sample.
+ * This is done to avoid reporting last sample of the
+ * touch sequence, which may be incomplete if finger
+ * leaves the surface before last reading is taken.
+ */
+ if (timer_pending(&ts->timer)) {
+ /* Touch continues */
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_report_abs(input_dev, ABS_X, ts->x);
+ input_report_abs(input_dev, ABS_Y, ts->y);
+ input_report_abs(input_dev, ABS_PRESSURE, ts->Rt);
+ input_sync(input_dev);
+ }
+
+ ts->x = x;
+ ts->y = y;
+ ts->Rt = Rt;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+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);
+}
+
+static void ad7879_timer(unsigned long handle)
+{
+ struct ad7879 *ts = (void *)handle;
+
+ ad7879_ts_event_release(ts);
+}
+
+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);
+
+ if (!ad7879_report(ts))
+ mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+
+ return IRQ_HANDLED;
+}
+
+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);
+}
+
+static void __ad7879_disable(struct ad7879 *ts)
+{
+ u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) |
+ AD7879_PM(AD7879_PM_SHUTDOWN);
+ disable_irq(ts->irq);
+
+ if (del_timer_sync(&ts->timer))
+ ad7879_ts_event_release(ts);
+
+ ad7879_write(ts, AD7879_REG_CTRL2, reg);
+}
+
+
+static int ad7879_open(struct input_dev *input)
+{
+ struct ad7879 *ts = input_get_drvdata(input);
+
+ /* protected by input->mutex */
+ if (!ts->disabled && !ts->suspended)
+ __ad7879_enable(ts);
+
+ return 0;
+}
+
+static void ad7879_close(struct input_dev* input)
+{
+ struct ad7879 *ts = input_get_drvdata(input);
+
+ /* protected by input->mutex */
+ if (!ts->disabled && !ts->suspended)
+ __ad7879_disable(ts);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ad7879_suspend(struct device *dev)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->input->mutex);
+
+ if (!ts->suspended && !ts->disabled && ts->input->users)
+ __ad7879_disable(ts);
+
+ ts->suspended = true;
+
+ mutex_unlock(&ts->input->mutex);
+
+ return 0;
+}
+
+static int ad7879_resume(struct device *dev)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->suspended && !ts->disabled && ts->input->users)
+ __ad7879_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->input->mutex);
+
+ return 0;
+}
+#endif
+
+SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume);
+EXPORT_SYMBOL(ad7879_pm_ops);
+
+static void ad7879_toggle(struct ad7879 *ts, bool disable)
+{
+ 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);
+ }
+ }
+
+ ts->disabled = disable;
+
+ mutex_unlock(&ts->input->mutex);
+}
+
+static ssize_t ad7879_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7879_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ad7879 *ts = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ ad7879_toggle(ts, val);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store);
+
+static struct attribute *ad7879_attributes[] = {
+ &dev_attr_disable.attr,
+ NULL
+};
+
+static const struct attribute_group ad7879_attr_group = {
+ .attrs = ad7879_attributes,
+};
+
+#ifdef CONFIG_GPIOLIB
+static int ad7879_gpio_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+ int err;
+
+ mutex_lock(&ts->mutex);
+ ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL;
+ err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+ mutex_unlock(&ts->mutex);
+
+ return err;
+}
+
+static int ad7879_gpio_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int level)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+ int err;
+
+ mutex_lock(&ts->mutex);
+ ts->cmd_crtl2 &= ~AD7879_GPIODIR;
+ ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL;
+ if (level)
+ ts->cmd_crtl2 |= AD7879_GPIO_DATA;
+ else
+ ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
+
+ err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+ mutex_unlock(&ts->mutex);
+
+ return err;
+}
+
+static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+ u16 val;
+
+ mutex_lock(&ts->mutex);
+ val = ad7879_read(ts, AD7879_REG_CTRL2);
+ mutex_unlock(&ts->mutex);
+
+ return !!(val & AD7879_GPIO_DATA);
+}
+
+static void ad7879_gpio_set_value(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+
+ mutex_lock(&ts->mutex);
+ if (value)
+ ts->cmd_crtl2 |= AD7879_GPIO_DATA;
+ else
+ ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
+
+ ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+ mutex_unlock(&ts->mutex);
+}
+
+static int ad7879_gpio_add(struct ad7879 *ts,
+ const struct ad7879_platform_data *pdata)
+{
+ 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;
+ ts->gc.get = ad7879_gpio_get_value;
+ ts->gc.set = ad7879_gpio_set_value;
+ ts->gc.can_sleep = 1;
+ ts->gc.base = pdata->gpio_base;
+ ts->gc.ngpio = 1;
+ ts->gc.label = "AD7879-GPIO";
+ ts->gc.owner = THIS_MODULE;
+ ts->gc.dev = ts->dev;
+
+ ret = gpiochip_add(&ts->gc);
+ if (ret)
+ dev_err(ts->dev, "failed to register gpio %d\n",
+ ts->gc.base);
+ }
+
+ return ret;
+}
+
+static void ad7879_gpio_remove(struct ad7879 *ts)
+{
+ 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(ts->dev, "failed to remove gpio %d\n",
+ ts->gc.base);
+ }
+}
+#else
+static inline int ad7879_gpio_add(struct ad7879 *ts,
+ const struct ad7879_platform_data *pdata)
+{
+ return 0;
+}
+
+static inline void ad7879_gpio_remove(struct ad7879 *ts)
+{
+}
+#endif
+
+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;
+ int err;
+ u16 revid;
+
+ if (!irq) {
+ dev_err(dev, "no IRQ?\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ if (!pdata) {
+ dev_err(dev, "no platform data?\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->bops = bops;
+ ts->dev = dev;
+ ts->input = input_dev;
+ ts->irq = irq;
+
+ setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
+
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+ ts->pressure_max = pdata->pressure_max ? : ~0;
+
+ ts->first_conversion_delay = pdata->first_conversion_delay;
+ ts->acquisition_time = pdata->acquisition_time;
+ ts->averaging = pdata->averaging;
+ ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+ ts->median = pdata->median;
+
+ 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 = 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);
+ __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,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+ err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
+ if (err < 0) {
+ dev_err(dev, "Failed to write %s\n", input_dev->name);
+ goto err_free_mem;
+ }
+
+ 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;
+ }
+
+ ts->cmd_crtl3 = AD7879_YPLUS_BIT |
+ AD7879_XPLUS_BIT |
+ AD7879_Z2_BIT |
+ AD7879_Z1_BIT |
+ AD7879_TEMPMASK_BIT |
+ AD7879_AUXVBATMASK_BIT |
+ AD7879_GPIOALERTMASK_BIT;
+
+ ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR |
+ AD7879_AVG(ts->averaging) |
+ AD7879_MFS(ts->median) |
+ AD7879_FCD(ts->first_conversion_delay);
+
+ ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 |
+ AD7879_ACQ(ts->acquisition_time) |
+ AD7879_TMR(ts->pen_down_acc_interval);
+
+ err = request_threaded_irq(ts->irq, NULL, ad7879_irq,
+ IRQF_TRIGGER_FALLING,
+ dev_name(dev), ts);
+ if (err) {
+ dev_err(dev, "irq %d busy?\n", ts->irq);
+ goto err_free_mem;
+ }
+
+ __ad7879_disable(ts);
+
+ err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
+ if (err)
+ goto err_free_irq;
+
+ err = ad7879_gpio_add(ts, pdata);
+ if (err)
+ goto err_remove_attr;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_gpio;
+
+ return ts;
+
+err_remove_gpio:
+ ad7879_gpio_remove(ts);
+err_remove_attr:
+ sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
+err_free_irq:
+ free_irq(ts->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+err_out:
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ad7879_probe);
+
+void ad7879_remove(struct ad7879 *ts)
+{
+ ad7879_gpio_remove(ts);
+ sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ kfree(ts);
+}
+EXPORT_SYMBOL(ad7879_remove);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h
new file mode 100644
index 00000000..6fd13c48
--- /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 <linux/types.h>
+
+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);
+};
+
+extern const struct dev_pm_ops ad7879_pm_ops;
+
+struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
+ const struct ad7879_bus_ops *bops);
+void ad7879_remove(struct ad7879 *);
+
+#endif
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
new file mode 100644
index 00000000..f02028ec
--- /dev/null
+++ b/drivers/input/touchscreen/ads7846.c
@@ -0,0 +1,1440 @@
+/*
+ * ADS7846 based touchscreen and sensor driver
+ *
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <asm/irq.h>
+
+/*
+ * This code has been heavily tested on a Nokia 770, and lightly
+ * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz).
+ * TSC2046 is just newer ads7846 silicon.
+ * Support for ads7843 tested on Atmel at91sam926x-EK.
+ * Support for ads7845 has only been stubbed in.
+ * Support for Analog Devices AD7873 and AD7843 tested.
+ *
+ * IRQ handling needs a workaround because of a shortcoming in handling
+ * edge triggered IRQs on some platforms like the OMAP1/2. These
+ * platforms don't handle the ARM lazy IRQ disabling properly, thus we
+ * have to maintain our own SW IRQ disabled status. This should be
+ * removed as soon as the affected platform's IRQ handling is fixed.
+ *
+ * App note sbaa036 talks in more detail about accurate sampling...
+ * that ought to help in situations like LCDs inducing noise (which
+ * can also be helped by using synch signals) and more generally.
+ * This driver tries to utilize the measures described in the app
+ * note. The strength of filtering can be set in the board-* specific
+ * files.
+ */
+
+#define TS_POLL_DELAY 1 /* ms delay before the first sample */
+#define TS_POLL_PERIOD 5 /* ms delay between samples */
+
+/* this driver doesn't aim at the peak continuous sample rate */
+#define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)
+
+struct ts_event {
+ /*
+ * For portability, we can't read 12 bit values using SPI (which
+ * would make the controller deliver them as native byte order u16
+ * with msbs zeroed). Instead, we read them as two 8-bit values,
+ * *** WHICH NEED BYTESWAPPING *** and range adjustment.
+ */
+ u16 x;
+ u16 y;
+ u16 z1, z2;
+ bool ignore;
+ u8 x_buf[3];
+ u8 y_buf[3];
+};
+
+/*
+ * We allocate this separately to avoid cache line sharing issues when
+ * driver is used with DMA-based SPI controllers (like atmel_spi) on
+ * systems where main memory is not DMA-coherent (most non-x86 boards).
+ */
+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 {
+ struct input_dev *input;
+ char phys[32];
+ char name[32];
+
+ struct spi_device *spi;
+ struct regulator *reg;
+
+#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
+ struct attribute_group *attr_group;
+ struct device *hwmon;
+#endif
+
+ u16 model;
+ u16 vref_mv;
+ u16 vref_delay_usecs;
+ u16 x_plate_ohms;
+ u16 pressure_max;
+
+ bool swap_xy;
+ bool use_internal;
+
+ struct ads7846_packet *packet;
+
+ struct spi_transfer xfer[18];
+ struct spi_message msg[5];
+ int msg_count;
+ wait_queue_head_t wait;
+
+ bool pendown;
+
+ int read_cnt;
+ int read_rep;
+ int last_read;
+
+ u16 debounce_max;
+ u16 debounce_tol;
+ u16 debounce_rep;
+
+ u16 penirq_recheck_delay_usecs;
+
+ struct mutex lock;
+ bool stopped; /* P: lock */
+ bool disabled; /* P: lock */
+ bool suspended; /* P: lock */
+
+ int (*filter)(void *data, int data_idx, int *val);
+ void *filter_data;
+ void (*filter_cleanup)(void *data);
+ int (*get_pendown_state)(void);
+ int gpio_pendown;
+
+ void (*wait_for_sync)(void);
+};
+
+/* leave chip selected when we're done, for quicker re-select? */
+#if 0
+#define CS_CHANGE(xfer) ((xfer).cs_change = 1)
+#else
+#define CS_CHANGE(xfer) ((xfer).cs_change = 0)
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+/* The ADS7846 has touchscreen and other sensors.
+ * Earlier ads784x chips are somewhat compatible.
+ */
+#define ADS_START (1 << 7)
+#define ADS_A2A1A0_d_y (1 << 4) /* differential */
+#define ADS_A2A1A0_d_z1 (3 << 4) /* differential */
+#define ADS_A2A1A0_d_z2 (4 << 4) /* differential */
+#define ADS_A2A1A0_d_x (5 << 4) /* differential */
+#define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */
+#define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */
+#define ADS_A2A1A0_vaux (6 << 4) /* non-differential */
+#define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */
+#define ADS_8_BIT (1 << 3)
+#define ADS_12_BIT (0 << 3)
+#define ADS_SER (1 << 2) /* non-differential */
+#define ADS_DFR (0 << 2) /* differential */
+#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */
+#define ADS_PD10_ADC_ON (1 << 0) /* ADC on */
+#define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */
+#define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */
+
+#define MAX_12BIT ((1<<12)-1)
+
+/* leave ADC powered up (disables penirq) between differential samples */
+#define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \
+ | ADS_12_BIT | ADS_DFR | \
+ (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0))
+
+#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref))
+#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref))
+#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref))
+
+#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref))
+#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */
+
+/* single-ended samples need to first power up reference voltage;
+ * we leave both ADC and VREF powered
+ */
+#define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \
+ | ADS_12_BIT | ADS_SER)
+
+#define REF_ON (READ_12BIT_DFR(x, 1, 1))
+#define REF_OFF (READ_12BIT_DFR(y, 0, 0))
+
+/* Must be called with ts->lock held */
+static void ads7846_stop(struct ads7846 *ts)
+{
+ if (!ts->disabled && !ts->suspended) {
+ /* Signal IRQ thread to stop polling and disable the handler. */
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+ disable_irq(ts->spi->irq);
+ }
+}
+
+/* Must be called with ts->lock held */
+static void ads7846_restart(struct ads7846 *ts)
+{
+ if (!ts->disabled && !ts->suspended) {
+ /* Tell IRQ thread that it may poll the device. */
+ ts->stopped = false;
+ mb();
+ enable_irq(ts->spi->irq);
+ }
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_disable(struct ads7846 *ts)
+{
+ ads7846_stop(ts);
+ regulator_disable(ts->reg);
+
+ /*
+ * We know the chip's in low power mode since we always
+ * leave it that way after every request
+ */
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_enable(struct ads7846 *ts)
+{
+ regulator_enable(ts->reg);
+ ads7846_restart(ts);
+}
+
+static void ads7846_disable(struct ads7846 *ts)
+{
+ mutex_lock(&ts->lock);
+
+ if (!ts->disabled) {
+
+ if (!ts->suspended)
+ __ads7846_disable(ts);
+
+ ts->disabled = true;
+ }
+
+ mutex_unlock(&ts->lock);
+}
+
+static void ads7846_enable(struct ads7846 *ts)
+{
+ mutex_lock(&ts->lock);
+
+ if (ts->disabled) {
+
+ ts->disabled = false;
+
+ if (!ts->suspended)
+ __ads7846_enable(ts);
+ }
+
+ mutex_unlock(&ts->lock);
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Non-touchscreen sensors only use single-ended conversions.
+ * The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
+ * ads7846 lets that pin be unconnected, to use internal vREF.
+ */
+
+struct ser_req {
+ u8 ref_on;
+ u8 command;
+ u8 ref_off;
+ u16 scratch;
+ struct spi_message msg;
+ struct spi_transfer xfer[6];
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 sample ____cacheline_aligned;
+};
+
+struct ads7845_ser_req {
+ u8 command[3];
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 sample[3] ____cacheline_aligned;
+};
+
+static int ads7846_read12_ser(struct device *dev, unsigned command)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ struct ser_req *req;
+ int status;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ spi_message_init(&req->msg);
+
+ /* maybe turn on internal vREF, and let it settle */
+ if (ts->use_internal) {
+ req->ref_on = REF_ON;
+ req->xfer[0].tx_buf = &req->ref_on;
+ req->xfer[0].len = 1;
+ spi_message_add_tail(&req->xfer[0], &req->msg);
+
+ req->xfer[1].rx_buf = &req->scratch;
+ req->xfer[1].len = 2;
+
+ /* for 1uF, settle for 800 usec; no cap, 100 usec. */
+ req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+ spi_message_add_tail(&req->xfer[1], &req->msg);
+
+ /* Enable reference voltage */
+ command |= ADS_PD10_REF_ON;
+ }
+
+ /* Enable ADC in every case */
+ command |= ADS_PD10_ADC_ON;
+
+ /* take sample */
+ req->command = (u8) command;
+ req->xfer[2].tx_buf = &req->command;
+ req->xfer[2].len = 1;
+ spi_message_add_tail(&req->xfer[2], &req->msg);
+
+ req->xfer[3].rx_buf = &req->sample;
+ req->xfer[3].len = 2;
+ spi_message_add_tail(&req->xfer[3], &req->msg);
+
+ /* REVISIT: take a few more samples, and compare ... */
+
+ /* converter in low power mode & enable PENIRQ */
+ req->ref_off = PWRDOWN;
+ req->xfer[4].tx_buf = &req->ref_off;
+ req->xfer[4].len = 1;
+ spi_message_add_tail(&req->xfer[4], &req->msg);
+
+ req->xfer[5].rx_buf = &req->scratch;
+ req->xfer[5].len = 2;
+ CS_CHANGE(req->xfer[5]);
+ spi_message_add_tail(&req->xfer[5], &req->msg);
+
+ mutex_lock(&ts->lock);
+ ads7846_stop(ts);
+ status = spi_sync(spi, &req->msg);
+ ads7846_restart(ts);
+ mutex_unlock(&ts->lock);
+
+ if (status == 0) {
+ /* on-wire is a must-ignore bit, a BE12 value, then padding */
+ status = be16_to_cpu(req->sample);
+ status = status >> 3;
+ status &= 0x0fff;
+ }
+
+ kfree(req);
+ 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;
+ int status;
+
+ req = kzalloc(sizeof *req, GFP_KERNEL);
+ 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);
+
+ mutex_lock(&ts->lock);
+ ads7846_stop(ts);
+ status = spi_sync(spi, &req->msg);
+ ads7846_restart(ts);
+ mutex_unlock(&ts->lock);
+
+ 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 \
+name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct ads7846 *ts = dev_get_drvdata(dev); \
+ ssize_t v = ads7846_read12_ser(dev, \
+ READ_12BIT_SER(var)); \
+ if (v < 0) \
+ return v; \
+ return sprintf(buf, "%u\n", adjust(ts, v)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
+
+
+/* Sysfs conventions report temperatures in millidegrees Celsius.
+ * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high
+ * accuracy scheme without calibration data. For now we won't try either;
+ * userspace sees raw sensor values, and must scale/calibrate appropriately.
+ */
+static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v)
+{
+ return v;
+}
+
+SHOW(temp0, temp0, null_adjust) /* temp1_input */
+SHOW(temp1, temp1, null_adjust) /* temp2_input */
+
+
+/* sysfs conventions report voltages in millivolts. We can convert voltages
+ * if we know vREF. userspace may need to scale vAUX to match the board's
+ * external resistors; we assume that vBATT only uses the internal ones.
+ */
+static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
+{
+ unsigned retval = v;
+
+ /* external resistors may scale vAUX into 0..vREF */
+ retval *= ts->vref_mv;
+ retval = retval >> 12;
+
+ return retval;
+}
+
+static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
+{
+ unsigned retval = vaux_adjust(ts, v);
+
+ /* ads7846 has a resistor ladder to scale this signal down */
+ if (ts->model == 7846)
+ retval *= 4;
+
+ return retval;
+}
+
+SHOW(in0_input, vaux, vaux_adjust)
+SHOW(in1_input, vbatt, vbatt_adjust)
+
+static struct attribute *ads7846_attributes[] = {
+ &dev_attr_temp0.attr,
+ &dev_attr_temp1.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ NULL,
+};
+
+static struct attribute_group ads7846_attr_group = {
+ .attrs = ads7846_attributes,
+};
+
+static struct attribute *ads7843_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ NULL,
+};
+
+static struct attribute_group ads7843_attr_group = {
+ .attrs = ads7843_attributes,
+};
+
+static struct attribute *ads7845_attributes[] = {
+ &dev_attr_in0_input.attr,
+ NULL,
+};
+
+static struct attribute_group ads7845_attr_group = {
+ .attrs = ads7845_attributes,
+};
+
+static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
+{
+ struct device *hwmon;
+ int err;
+
+ /* hwmon sensors need a reference voltage */
+ switch (ts->model) {
+ case 7846:
+ if (!ts->vref_mv) {
+ dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
+ ts->vref_mv = 2500;
+ ts->use_internal = true;
+ }
+ break;
+ case 7845:
+ case 7843:
+ if (!ts->vref_mv) {
+ dev_warn(&spi->dev,
+ "external vREF for ADS%d not specified\n",
+ ts->model);
+ return 0;
+ }
+ break;
+ }
+
+ /* different chips have different sensor groups */
+ switch (ts->model) {
+ case 7846:
+ ts->attr_group = &ads7846_attr_group;
+ break;
+ case 7845:
+ ts->attr_group = &ads7845_attr_group;
+ break;
+ case 7843:
+ ts->attr_group = &ads7843_attr_group;
+ break;
+ default:
+ dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model);
+ return 0;
+ }
+
+ err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
+ if (err)
+ return err;
+
+ hwmon = hwmon_device_register(&spi->dev);
+ if (IS_ERR(hwmon)) {
+ sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+ return PTR_ERR(hwmon);
+ }
+
+ ts->hwmon = hwmon;
+ return 0;
+}
+
+static void ads784x_hwmon_unregister(struct spi_device *spi,
+ struct ads7846 *ts)
+{
+ if (ts->hwmon) {
+ sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+ hwmon_device_unregister(ts->hwmon);
+ }
+}
+
+#else
+static inline int ads784x_hwmon_register(struct spi_device *spi,
+ struct ads7846 *ts)
+{
+ return 0;
+}
+
+static inline void ads784x_hwmon_unregister(struct spi_device *spi,
+ struct ads7846 *ts)
+{
+}
+#endif
+
+static ssize_t ads7846_pen_down_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->pendown);
+}
+
+static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);
+
+static ssize_t ads7846_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ads7846_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ unsigned int i;
+ int err;
+
+ err = kstrtouint(buf, 10, &i);
+ if (err)
+ return err;
+
+ if (i)
+ ads7846_disable(ts);
+ else
+ ads7846_enable(ts);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
+
+static struct attribute *ads784x_attributes[] = {
+ &dev_attr_pen_down.attr,
+ &dev_attr_disable.attr,
+ NULL,
+};
+
+static struct attribute_group ads784x_attr_group = {
+ .attrs = ads784x_attributes,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int get_pendown_state(struct ads7846 *ts)
+{
+ if (ts->get_pendown_state)
+ return ts->get_pendown_state();
+
+ return !gpio_get_value(ts->gpio_pendown);
+}
+
+static void null_wait_for_sync(void)
+{
+}
+
+static int ads7846_debounce_filter(void *ads, int data_idx, int *val)
+{
+ struct ads7846 *ts = ads;
+
+ if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
+ /* Start over collecting consistent readings. */
+ ts->read_rep = 0;
+ /*
+ * Repeat it, if this was the first read or the read
+ * wasn't consistent enough.
+ */
+ if (ts->read_cnt < ts->debounce_max) {
+ ts->last_read = *val;
+ ts->read_cnt++;
+ return ADS7846_FILTER_REPEAT;
+ } else {
+ /*
+ * Maximum number of debouncing reached and still
+ * not enough number of consistent readings. Abort
+ * the whole sample, repeat it in the next sampling
+ * period.
+ */
+ ts->read_cnt = 0;
+ return ADS7846_FILTER_IGNORE;
+ }
+ } else {
+ if (++ts->read_rep > ts->debounce_rep) {
+ /*
+ * Got a good reading for this coordinate,
+ * go for the next one.
+ */
+ ts->read_cnt = 0;
+ ts->read_rep = 0;
+ return ADS7846_FILTER_OK;
+ } else {
+ /* Read more values that are consistent. */
+ ts->read_cnt++;
+ return ADS7846_FILTER_REPEAT;
+ }
+ }
+}
+
+static int ads7846_no_filter(void *ads, int data_idx, int *val)
+{
+ return ADS7846_FILTER_OK;
+}
+
+static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m)
+{
+ struct spi_transfer *t =
+ list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+ if (ts->model == 7845) {
+ return 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.
+ */
+ return be16_to_cpup((__be16 *)t->rx_buf) >> 3;
+ }
+}
+
+static void ads7846_update_value(struct spi_message *m, int val)
+{
+ struct spi_transfer *t =
+ list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+ *(u16 *)t->rx_buf = val;
+}
+
+static void ads7846_read_state(struct ads7846 *ts)
+{
+ struct ads7846_packet *packet = ts->packet;
+ struct spi_message *m;
+ int msg_idx = 0;
+ int val;
+ int action;
+ int error;
+
+ while (msg_idx < ts->msg_count) {
+
+ ts->wait_for_sync();
+
+ m = &ts->msg[msg_idx];
+ error = spi_sync(ts->spi, m);
+ if (error) {
+ dev_err(&ts->spi->dev, "spi_async --> %d\n", error);
+ packet->tc.ignore = true;
+ return;
+ }
+
+ /*
+ * Last message is power down request, no need to convert
+ * or filter the value.
+ */
+ if (msg_idx < ts->msg_count - 1) {
+
+ val = ads7846_get_value(ts, m);
+
+ action = ts->filter(ts->filter_data, msg_idx, &val);
+ switch (action) {
+ case ADS7846_FILTER_REPEAT:
+ continue;
+
+ case ADS7846_FILTER_IGNORE:
+ packet->tc.ignore = true;
+ msg_idx = ts->msg_count - 1;
+ continue;
+
+ case ADS7846_FILTER_OK:
+ ads7846_update_value(m, val);
+ packet->tc.ignore = false;
+ msg_idx++;
+ break;
+
+ default:
+ BUG();
+ }
+ } else {
+ msg_idx++;
+ }
+ }
+}
+
+static void ads7846_report_state(struct ads7846 *ts)
+{
+ struct ads7846_packet *packet = ts->packet;
+ unsigned int Rt;
+ u16 x, y, z1, z2;
+
+ /*
+ * ads7846_get_value() does in-place conversion (including byte swap)
+ * from on-the-wire format as part of debouncing to get stable
+ * readings.
+ */
+ 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)
+ x = 0;
+
+ 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;
+ Rt -= z1;
+ Rt *= x;
+ Rt *= ts->x_plate_ohms;
+ Rt /= z1;
+ Rt = (Rt + 2047) >> 12;
+ } else {
+ Rt = 0;
+ }
+
+ /*
+ * Sample found inconsistent by debouncing or pressure is beyond
+ * the maximum. Don't report it to user space, repeat at least
+ * once more the measurement
+ */
+ if (packet->tc.ignore || Rt > ts->pressure_max) {
+ dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
+ packet->tc.ignore, Rt);
+ return;
+ }
+
+ /*
+ * Maybe check the pendown state before reporting. This discards
+ * false readings when the pen is lifted.
+ */
+ if (ts->penirq_recheck_delay_usecs) {
+ udelay(ts->penirq_recheck_delay_usecs);
+ if (!get_pendown_state(ts))
+ Rt = 0;
+ }
+
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
+ * state, even this controller has a pressure sensor. The pressure
+ * value can fluctuate for quite a while after lifting the pen and
+ * in some cases may not even settle at the expected value.
+ *
+ * The only safe way to check for the pen up condition is in the
+ * timer by reading the pen signal state (it's a GPIO _and_ IRQ).
+ */
+ if (Rt) {
+ struct input_dev *input = ts->input;
+
+ if (ts->swap_xy)
+ swap(x, y);
+
+ if (!ts->pendown) {
+ input_report_key(input, BTN_TOUCH, 1);
+ ts->pendown = true;
+ dev_vdbg(&ts->spi->dev, "DOWN\n");
+ }
+
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
+
+ input_sync(input);
+ dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
+ }
+}
+
+static irqreturn_t ads7846_hard_irq(int irq, void *handle)
+{
+ struct ads7846 *ts = handle;
+
+ return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+
+static irqreturn_t ads7846_irq(int irq, void *handle)
+{
+ struct ads7846 *ts = handle;
+
+ /* Start with a small delay before checking pendown state */
+ msleep(TS_POLL_DELAY);
+
+ while (!ts->stopped && get_pendown_state(ts)) {
+
+ /* pen is down, continue with the measurement */
+ ads7846_read_state(ts);
+
+ if (!ts->stopped)
+ ads7846_report_state(ts);
+
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(TS_POLL_PERIOD));
+ }
+
+ if (ts->pendown) {
+ struct input_dev *input = ts->input;
+
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+
+ ts->pendown = false;
+ dev_vdbg(&ts->spi->dev, "UP\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ads7846_suspend(struct device *dev)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->lock);
+
+ if (!ts->suspended) {
+
+ if (!ts->disabled)
+ __ads7846_disable(ts);
+
+ if (device_may_wakeup(&ts->spi->dev))
+ enable_irq_wake(ts->spi->irq);
+
+ ts->suspended = true;
+ }
+
+ mutex_unlock(&ts->lock);
+
+ return 0;
+}
+
+static int ads7846_resume(struct device *dev)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->lock);
+
+ if (ts->suspended) {
+
+ ts->suspended = false;
+
+ if (device_may_wakeup(&ts->spi->dev))
+ disable_irq_wake(ts->spi->irq);
+
+ if (!ts->disabled)
+ __ads7846_enable(ts);
+ }
+
+ mutex_unlock(&ts->lock);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume);
+
+static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts)
+{
+ struct ads7846_platform_data *pdata = spi->dev.platform_data;
+ int err;
+
+ /*
+ * REVISIT when the irq can be triggered active-low, or if for some
+ * reason the touchscreen isn't hooked up, we don't need to access
+ * the pendown state.
+ */
+
+ if (pdata->get_pendown_state) {
+ ts->get_pendown_state = pdata->get_pendown_state;
+ } else if (gpio_is_valid(pdata->gpio_pendown)) {
+
+ err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN,
+ "ads7846_pendown");
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to request/setup pendown GPIO%d: %d\n",
+ pdata->gpio_pendown, err);
+ return err;
+ }
+
+ ts->gpio_pendown = pdata->gpio_pendown;
+
+ } else {
+ dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up the transfers to read touchscreen state; this assumes we
+ * use formula #2 for pressure, not #3.
+ */
+static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts,
+ const struct ads7846_platform_data *pdata)
+{
+ struct spi_message *m = &ts->msg[0];
+ struct spi_transfer *x = ts->xfer;
+ struct ads7846_packet *packet = ts->packet;
+ int vref = pdata->keep_vref_on;
+
+ if (ts->model == 7873) {
+ /*
+ * The AD7873 is almost identical to the ADS7846
+ * keep VREF off during differential/ratiometric
+ * conversion modes.
+ */
+ ts->model = 7846;
+ vref = 0;
+ }
+
+ ts->msg_count = 1;
+ spi_message_init(m);
+ m->context = ts;
+
+ 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);
+ }
+
+ /*
+ * The first sample after switching drivers can be low quality;
+ * optionally discard it, using a second one after the signals
+ * have had enough time to stabilize.
+ */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ 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);
+ }
+
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ 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);
+ }
+
+ /* ... maybe discard first sample ... */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ 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);
+ }
+
+ /* turn y+ off, x- on; we'll use formula #2 */
+ if (ts->model == 7846) {
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ x++;
+ packet->read_z1 = READ_Z1(vref);
+ x->tx_buf = &packet->read_z1;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z1;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+
+ /* ... maybe discard first sample ... */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ x->tx_buf = &packet->read_z1;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z1;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ x++;
+ packet->read_z2 = READ_Z2(vref);
+ x->tx_buf = &packet->read_z2;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z2;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+
+ /* ... maybe discard first sample ... */
+ if (pdata->settle_delay_usecs) {
+ x->delay_usecs = pdata->settle_delay_usecs;
+
+ x++;
+ x->tx_buf = &packet->read_z2;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &packet->tc.z2;
+ x->len = 2;
+ spi_message_add_tail(x, m);
+ }
+ }
+
+ /* power down */
+ ts->msg_count++;
+ m++;
+ spi_message_init(m);
+ m->context = ts;
+
+ 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;
+ }
+
+ CS_CHANGE(*x);
+ spi_message_add_tail(x, m);
+}
+
+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;
+ unsigned long irq_flags;
+ int err;
+
+ if (!spi->irq) {
+ dev_dbg(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ /* don't exceed max specified sample rate */
+ if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
+ dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
+ (spi->max_speed_hz/SAMPLE_BITS)/1000);
+ return -EINVAL;
+ }
+
+ /* We'd set TX word size 8 bits and RX word size to 13 bits ... except
+ * that even if the hardware can do that, the SPI controller driver
+ * may not. So we stick to very-portable 8 bit words, both RX and TX.
+ */
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
+ packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !packet || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ dev_set_drvdata(&spi->dev, ts);
+
+ ts->packet = packet;
+ ts->spi = spi;
+ ts->input = input_dev;
+ ts->vref_mv = pdata->vref_mv;
+ ts->swap_xy = pdata->swap_xy;
+
+ mutex_init(&ts->lock);
+ init_waitqueue_head(&ts->wait);
+
+ ts->model = pdata->model ? : 7846;
+ ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+ ts->pressure_max = pdata->pressure_max ? : ~0;
+
+ if (pdata->filter != NULL) {
+ if (pdata->filter_init != NULL) {
+ err = pdata->filter_init(pdata, &ts->filter_data);
+ if (err < 0)
+ goto err_free_mem;
+ }
+ ts->filter = pdata->filter;
+ ts->filter_cleanup = pdata->filter_cleanup;
+ } else if (pdata->debounce_max) {
+ ts->debounce_max = pdata->debounce_max;
+ if (ts->debounce_max < 2)
+ ts->debounce_max = 2;
+ ts->debounce_tol = pdata->debounce_tol;
+ ts->debounce_rep = pdata->debounce_rep;
+ ts->filter = ads7846_debounce_filter;
+ ts->filter_data = ts;
+ } else {
+ ts->filter = ads7846_no_filter;
+ }
+
+ err = ads7846_setup_pendown(spi, ts);
+ if (err)
+ goto err_cleanup_filter;
+
+ if (pdata->penirq_recheck_delay_usecs)
+ ts->penirq_recheck_delay_usecs =
+ pdata->penirq_recheck_delay_usecs;
+
+ ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+ snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
+
+ input_dev->name = ts->name;
+ input_dev->phys = ts->phys;
+ input_dev->dev.parent = &spi->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,
+ pdata->x_min ? : 0,
+ pdata->x_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+ ads7846_setup_spi_msg(ts, pdata);
+
+ ts->reg = regulator_get(&spi->dev, "vcc");
+ if (IS_ERR(ts->reg)) {
+ err = PTR_ERR(ts->reg);
+ dev_err(&spi->dev, "unable to get regulator: %d\n", err);
+ goto err_free_gpio;
+ }
+
+ err = regulator_enable(ts->reg);
+ if (err) {
+ dev_err(&spi->dev, "unable to enable regulator: %d\n", err);
+ goto err_put_regulator;
+ }
+
+ irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING;
+ irq_flags |= IRQF_ONESHOT;
+
+ err = request_threaded_irq(spi->irq, ads7846_hard_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);
+ irq_flags |= IRQF_TRIGGER_RISING;
+ err = request_threaded_irq(spi->irq,
+ ads7846_hard_irq, ads7846_irq,
+ irq_flags, spi->dev.driver->name, ts);
+ }
+
+ if (err) {
+ dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+ goto err_disable_regulator;
+ }
+
+ err = ads784x_hwmon_register(spi, ts);
+ if (err)
+ goto err_free_irq;
+
+ dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
+
+ /*
+ * Take a first sample, leaving nPENIRQ active and vREF off; avoid
+ * the touchscreen, in case it's not connected.
+ */
+ if (ts->model == 7845)
+ ads7845_read12_ser(&spi->dev, PWRDOWN);
+ else
+ (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));
+
+ err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
+ if (err)
+ goto err_remove_hwmon;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_attr_group;
+
+ device_init_wakeup(&spi->dev, pdata->wakeup);
+
+ return 0;
+
+ err_remove_attr_group:
+ sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+ err_remove_hwmon:
+ ads784x_hwmon_unregister(spi, ts);
+ err_free_irq:
+ free_irq(spi->irq, ts);
+ err_disable_regulator:
+ regulator_disable(ts->reg);
+ err_put_regulator:
+ regulator_put(ts->reg);
+ err_free_gpio:
+ if (!ts->get_pendown_state)
+ gpio_free(ts->gpio_pendown);
+ err_cleanup_filter:
+ if (ts->filter_cleanup)
+ ts->filter_cleanup(ts->filter_data);
+ err_free_mem:
+ input_free_device(input_dev);
+ kfree(packet);
+ kfree(ts);
+ return err;
+}
+
+static int __devexit ads7846_remove(struct spi_device *spi)
+{
+ struct ads7846 *ts = dev_get_drvdata(&spi->dev);
+
+ device_init_wakeup(&spi->dev, false);
+
+ sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+
+ ads7846_disable(ts);
+ free_irq(ts->spi->irq, ts);
+
+ input_unregister_device(ts->input);
+
+ ads784x_hwmon_unregister(spi, ts);
+
+ regulator_disable(ts->reg);
+ regulator_put(ts->reg);
+
+ if (!ts->get_pendown_state) {
+ /*
+ * If we are not using specialized pendown method we must
+ * have been relying on gpio we set up ourselves.
+ */
+ gpio_free(ts->gpio_pendown);
+ }
+
+ if (ts->filter_cleanup)
+ ts->filter_cleanup(ts->filter_data);
+
+ kfree(ts->packet);
+ kfree(ts);
+
+ dev_dbg(&spi->dev, "unregistered touchscreen\n");
+
+ return 0;
+}
+
+static struct spi_driver ads7846_driver = {
+ .driver = {
+ .name = "ads7846",
+ .owner = THIS_MODULE,
+ .pm = &ads7846_pm,
+ },
+ .probe = ads7846_probe,
+ .remove = __devexit_p(ads7846_remove),
+};
+
+module_spi_driver(ads7846_driver);
+
+MODULE_DESCRIPTION("ADS7846 TouchScreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ads7846");
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
new file mode 100644
index 00000000..c5c2dbb9
--- /dev/null
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -0,0 +1,449 @@
+/*
+ * Atmel AT91 and AVR32 continuous touch screen driver for Wolfson WM97xx AC97
+ * codecs.
+ *
+ * Copyright (C) 2008 - 2009 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wm97xx.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define AC97C_ICA 0x10
+#define AC97C_CBRHR 0x30
+#define AC97C_CBSR 0x38
+#define AC97C_CBMR 0x3c
+#define AC97C_IER 0x54
+#define AC97C_IDR 0x58
+
+#define AC97C_RXRDY (1 << 4)
+#define AC97C_OVRUN (1 << 5)
+
+#define AC97C_CMR_SIZE_20 (0 << 16)
+#define AC97C_CMR_SIZE_18 (1 << 16)
+#define AC97C_CMR_SIZE_16 (2 << 16)
+#define AC97C_CMR_SIZE_10 (3 << 16)
+#define AC97C_CMR_CEM_LITTLE (1 << 18)
+#define AC97C_CMR_CEM_BIG (0 << 18)
+#define AC97C_CMR_CENA (1 << 21)
+
+#define AC97C_INT_CBEVT (1 << 4)
+
+#define AC97C_SR_CAEVT (1 << 3)
+
+#define AC97C_CH_MASK(slot) \
+ (0x7 << (3 * (slot - 3)))
+#define AC97C_CH_ASSIGN(slot, channel) \
+ (AC97C_CHANNEL_##channel << (3 * (slot - 3)))
+#define AC97C_CHANNEL_NONE 0x0
+#define AC97C_CHANNEL_B 0x2
+
+#define ac97c_writel(chip, reg, val) \
+ __raw_writel((val), (chip)->regs + AC97C_##reg)
+#define ac97c_readl(chip, reg) \
+ __raw_readl((chip)->regs + AC97C_##reg)
+
+#ifdef CONFIG_CPU_AT32AP700X
+#define ATMEL_WM97XX_AC97C_IOMEM (0xfff02800)
+#define ATMEL_WM97XX_AC97C_IRQ (29)
+#define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */
+#else
+#error Unknown CPU, this driver only supports AT32AP700X CPUs.
+#endif
+
+struct continuous {
+ u16 id; /* codec id */
+ u8 code; /* continuous code */
+ u8 reads; /* number of coord reads per read cycle */
+ u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+ {WM9705_ID2, 0, WM_READS(94), 94},
+ {WM9705_ID2, 1, WM_READS(188), 188},
+ {WM9705_ID2, 2, WM_READS(375), 375},
+ {WM9705_ID2, 3, WM_READS(750), 750},
+ {WM9712_ID2, 0, WM_READS(94), 94},
+ {WM9712_ID2, 1, WM_READS(188), 188},
+ {WM9712_ID2, 2, WM_READS(375), 375},
+ {WM9712_ID2, 3, WM_READS(750), 750},
+ {WM9713_ID2, 0, WM_READS(94), 94},
+ {WM9713_ID2, 1, WM_READS(120), 120},
+ {WM9713_ID2, 2, WM_READS(154), 154},
+ {WM9713_ID2, 3, WM_READS(188), 188},
+};
+
+/* Continuous speed index. */
+static int sp_idx;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 188;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pen down detection.
+ *
+ * This driver can either poll or use an interrupt to indicate a pen down
+ * event. If the irq request fails then it will fall back to polling mode.
+ */
+static int pen_int = 1;
+module_param(pen_int, int, 0);
+MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure.
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot.
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+/*
+ * GPIO line number.
+ *
+ * Set to GPIO number where the signal from the WM97xx device is hooked up.
+ */
+static int atmel_gpio_line = ATMEL_WM97XX_GPIO_DEFAULT;
+module_param(atmel_gpio_line, int, 0);
+MODULE_PARM_DESC(atmel_gpio_line, "GPIO line number connected to WM97xx");
+
+struct atmel_wm97xx {
+ struct wm97xx *wm;
+ struct timer_list pen_timer;
+ void __iomem *regs;
+ unsigned long ac97c_irq;
+ unsigned long gpio_pen;
+ unsigned long gpio_irq;
+ unsigned short x;
+ unsigned short y;
+};
+
+static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id)
+{
+ struct atmel_wm97xx *atmel_wm97xx = dev_id;
+ struct wm97xx *wm = atmel_wm97xx->wm;
+ int status = ac97c_readl(atmel_wm97xx, CBSR);
+ irqreturn_t retval = IRQ_NONE;
+
+ if (status & AC97C_OVRUN) {
+ dev_dbg(&wm->touch_dev->dev, "AC97C overrun\n");
+ ac97c_readl(atmel_wm97xx, CBRHR);
+ retval = IRQ_HANDLED;
+ } else if (status & AC97C_RXRDY) {
+ u16 data;
+ u16 value;
+ u16 source;
+ u16 pen_down;
+
+ data = ac97c_readl(atmel_wm97xx, CBRHR);
+ value = data & 0x0fff;
+ source = data & WM97XX_ADCSEL_MASK;
+ pen_down = (data & WM97XX_PEN_DOWN) >> 8;
+
+ if (source == WM97XX_ADCSEL_X)
+ atmel_wm97xx->x = value;
+ if (source == WM97XX_ADCSEL_Y)
+ atmel_wm97xx->y = value;
+
+ if (!pressure && source == WM97XX_ADCSEL_Y) {
+ input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
+ input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
+ input_report_key(wm->input_dev, BTN_TOUCH, pen_down);
+ input_sync(wm->input_dev);
+ } else if (pressure && source == WM97XX_ADCSEL_PRES) {
+ input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
+ input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, value);
+ input_report_key(wm->input_dev, BTN_TOUCH, value);
+ input_sync(wm->input_dev);
+ }
+
+ retval = IRQ_HANDLED;
+ }
+
+ return retval;
+}
+
+static void atmel_wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
+ struct input_dev *input_dev = wm->input_dev;
+ int pen_down = gpio_get_value(atmel_wm97xx->gpio_pen);
+
+ if (pen_down != 0) {
+ mod_timer(&atmel_wm97xx->pen_timer,
+ jiffies + msecs_to_jiffies(1));
+ } else {
+ if (pressure)
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+ }
+}
+
+static void atmel_wm97xx_pen_timer(unsigned long data)
+{
+ atmel_wm97xx_acc_pen_up((struct wm97xx *)data);
+}
+
+static int atmel_wm97xx_acc_startup(struct wm97xx *wm)
+{
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
+ int idx = 0;
+
+ if (wm->ac97 == NULL)
+ return -ENODEV;
+
+ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+ if (wm->id != cinfo[idx].id)
+ continue;
+
+ sp_idx = idx;
+
+ if (cont_rate <= cinfo[idx].speed)
+ break;
+ }
+
+ wm->acc_rate = cinfo[sp_idx].code;
+ wm->acc_slot = ac97_touch_slot;
+ dev_info(&wm->touch_dev->dev, "atmel accelerated touchscreen driver, "
+ "%d samples/sec\n", cinfo[sp_idx].speed);
+
+ if (pen_int) {
+ unsigned long reg;
+
+ wm->pen_irq = atmel_wm97xx->gpio_irq;
+
+ switch (wm->id) {
+ case WM9712_ID2: /* Fall through. */
+ case WM9713_ID2:
+ /*
+ * Use GPIO 13 (PEN_DOWN) to assert GPIO line 3
+ * (PENDOWN).
+ */
+ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_STICKY,
+ WM97XX_GPIO_WAKE);
+ wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_NOTSTICKY,
+ WM97XX_GPIO_NOWAKE);
+ case WM9705_ID2: /* Fall through. */
+ /*
+ * Enable touch data slot in AC97 controller channel B.
+ */
+ reg = ac97c_readl(atmel_wm97xx, ICA);
+ reg &= ~AC97C_CH_MASK(wm->acc_slot);
+ reg |= AC97C_CH_ASSIGN(wm->acc_slot, B);
+ ac97c_writel(atmel_wm97xx, ICA, reg);
+
+ /*
+ * Enable channel and interrupt for RXRDY and OVERRUN.
+ */
+ ac97c_writel(atmel_wm97xx, CBMR, AC97C_CMR_CENA
+ | AC97C_CMR_CEM_BIG
+ | AC97C_CMR_SIZE_16
+ | AC97C_OVRUN
+ | AC97C_RXRDY);
+ /* Dummy read to empty RXRHR. */
+ ac97c_readl(atmel_wm97xx, CBRHR);
+ /*
+ * Enable interrupt for channel B in the AC97
+ * controller.
+ */
+ ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
+ break;
+ default:
+ dev_err(&wm->touch_dev->dev, "pen down irq not "
+ "supported on this device\n");
+ pen_int = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void atmel_wm97xx_acc_shutdown(struct wm97xx *wm)
+{
+ if (pen_int) {
+ struct atmel_wm97xx *atmel_wm97xx =
+ platform_get_drvdata(wm->touch_dev);
+ unsigned long ica;
+
+ switch (wm->id & 0xffff) {
+ case WM9705_ID2: /* Fall through. */
+ case WM9712_ID2: /* Fall through. */
+ case WM9713_ID2:
+ /* Disable slot and turn off channel B interrupts. */
+ ica = ac97c_readl(atmel_wm97xx, ICA);
+ ica &= ~AC97C_CH_MASK(wm->acc_slot);
+ ac97c_writel(atmel_wm97xx, ICA, ica);
+ ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+ ac97c_writel(atmel_wm97xx, CBMR, 0);
+ wm->pen_irq = 0;
+ break;
+ default:
+ dev_err(&wm->touch_dev->dev, "unknown codec\n");
+ break;
+ }
+ }
+}
+
+static void atmel_wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+ /* Intentionally left empty. */
+}
+
+static struct wm97xx_mach_ops atmel_mach_ops = {
+ .acc_enabled = 1,
+ .acc_pen_up = atmel_wm97xx_acc_pen_up,
+ .acc_startup = atmel_wm97xx_acc_startup,
+ .acc_shutdown = atmel_wm97xx_acc_shutdown,
+ .irq_enable = atmel_wm97xx_irq_enable,
+ .irq_gpio = WM97XX_GPIO_3,
+};
+
+static int __init atmel_wm97xx_probe(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+ struct atmel_wm97xx *atmel_wm97xx;
+ int ret;
+
+ atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL);
+ if (!atmel_wm97xx) {
+ dev_dbg(&pdev->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ atmel_wm97xx->wm = wm;
+ atmel_wm97xx->regs = (void *)ATMEL_WM97XX_AC97C_IOMEM;
+ atmel_wm97xx->ac97c_irq = ATMEL_WM97XX_AC97C_IRQ;
+ atmel_wm97xx->gpio_pen = atmel_gpio_line;
+ atmel_wm97xx->gpio_irq = gpio_to_irq(atmel_wm97xx->gpio_pen);
+
+ setup_timer(&atmel_wm97xx->pen_timer, atmel_wm97xx_pen_timer,
+ (unsigned long)wm);
+
+ ret = request_irq(atmel_wm97xx->ac97c_irq,
+ atmel_wm97xx_channel_b_interrupt,
+ IRQF_SHARED, "atmel-wm97xx-ch-b", atmel_wm97xx);
+ if (ret) {
+ dev_dbg(&pdev->dev, "could not request ac97c irq\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, atmel_wm97xx);
+
+ ret = wm97xx_register_mach_ops(wm, &atmel_mach_ops);
+ if (ret)
+ goto err_irq;
+
+ return ret;
+
+err_irq:
+ free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
+err:
+ platform_set_drvdata(pdev, NULL);
+ kfree(atmel_wm97xx);
+ return ret;
+}
+
+static int __exit atmel_wm97xx_remove(struct platform_device *pdev)
+{
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+ struct wm97xx *wm = atmel_wm97xx->wm;
+
+ ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+ free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
+ del_timer_sync(&atmel_wm97xx->pen_timer);
+ wm97xx_unregister_mach_ops(wm);
+ platform_set_drvdata(pdev, NULL);
+ kfree(atmel_wm97xx);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int atmel_wm97xx_suspend(struct *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+
+ ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+ disable_irq(atmel_wm97xx->gpio_irq);
+ del_timer_sync(&atmel_wm97xx->pen_timer);
+
+ return 0;
+}
+
+static int atmel_wm97xx_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+ struct wm97xx *wm = atmel_wm97xx->wm;
+
+ if (wm->input_dev->users) {
+ enable_irq(atmel_wm97xx->gpio_irq);
+ ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops,
+ atmel_wm97xx_suspend, atmel_wm97xx_resume);
+
+static struct platform_driver atmel_wm97xx_driver = {
+ .remove = __exit_p(atmel_wm97xx_remove),
+ .driver = {
+ .name = "wm97xx-touch",
+ .owner = THIS_MODULE,
+ .pm = &atmel_wm97xx_pm_ops,
+ },
+};
+
+static int __init atmel_wm97xx_init(void)
+{
+ return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe);
+}
+module_init(atmel_wm97xx_init);
+
+static void __exit atmel_wm97xx_exit(void)
+{
+ platform_driver_unregister(&atmel_wm97xx_driver);
+}
+module_exit(atmel_wm97xx_exit);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
new file mode 100644
index 00000000..19d4ea65
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -0,0 +1,1275 @@
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+/* Version */
+#define MXT_VER_20 20
+#define MXT_VER_21 21
+#define MXT_VER_22 22
+
+/* Slave addresses */
+#define MXT_APP_LOW 0x4a
+#define MXT_APP_HIGH 0x4b
+#define MXT_BOOT_LOW 0x24
+#define MXT_BOOT_HIGH 0x25
+
+/* Firmware */
+#define MXT_FW_NAME "maxtouch.fw"
+
+/* Registers */
+#define MXT_FAMILY_ID 0x00
+#define MXT_VARIANT_ID 0x01
+#define MXT_VERSION 0x02
+#define MXT_BUILD 0x03
+#define MXT_MATRIX_X_SIZE 0x04
+#define MXT_MATRIX_Y_SIZE 0x05
+#define MXT_OBJECT_NUM 0x06
+#define MXT_OBJECT_START 0x07
+
+#define MXT_OBJECT_SIZE 6
+
+/* Object types */
+#define MXT_DEBUG_DIAGNOSTIC_T37 37
+#define MXT_GEN_MESSAGE_T5 5
+#define MXT_GEN_COMMAND_T6 6
+#define MXT_GEN_POWER_T7 7
+#define MXT_GEN_ACQUIRE_T8 8
+#define MXT_GEN_DATASOURCE_T53 53
+#define MXT_TOUCH_MULTI_T9 9
+#define MXT_TOUCH_KEYARRAY_T15 15
+#define MXT_TOUCH_PROXIMITY_T23 23
+#define MXT_TOUCH_PROXKEY_T52 52
+#define MXT_PROCI_GRIPFACE_T20 20
+#define MXT_PROCG_NOISE_T22 22
+#define MXT_PROCI_ONETOUCH_T24 24
+#define MXT_PROCI_TWOTOUCH_T27 27
+#define MXT_PROCI_GRIP_T40 40
+#define MXT_PROCI_PALM_T41 41
+#define MXT_PROCI_TOUCHSUPPRESSION_T42 42
+#define MXT_PROCI_STYLUS_T47 47
+#define MXT_PROCG_NOISESUPPRESSION_T48 48
+#define MXT_SPT_COMMSCONFIG_T18 18
+#define MXT_SPT_GPIOPWM_T19 19
+#define MXT_SPT_SELFTEST_T25 25
+#define MXT_SPT_CTECONFIG_T28 28
+#define MXT_SPT_USERDATA_T38 38
+#define MXT_SPT_DIGITIZER_T43 43
+#define MXT_SPT_MESSAGECOUNT_T44 44
+#define MXT_SPT_CTECONFIG_T46 46
+
+/* MXT_GEN_COMMAND_T6 field */
+#define MXT_COMMAND_RESET 0
+#define MXT_COMMAND_BACKUPNV 1
+#define MXT_COMMAND_CALIBRATE 2
+#define MXT_COMMAND_REPORTALL 3
+#define MXT_COMMAND_DIAGNOSTIC 5
+
+/* MXT_GEN_POWER_T7 field */
+#define MXT_POWER_IDLEACQINT 0
+#define MXT_POWER_ACTVACQINT 1
+#define MXT_POWER_ACTV2IDLETO 2
+
+/* MXT_GEN_ACQUIRE_T8 field */
+#define MXT_ACQUIRE_CHRGTIME 0
+#define MXT_ACQUIRE_TCHDRIFT 2
+#define MXT_ACQUIRE_DRIFTST 3
+#define MXT_ACQUIRE_TCHAUTOCAL 4
+#define MXT_ACQUIRE_SYNC 5
+#define MXT_ACQUIRE_ATCHCALST 6
+#define MXT_ACQUIRE_ATCHCALSTHR 7
+
+/* MXT_TOUCH_MULTI_T9 field */
+#define MXT_TOUCH_CTRL 0
+#define MXT_TOUCH_XORIGIN 1
+#define MXT_TOUCH_YORIGIN 2
+#define MXT_TOUCH_XSIZE 3
+#define MXT_TOUCH_YSIZE 4
+#define MXT_TOUCH_BLEN 6
+#define MXT_TOUCH_TCHTHR 7
+#define MXT_TOUCH_TCHDI 8
+#define MXT_TOUCH_ORIENT 9
+#define MXT_TOUCH_MOVHYSTI 11
+#define MXT_TOUCH_MOVHYSTN 12
+#define MXT_TOUCH_NUMTOUCH 14
+#define MXT_TOUCH_MRGHYST 15
+#define MXT_TOUCH_MRGTHR 16
+#define MXT_TOUCH_AMPHYST 17
+#define MXT_TOUCH_XRANGE_LSB 18
+#define MXT_TOUCH_XRANGE_MSB 19
+#define MXT_TOUCH_YRANGE_LSB 20
+#define MXT_TOUCH_YRANGE_MSB 21
+#define MXT_TOUCH_XLOCLIP 22
+#define MXT_TOUCH_XHICLIP 23
+#define MXT_TOUCH_YLOCLIP 24
+#define MXT_TOUCH_YHICLIP 25
+#define MXT_TOUCH_XEDGECTRL 26
+#define MXT_TOUCH_XEDGEDIST 27
+#define MXT_TOUCH_YEDGECTRL 28
+#define MXT_TOUCH_YEDGEDIST 29
+#define MXT_TOUCH_JUMPLIMIT 30
+
+/* MXT_PROCI_GRIPFACE_T20 field */
+#define MXT_GRIPFACE_CTRL 0
+#define MXT_GRIPFACE_XLOGRIP 1
+#define MXT_GRIPFACE_XHIGRIP 2
+#define MXT_GRIPFACE_YLOGRIP 3
+#define MXT_GRIPFACE_YHIGRIP 4
+#define MXT_GRIPFACE_MAXTCHS 5
+#define MXT_GRIPFACE_SZTHR1 7
+#define MXT_GRIPFACE_SZTHR2 8
+#define MXT_GRIPFACE_SHPTHR1 9
+#define MXT_GRIPFACE_SHPTHR2 10
+#define MXT_GRIPFACE_SUPEXTTO 11
+
+/* MXT_PROCI_NOISE field */
+#define MXT_NOISE_CTRL 0
+#define MXT_NOISE_OUTFLEN 1
+#define MXT_NOISE_GCAFUL_LSB 3
+#define MXT_NOISE_GCAFUL_MSB 4
+#define MXT_NOISE_GCAFLL_LSB 5
+#define MXT_NOISE_GCAFLL_MSB 6
+#define MXT_NOISE_ACTVGCAFVALID 7
+#define MXT_NOISE_NOISETHR 8
+#define MXT_NOISE_FREQHOPSCALE 10
+#define MXT_NOISE_FREQ0 11
+#define MXT_NOISE_FREQ1 12
+#define MXT_NOISE_FREQ2 13
+#define MXT_NOISE_FREQ3 14
+#define MXT_NOISE_FREQ4 15
+#define MXT_NOISE_IDLEGCAFVALID 16
+
+/* MXT_SPT_COMMSCONFIG_T18 */
+#define MXT_COMMS_CTRL 0
+#define MXT_COMMS_CMD 1
+
+/* MXT_SPT_CTECONFIG_T28 field */
+#define MXT_CTE_CTRL 0
+#define MXT_CTE_CMD 1
+#define MXT_CTE_MODE 2
+#define MXT_CTE_IDLEGCAFDEPTH 3
+#define MXT_CTE_ACTVGCAFDEPTH 4
+#define MXT_CTE_VOLTAGE 5
+
+#define MXT_VOLTAGE_DEFAULT 2700000
+#define MXT_VOLTAGE_STEP 10000
+
+/* Define for MXT_GEN_COMMAND_T6 */
+#define MXT_BOOT_VALUE 0xa5
+#define MXT_BACKUP_VALUE 0x55
+#define MXT_BACKUP_TIME 25 /* msec */
+#define MXT_RESET_TIME 65 /* msec */
+
+#define MXT_FWRESET_TIME 175 /* msec */
+
+/* Command to unlock bootloader */
+#define MXT_UNLOCK_CMD_MSB 0xaa
+#define MXT_UNLOCK_CMD_LSB 0xdc
+
+/* Bootloader mode status */
+#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
+#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
+#define MXT_FRAME_CRC_CHECK 0x02
+#define MXT_FRAME_CRC_FAIL 0x03
+#define MXT_FRAME_CRC_PASS 0x04
+#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
+#define MXT_BOOT_STATUS_MASK 0x3f
+
+/* Touch status */
+#define MXT_SUPPRESS (1 << 1)
+#define MXT_AMP (1 << 2)
+#define MXT_VECTOR (1 << 3)
+#define MXT_MOVE (1 << 4)
+#define MXT_RELEASE (1 << 5)
+#define MXT_PRESS (1 << 6)
+#define MXT_DETECT (1 << 7)
+
+/* Touch orient bits */
+#define MXT_XY_SWITCH (1 << 0)
+#define MXT_X_INVERT (1 << 1)
+#define MXT_Y_INVERT (1 << 2)
+
+/* Touchscreen absolute values */
+#define MXT_MAX_AREA 0xff
+
+#define MXT_MAX_FINGER 10
+
+struct mxt_info {
+ u8 family_id;
+ u8 variant_id;
+ u8 version;
+ u8 build;
+ u8 matrix_xsize;
+ u8 matrix_ysize;
+ u8 object_num;
+};
+
+struct mxt_object {
+ u8 type;
+ u16 start_address;
+ u8 size;
+ u8 instances;
+ u8 num_report_ids;
+
+ /* to map object and message */
+ u8 max_reportid;
+};
+
+struct mxt_message {
+ u8 reportid;
+ u8 message[7];
+ u8 checksum;
+};
+
+struct mxt_finger {
+ int status;
+ int x;
+ int y;
+ int area;
+ int pressure;
+};
+
+/* Each client has this additional data */
+struct mxt_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ const struct mxt_platform_data *pdata;
+ struct mxt_object *object_table;
+ struct mxt_info info;
+ struct mxt_finger finger[MXT_MAX_FINGER];
+ unsigned int irq;
+ unsigned int max_x;
+ unsigned int max_y;
+};
+
+static bool mxt_object_readable(unsigned int type)
+{
+ switch (type) {
+ case MXT_GEN_MESSAGE_T5:
+ case MXT_GEN_COMMAND_T6:
+ case MXT_GEN_POWER_T7:
+ case MXT_GEN_ACQUIRE_T8:
+ case MXT_GEN_DATASOURCE_T53:
+ case MXT_TOUCH_MULTI_T9:
+ case MXT_TOUCH_KEYARRAY_T15:
+ case MXT_TOUCH_PROXIMITY_T23:
+ case MXT_TOUCH_PROXKEY_T52:
+ case MXT_PROCI_GRIPFACE_T20:
+ case MXT_PROCG_NOISE_T22:
+ case MXT_PROCI_ONETOUCH_T24:
+ case MXT_PROCI_TWOTOUCH_T27:
+ case MXT_PROCI_GRIP_T40:
+ case MXT_PROCI_PALM_T41:
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ case MXT_SPT_COMMSCONFIG_T18:
+ case MXT_SPT_GPIOPWM_T19:
+ case MXT_SPT_SELFTEST_T25:
+ case MXT_SPT_CTECONFIG_T28:
+ case MXT_SPT_USERDATA_T38:
+ case MXT_SPT_DIGITIZER_T43:
+ case MXT_SPT_CTECONFIG_T46:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mxt_object_writable(unsigned int type)
+{
+ switch (type) {
+ case MXT_GEN_COMMAND_T6:
+ case MXT_GEN_POWER_T7:
+ case MXT_GEN_ACQUIRE_T8:
+ case MXT_TOUCH_MULTI_T9:
+ case MXT_TOUCH_KEYARRAY_T15:
+ case MXT_TOUCH_PROXIMITY_T23:
+ case MXT_TOUCH_PROXKEY_T52:
+ case MXT_PROCI_GRIPFACE_T20:
+ case MXT_PROCG_NOISE_T22:
+ case MXT_PROCI_ONETOUCH_T24:
+ case MXT_PROCI_TWOTOUCH_T27:
+ case MXT_PROCI_GRIP_T40:
+ case MXT_PROCI_PALM_T41:
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ case MXT_SPT_COMMSCONFIG_T18:
+ case MXT_SPT_GPIOPWM_T19:
+ case MXT_SPT_SELFTEST_T25:
+ case MXT_SPT_CTECONFIG_T28:
+ case MXT_SPT_DIGITIZER_T43:
+ case MXT_SPT_CTECONFIG_T46:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void mxt_dump_message(struct device *dev,
+ struct mxt_message *message)
+{
+ dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
+ dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
+ dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
+ dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
+ dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
+ dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
+ dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
+ dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
+ dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
+}
+
+static int mxt_check_bootloader(struct i2c_client *client,
+ unsigned int state)
+{
+ u8 val;
+
+recheck:
+ if (i2c_master_recv(client, &val, 1) != 1) {
+ dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+ return -EIO;
+ }
+
+ switch (state) {
+ case MXT_WAITING_BOOTLOAD_CMD:
+ case MXT_WAITING_FRAME_DATA:
+ val &= ~MXT_BOOT_STATUS_MASK;
+ break;
+ case MXT_FRAME_CRC_PASS:
+ if (val == MXT_FRAME_CRC_CHECK)
+ goto recheck;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val != state) {
+ dev_err(&client->dev, "Unvalid bootloader mode state\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxt_unlock_bootloader(struct i2c_client *client)
+{
+ u8 buf[2];
+
+ buf[0] = MXT_UNLOCK_CMD_LSB;
+ buf[1] = MXT_UNLOCK_CMD_MSB;
+
+ if (i2c_master_send(client, buf, 2) != 2) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_fw_write(struct i2c_client *client,
+ const u8 *data, unsigned int frame_size)
+{
+ if (i2c_master_send(client, data, frame_size) != frame_size) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int __mxt_read_reg(struct i2c_client *client,
+ u16 reg, u16 len, void *val)
+{
+ struct i2c_msg xfer[2];
+ u8 buf[2];
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 2;
+ xfer[0].buf = buf;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = len;
+ xfer[1].buf = val;
+
+ if (i2c_transfer(client->adapter, xfer, 2) != 2) {
+ dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
+{
+ return __mxt_read_reg(client, reg, 1, val);
+}
+
+static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+{
+ u8 buf[3];
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ buf[2] = val;
+
+ if (i2c_master_send(client, buf, 3) != 3) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_read_object_table(struct i2c_client *client,
+ u16 reg, u8 *object_buf)
+{
+ return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE,
+ object_buf);
+}
+
+static struct mxt_object *
+mxt_get_object(struct mxt_data *data, u8 type)
+{
+ struct mxt_object *object;
+ int i;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+ if (object->type == type)
+ return object;
+ }
+
+ dev_err(&data->client->dev, "Invalid object type\n");
+ return NULL;
+}
+
+static int mxt_read_message(struct mxt_data *data,
+ struct mxt_message *message)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, MXT_GEN_MESSAGE_T5);
+ if (!object)
+ return -EINVAL;
+
+ reg = object->start_address;
+ return __mxt_read_reg(data->client, reg,
+ sizeof(struct mxt_message), message);
+}
+
+static int mxt_read_object(struct mxt_data *data,
+ u8 type, u8 offset, u8 *val)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, type);
+ if (!object)
+ return -EINVAL;
+
+ reg = object->start_address;
+ return __mxt_read_reg(data->client, reg + offset, 1, val);
+}
+
+static int mxt_write_object(struct mxt_data *data,
+ u8 type, u8 offset, u8 val)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, type);
+ if (!object)
+ return -EINVAL;
+
+ reg = object->start_address;
+ return mxt_write_reg(data->client, reg + offset, val);
+}
+
+static void mxt_input_report(struct mxt_data *data, int single_id)
+{
+ struct mxt_finger *finger = data->finger;
+ struct input_dev *input_dev = data->input_dev;
+ int status = finger[single_id].status;
+ int finger_num = 0;
+ int id;
+
+ for (id = 0; id < MXT_MAX_FINGER; id++) {
+ if (!finger[id].status)
+ continue;
+
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+ finger[id].status != MXT_RELEASE);
+
+ if (finger[id].status != MXT_RELEASE) {
+ finger_num++;
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ finger[id].area);
+ 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_report_abs(input_dev, ABS_MT_PRESSURE,
+ finger[id].pressure);
+ } else {
+ finger[id].status = 0;
+ }
+ }
+
+ input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
+
+ if (status != MXT_RELEASE) {
+ input_report_abs(input_dev, ABS_X, finger[single_id].x);
+ input_report_abs(input_dev, ABS_Y, finger[single_id].y);
+ input_report_abs(input_dev,
+ ABS_PRESSURE, finger[single_id].pressure);
+ }
+
+ input_sync(input_dev);
+}
+
+static void mxt_input_touchevent(struct mxt_data *data,
+ struct mxt_message *message, int id)
+{
+ struct mxt_finger *finger = data->finger;
+ struct device *dev = &data->client->dev;
+ u8 status = message->message[0];
+ int x;
+ int y;
+ int area;
+ int pressure;
+
+ /* Check the touch is present on the screen */
+ if (!(status & MXT_DETECT)) {
+ if (status & MXT_RELEASE) {
+ dev_dbg(dev, "[%d] released\n", id);
+
+ finger[id].status = MXT_RELEASE;
+ mxt_input_report(data, id);
+ }
+ return;
+ }
+
+ /* Check only AMP detection */
+ if (!(status & (MXT_PRESS | MXT_MOVE)))
+ return;
+
+ x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
+ y = (message->message[2] << 4) | ((message->message[3] & 0xf));
+ if (data->max_x < 1024)
+ x = x >> 2;
+ if (data->max_y < 1024)
+ y = y >> 2;
+
+ area = message->message[4];
+ pressure = message->message[5];
+
+ dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
+ status & MXT_MOVE ? "moved" : "pressed",
+ x, y, area);
+
+ finger[id].status = status & MXT_MOVE ?
+ MXT_MOVE : MXT_PRESS;
+ finger[id].x = x;
+ finger[id].y = y;
+ finger[id].area = area;
+ finger[id].pressure = pressure;
+
+ mxt_input_report(data, id);
+}
+
+static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+{
+ struct mxt_data *data = dev_id;
+ struct mxt_message message;
+ struct mxt_object *object;
+ struct device *dev = &data->client->dev;
+ int id;
+ u8 reportid;
+ u8 max_reportid;
+ u8 min_reportid;
+
+ do {
+ if (mxt_read_message(data, &message)) {
+ dev_err(dev, "Failed to read message\n");
+ goto end;
+ }
+
+ reportid = message.reportid;
+
+ /* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
+ object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
+ if (!object)
+ goto end;
+
+ max_reportid = object->max_reportid;
+ min_reportid = max_reportid - object->num_report_ids + 1;
+ id = reportid - min_reportid;
+
+ if (reportid >= min_reportid && reportid <= max_reportid)
+ mxt_input_touchevent(data, &message, id);
+ else
+ mxt_dump_message(dev, &message);
+ } while (reportid != 0xff);
+
+end:
+ return IRQ_HANDLED;
+}
+
+static int mxt_check_reg_init(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct mxt_object *object;
+ struct device *dev = &data->client->dev;
+ int index = 0;
+ int i, j, config_offset;
+
+ if (!pdata->config) {
+ dev_dbg(dev, "No cfg data defined, skipping reg init\n");
+ return 0;
+ }
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+
+ if (!mxt_object_writable(object->type))
+ continue;
+
+ for (j = 0;
+ j < (object->size + 1) * (object->instances + 1);
+ j++) {
+ config_offset = index + j;
+ if (config_offset > pdata->config_length) {
+ dev_err(dev, "Not enough config data!\n");
+ return -EINVAL;
+ }
+ mxt_write_object(data, object->type, j,
+ pdata->config[config_offset]);
+ }
+ index += (object->size + 1) * (object->instances + 1);
+ }
+
+ return 0;
+}
+
+static int mxt_make_highchg(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_message message;
+ int count = 10;
+ int error;
+
+ /* Read dummy message to make high CHG pin */
+ do {
+ error = mxt_read_message(data, &message);
+ if (error)
+ return error;
+ } while (message.reportid != 0xff && --count);
+
+ if (!count) {
+ dev_err(dev, "CHG pin isn't cleared\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void mxt_handle_pdata(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ u8 voltage;
+
+ /* Set touchscreen lines */
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE,
+ pdata->x_line);
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE,
+ pdata->y_line);
+
+ /* Set touchscreen orient */
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT,
+ pdata->orient);
+
+ /* Set touchscreen burst length */
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+ MXT_TOUCH_BLEN, pdata->blen);
+
+ /* Set touchscreen threshold */
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+ MXT_TOUCH_TCHTHR, pdata->threshold);
+
+ /* Set touchscreen resolution */
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+ MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+ MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+ MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
+ mxt_write_object(data, MXT_TOUCH_MULTI_T9,
+ MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
+
+ /* Set touchscreen voltage */
+ if (pdata->voltage) {
+ if (pdata->voltage < MXT_VOLTAGE_DEFAULT) {
+ voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) /
+ MXT_VOLTAGE_STEP;
+ voltage = 0xff - voltage + 1;
+ } else
+ voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
+ MXT_VOLTAGE_STEP;
+
+ mxt_write_object(data, MXT_SPT_CTECONFIG_T28,
+ MXT_CTE_VOLTAGE, voltage);
+ }
+}
+
+static int mxt_get_info(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct mxt_info *info = &data->info;
+ int error;
+ u8 val;
+
+ error = mxt_read_reg(client, MXT_FAMILY_ID, &val);
+ if (error)
+ return error;
+ info->family_id = val;
+
+ error = mxt_read_reg(client, MXT_VARIANT_ID, &val);
+ if (error)
+ return error;
+ info->variant_id = val;
+
+ error = mxt_read_reg(client, MXT_VERSION, &val);
+ if (error)
+ return error;
+ info->version = val;
+
+ error = mxt_read_reg(client, MXT_BUILD, &val);
+ if (error)
+ return error;
+ info->build = val;
+
+ error = mxt_read_reg(client, MXT_OBJECT_NUM, &val);
+ if (error)
+ return error;
+ info->object_num = val;
+
+ return 0;
+}
+
+static int mxt_get_object_table(struct mxt_data *data)
+{
+ int error;
+ int i;
+ u16 reg;
+ u8 reportid = 0;
+ u8 buf[MXT_OBJECT_SIZE];
+
+ for (i = 0; i < data->info.object_num; i++) {
+ struct mxt_object *object = data->object_table + i;
+
+ reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i;
+ error = mxt_read_object_table(data->client, reg, buf);
+ if (error)
+ return error;
+
+ object->type = buf[0];
+ object->start_address = (buf[2] << 8) | buf[1];
+ object->size = buf[3];
+ object->instances = buf[4];
+ object->num_report_ids = buf[5];
+
+ if (object->num_report_ids) {
+ reportid += object->num_report_ids *
+ (object->instances + 1);
+ object->max_reportid = reportid;
+ }
+ }
+
+ return 0;
+}
+
+static int mxt_initialize(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct mxt_info *info = &data->info;
+ int error;
+ u8 val;
+
+ error = mxt_get_info(data);
+ if (error)
+ return error;
+
+ data->object_table = kcalloc(info->object_num,
+ sizeof(struct mxt_object),
+ GFP_KERNEL);
+ if (!data->object_table) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ /* Get object table information */
+ error = mxt_get_object_table(data);
+ if (error)
+ return error;
+
+ /* Check register init values */
+ error = mxt_check_reg_init(data);
+ if (error)
+ return error;
+
+ mxt_handle_pdata(data);
+
+ /* Backup to memory */
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
+ MXT_COMMAND_BACKUPNV,
+ MXT_BACKUP_VALUE);
+ msleep(MXT_BACKUP_TIME);
+
+ /* Soft reset */
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
+ MXT_COMMAND_RESET, 1);
+ msleep(MXT_RESET_TIME);
+
+ /* Update matrix size at info struct */
+ error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
+ if (error)
+ return error;
+ info->matrix_xsize = val;
+
+ error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
+ if (error)
+ return error;
+ info->matrix_ysize = val;
+
+ dev_info(&client->dev,
+ "Family ID: %d Variant ID: %d Version: %d Build: %d\n",
+ info->family_id, info->variant_id, info->version,
+ info->build);
+
+ dev_info(&client->dev,
+ "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
+ info->matrix_xsize, info->matrix_ysize,
+ info->object_num);
+
+ return 0;
+}
+
+static void mxt_calc_resolution(struct mxt_data *data)
+{
+ unsigned int max_x = data->pdata->x_size - 1;
+ unsigned int max_y = data->pdata->y_size - 1;
+
+ if (data->pdata->orient & MXT_XY_SWITCH) {
+ data->max_x = max_y;
+ data->max_y = max_x;
+ } else {
+ data->max_x = max_x;
+ data->max_y = max_y;
+ }
+}
+
+static ssize_t mxt_object_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_object *object;
+ int count = 0;
+ int i, j;
+ int error;
+ u8 val;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "Object[%d] (Type %d)\n",
+ i + 1, object->type);
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
+
+ if (!mxt_object_readable(object->type)) {
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\n");
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
+ continue;
+ }
+
+ for (j = 0; j < object->size + 1; j++) {
+ error = mxt_read_object(data,
+ object->type, j, &val);
+ if (error)
+ return error;
+
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\t[%2d]: %02x (%d)\n", j, val, val);
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
+ }
+
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ if (count >= PAGE_SIZE)
+ return PAGE_SIZE - 1;
+ }
+
+ return count;
+}
+
+static int mxt_load_fw(struct device *dev, const char *fn)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ const struct firmware *fw = NULL;
+ unsigned int frame_size;
+ unsigned int pos = 0;
+ int ret;
+
+ ret = request_firmware(&fw, fn, dev);
+ if (ret) {
+ dev_err(dev, "Unable to open firmware %s\n", fn);
+ return ret;
+ }
+
+ /* Change to the bootloader mode */
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
+ MXT_COMMAND_RESET, MXT_BOOT_VALUE);
+ msleep(MXT_RESET_TIME);
+
+ /* Change to slave address of bootloader */
+ if (client->addr == MXT_APP_LOW)
+ client->addr = MXT_BOOT_LOW;
+ else
+ client->addr = MXT_BOOT_HIGH;
+
+ ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
+ if (ret)
+ goto out;
+
+ /* Unlock bootloader */
+ mxt_unlock_bootloader(client);
+
+ while (pos < fw->size) {
+ ret = mxt_check_bootloader(client,
+ MXT_WAITING_FRAME_DATA);
+ if (ret)
+ goto out;
+
+ frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+
+ /* We should add 2 at frame size as the the firmware data is not
+ * included the CRC bytes.
+ */
+ frame_size += 2;
+
+ /* Write one frame to device */
+ mxt_fw_write(client, fw->data + pos, frame_size);
+
+ ret = mxt_check_bootloader(client,
+ MXT_FRAME_CRC_PASS);
+ if (ret)
+ goto out;
+
+ pos += frame_size;
+
+ dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
+ }
+
+out:
+ release_firmware(fw);
+
+ /* Change to slave address of application */
+ if (client->addr == MXT_BOOT_LOW)
+ client->addr = MXT_APP_LOW;
+ else
+ client->addr = MXT_APP_HIGH;
+
+ return ret;
+}
+
+static ssize_t mxt_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int error;
+
+ disable_irq(data->irq);
+
+ error = mxt_load_fw(dev, MXT_FW_NAME);
+ if (error) {
+ dev_err(dev, "The firmware update failed(%d)\n", error);
+ count = error;
+ } else {
+ dev_dbg(dev, "The firmware update succeeded\n");
+
+ /* Wait for reset */
+ msleep(MXT_FWRESET_TIME);
+
+ kfree(data->object_table);
+ data->object_table = NULL;
+
+ mxt_initialize(data);
+ }
+
+ enable_irq(data->irq);
+
+ error = mxt_make_highchg(data);
+ if (error)
+ return error;
+
+ return count;
+}
+
+static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
+
+static struct attribute *mxt_attrs[] = {
+ &dev_attr_object.attr,
+ &dev_attr_update_fw.attr,
+ NULL
+};
+
+static const struct attribute_group mxt_attr_group = {
+ .attrs = mxt_attrs,
+};
+
+static void mxt_start(struct mxt_data *data)
+{
+ /* Touch enable */
+ mxt_write_object(data,
+ MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
+}
+
+static void mxt_stop(struct mxt_data *data)
+{
+ /* Touch disable */
+ mxt_write_object(data,
+ MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
+}
+
+static int mxt_input_open(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_start(data);
+
+ return 0;
+}
+
+static void mxt_input_close(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_stop(data);
+}
+
+static int __devinit mxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct mxt_platform_data *pdata = client->dev.platform_data;
+ struct mxt_data *data;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!pdata)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ input_dev->name = "Atmel maXTouch Touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->pdata = pdata;
+ data->irq = client->irq;
+
+ mxt_calc_resolution(data);
+
+ __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, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, 255, 0, 0);
+
+ /* For multi touch */
+ input_mt_init_slots(input_dev, MXT_MAX_FINGER);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+ i2c_set_clientdata(client, data);
+
+ error = mxt_initialize(data);
+ if (error)
+ goto err_free_object;
+
+ error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
+ pdata->irqflags, client->dev.driver->name, data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_object;
+ }
+
+ error = mxt_make_highchg(data);
+ if (error)
+ goto err_free_irq;
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_free_irq;
+
+ error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
+ if (error)
+ goto err_unregister_device;
+
+ return 0;
+
+err_unregister_device:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_object:
+ kfree(data->object_table);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+ return error;
+}
+
+static int __devexit mxt_remove(struct i2c_client *client)
+{
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+ free_irq(data->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data->object_table);
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxt_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_stop(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int mxt_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ /* Soft reset */
+ mxt_write_object(data, MXT_GEN_COMMAND_T6,
+ MXT_COMMAND_RESET, 1);
+
+ msleep(MXT_RESET_TIME);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_start(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mxt_pm_ops = {
+ .suspend = mxt_suspend,
+ .resume = mxt_resume,
+};
+#endif
+
+static const struct i2c_device_id mxt_id[] = {
+ { "qt602240_ts", 0 },
+ { "atmel_mxt_ts", 0 },
+ { "mXT224", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mxt_id);
+
+static struct i2c_driver mxt_driver = {
+ .driver = {
+ .name = "atmel_mxt_ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &mxt_pm_ops,
+#endif
+ },
+ .probe = mxt_probe,
+ .remove = __devexit_p(mxt_remove),
+ .id_table = mxt_id,
+};
+
+module_i2c_driver(mxt_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c
new file mode 100644
index 00000000..201b2d2e
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_tsadcc.c
@@ -0,0 +1,359 @@
+/*
+ * Atmel Touch Screen Driver
+ *
+ * Copyright (c) 2008 ATMEL
+ * Copyright (c) 2008 Dan Liang
+ * Copyright (c) 2008 TimeSys Corporation
+ * Copyright (c) 2008 Justin Waters
+ *
+ * Based on touchscreen code from Atmel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
+
+/* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */
+
+#define ATMEL_TSADCC_CR 0x00 /* Control register */
+#define ATMEL_TSADCC_SWRST (1 << 0) /* Software Reset*/
+#define ATMEL_TSADCC_START (1 << 1) /* Start conversion */
+
+#define ATMEL_TSADCC_MR 0x04 /* Mode register */
+#define ATMEL_TSADCC_TSAMOD (3 << 0) /* ADC mode */
+#define ATMEL_TSADCC_TSAMOD_ADC_ONLY_MODE (0x0) /* ADC Mode */
+#define ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE (0x1) /* Touch Screen Only Mode */
+#define ATMEL_TSADCC_LOWRES (1 << 4) /* Resolution selection */
+#define ATMEL_TSADCC_SLEEP (1 << 5) /* Sleep mode */
+#define ATMEL_TSADCC_PENDET (1 << 6) /* Pen Detect selection */
+#define ATMEL_TSADCC_PRES (1 << 7) /* Pressure Measurement Selection */
+#define ATMEL_TSADCC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */
+#define ATMEL_TSADCC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */
+#define ATMEL_TSADCC_STARTUP (0x7f << 16) /* Start Up time */
+#define ATMEL_TSADCC_SHTIM (0xf << 24) /* Sample & Hold time */
+#define ATMEL_TSADCC_PENDBC (0xf << 28) /* Pen Detect debouncing time */
+
+#define ATMEL_TSADCC_TRGR 0x08 /* Trigger register */
+#define ATMEL_TSADCC_TRGMOD (7 << 0) /* Trigger mode */
+#define ATMEL_TSADCC_TRGMOD_NONE (0 << 0)
+#define ATMEL_TSADCC_TRGMOD_EXT_RISING (1 << 0)
+#define ATMEL_TSADCC_TRGMOD_EXT_FALLING (2 << 0)
+#define ATMEL_TSADCC_TRGMOD_EXT_ANY (3 << 0)
+#define ATMEL_TSADCC_TRGMOD_PENDET (4 << 0)
+#define ATMEL_TSADCC_TRGMOD_PERIOD (5 << 0)
+#define ATMEL_TSADCC_TRGMOD_CONTINUOUS (6 << 0)
+#define ATMEL_TSADCC_TRGPER (0xffff << 16) /* Trigger period */
+
+#define ATMEL_TSADCC_TSR 0x0C /* Touch Screen register */
+#define ATMEL_TSADCC_TSFREQ (0xf << 0) /* TS Frequency in Interleaved mode */
+#define ATMEL_TSADCC_TSSHTIM (0xf << 24) /* Sample & Hold time */
+
+#define ATMEL_TSADCC_CHER 0x10 /* Channel Enable register */
+#define ATMEL_TSADCC_CHDR 0x14 /* Channel Disable register */
+#define ATMEL_TSADCC_CHSR 0x18 /* Channel Status register */
+#define ATMEL_TSADCC_CH(n) (1 << (n)) /* Channel number */
+
+#define ATMEL_TSADCC_SR 0x1C /* Status register */
+#define ATMEL_TSADCC_EOC(n) (1 << ((n)+0)) /* End of conversion for channel N */
+#define ATMEL_TSADCC_OVRE(n) (1 << ((n)+8)) /* Overrun error for channel N */
+#define ATMEL_TSADCC_DRDY (1 << 16) /* Data Ready */
+#define ATMEL_TSADCC_GOVRE (1 << 17) /* General Overrun Error */
+#define ATMEL_TSADCC_ENDRX (1 << 18) /* End of RX Buffer */
+#define ATMEL_TSADCC_RXBUFF (1 << 19) /* TX Buffer full */
+#define ATMEL_TSADCC_PENCNT (1 << 20) /* Pen contact */
+#define ATMEL_TSADCC_NOCNT (1 << 21) /* No contact */
+
+#define ATMEL_TSADCC_LCDR 0x20 /* Last Converted Data register */
+#define ATMEL_TSADCC_DATA (0x3ff << 0) /* Channel data */
+
+#define ATMEL_TSADCC_IER 0x24 /* Interrupt Enable register */
+#define ATMEL_TSADCC_IDR 0x28 /* Interrupt Disable register */
+#define ATMEL_TSADCC_IMR 0x2C /* Interrupt Mask register */
+#define ATMEL_TSADCC_CDR0 0x30 /* Channel Data 0 */
+#define ATMEL_TSADCC_CDR1 0x34 /* Channel Data 1 */
+#define ATMEL_TSADCC_CDR2 0x38 /* Channel Data 2 */
+#define ATMEL_TSADCC_CDR3 0x3C /* Channel Data 3 */
+#define ATMEL_TSADCC_CDR4 0x40 /* Channel Data 4 */
+#define ATMEL_TSADCC_CDR5 0x44 /* Channel Data 5 */
+
+#define ATMEL_TSADCC_XPOS 0x50
+#define ATMEL_TSADCC_Z1DAT 0x54
+#define ATMEL_TSADCC_Z2DAT 0x58
+
+#define PRESCALER_VAL(x) ((x) >> 8)
+
+#define ADC_DEFAULT_CLOCK 100000
+
+struct atmel_tsadcc {
+ struct input_dev *input;
+ char phys[32];
+ struct clk *clk;
+ int irq;
+ unsigned int prev_absx;
+ unsigned int prev_absy;
+ unsigned char bufferedmeasure;
+};
+
+static void __iomem *tsc_base;
+
+#define atmel_tsadcc_read(reg) __raw_readl(tsc_base + (reg))
+#define atmel_tsadcc_write(reg, val) __raw_writel((val), tsc_base + (reg))
+
+static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev)
+{
+ struct atmel_tsadcc *ts_dev = (struct atmel_tsadcc *)dev;
+ struct input_dev *input_dev = ts_dev->input;
+
+ unsigned int status;
+ unsigned int reg;
+
+ status = atmel_tsadcc_read(ATMEL_TSADCC_SR);
+ status &= atmel_tsadcc_read(ATMEL_TSADCC_IMR);
+
+ if (status & ATMEL_TSADCC_NOCNT) {
+ /* Contact lost */
+ reg = atmel_tsadcc_read(ATMEL_TSADCC_MR) | ATMEL_TSADCC_PENDBC;
+
+ atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
+ atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE);
+ atmel_tsadcc_write(ATMEL_TSADCC_IDR,
+ ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT);
+ atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
+
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ ts_dev->bufferedmeasure = 0;
+ input_sync(input_dev);
+
+ } else if (status & ATMEL_TSADCC_PENCNT) {
+ /* Pen detected */
+ reg = atmel_tsadcc_read(ATMEL_TSADCC_MR);
+ reg &= ~ATMEL_TSADCC_PENDBC;
+
+ atmel_tsadcc_write(ATMEL_TSADCC_IDR, ATMEL_TSADCC_PENCNT);
+ atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
+ atmel_tsadcc_write(ATMEL_TSADCC_IER,
+ ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT);
+ atmel_tsadcc_write(ATMEL_TSADCC_TRGR,
+ ATMEL_TSADCC_TRGMOD_PERIOD | (0x0FFF << 16));
+
+ } else if (status & ATMEL_TSADCC_EOC(3)) {
+ /* Conversion finished */
+
+ if (ts_dev->bufferedmeasure) {
+ /* Last measurement is always discarded, since it can
+ * be erroneous.
+ * Always report previous measurement */
+ input_report_abs(input_dev, ABS_X, ts_dev->prev_absx);
+ input_report_abs(input_dev, ABS_Y, ts_dev->prev_absy);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_sync(input_dev);
+ } else
+ ts_dev->bufferedmeasure = 1;
+
+ /* Now make new measurement */
+ ts_dev->prev_absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10;
+ ts_dev->prev_absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2);
+
+ ts_dev->prev_absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10;
+ ts_dev->prev_absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
+{
+ struct atmel_tsadcc *ts_dev;
+ struct input_dev *input_dev;
+ struct resource *res;
+ struct at91_tsadcc_data *pdata = pdev->dev.platform_data;
+ int err = 0;
+ unsigned int prsc;
+ unsigned int reg;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no mmio resource defined.\n");
+ return -ENXIO;
+ }
+
+ /* Allocate memory for device */
+ ts_dev = kzalloc(sizeof(struct atmel_tsadcc), GFP_KERNEL);
+ if (!ts_dev) {
+ dev_err(&pdev->dev, "failed to allocate memory.\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, ts_dev);
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&pdev->dev, "failed to allocate input device.\n");
+ err = -EBUSY;
+ goto err_free_mem;
+ }
+
+ ts_dev->irq = platform_get_irq(pdev, 0);
+ if (ts_dev->irq < 0) {
+ dev_err(&pdev->dev, "no irq ID is designated.\n");
+ err = -ENODEV;
+ goto err_free_dev;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ "atmel tsadcc regs")) {
+ dev_err(&pdev->dev, "resources is unavailable.\n");
+ err = -EBUSY;
+ goto err_free_dev;
+ }
+
+ tsc_base = ioremap(res->start, resource_size(res));
+ if (!tsc_base) {
+ dev_err(&pdev->dev, "failed to map registers.\n");
+ err = -ENOMEM;
+ goto err_release_mem;
+ }
+
+ err = request_irq(ts_dev->irq, atmel_tsadcc_interrupt, 0,
+ pdev->dev.driver->name, ts_dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to allocate irq.\n");
+ goto err_unmap_regs;
+ }
+
+ ts_dev->clk = clk_get(&pdev->dev, "tsc_clk");
+ if (IS_ERR(ts_dev->clk)) {
+ dev_err(&pdev->dev, "failed to get ts_clk\n");
+ err = PTR_ERR(ts_dev->clk);
+ goto err_free_irq;
+ }
+
+ ts_dev->input = input_dev;
+ ts_dev->bufferedmeasure = 0;
+
+ snprintf(ts_dev->phys, sizeof(ts_dev->phys),
+ "%s/input0", dev_name(&pdev->dev));
+
+ input_dev->name = "atmel touch screen controller";
+ input_dev->phys = ts_dev->phys;
+ input_dev->dev.parent = &pdev->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0);
+
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+ /* clk_enable() always returns 0, no need to check it */
+ clk_enable(ts_dev->clk);
+
+ prsc = clk_get_rate(ts_dev->clk);
+ dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc);
+
+ if (!pdata)
+ goto err_fail;
+
+ if (!pdata->adc_clock)
+ pdata->adc_clock = ADC_DEFAULT_CLOCK;
+
+ prsc = (prsc / (2 * pdata->adc_clock)) - 1;
+
+ /* saturate if this value is too high */
+ if (cpu_is_at91sam9rl()) {
+ if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL))
+ prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL);
+ } else {
+ if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL))
+ prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL);
+ }
+
+ dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc);
+
+ reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE |
+ ((0x00 << 5) & ATMEL_TSADCC_SLEEP) | /* Normal Mode */
+ ((0x01 << 6) & ATMEL_TSADCC_PENDET) | /* Enable Pen Detect */
+ (prsc << 8) |
+ ((0x26 << 16) & ATMEL_TSADCC_STARTUP) |
+ ((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC);
+
+ atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST);
+ atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
+ atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE);
+ atmel_tsadcc_write(ATMEL_TSADCC_TSR,
+ (pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM);
+
+ atmel_tsadcc_read(ATMEL_TSADCC_SR);
+ atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
+
+ /* All went ok, so register to the input system */
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_fail;
+
+ return 0;
+
+err_fail:
+ clk_disable(ts_dev->clk);
+ clk_put(ts_dev->clk);
+err_free_irq:
+ free_irq(ts_dev->irq, ts_dev);
+err_unmap_regs:
+ iounmap(tsc_base);
+err_release_mem:
+ release_mem_region(res->start, resource_size(res));
+err_free_dev:
+ input_free_device(input_dev);
+err_free_mem:
+ kfree(ts_dev);
+ return err;
+}
+
+static int __devexit atmel_tsadcc_remove(struct platform_device *pdev)
+{
+ struct atmel_tsadcc *ts_dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ free_irq(ts_dev->irq, ts_dev);
+
+ input_unregister_device(ts_dev->input);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iounmap(tsc_base);
+ release_mem_region(res->start, resource_size(res));
+
+ clk_disable(ts_dev->clk);
+ clk_put(ts_dev->clk);
+
+ kfree(ts_dev);
+
+ return 0;
+}
+
+static struct platform_driver atmel_tsadcc_driver = {
+ .probe = atmel_tsadcc_probe,
+ .remove = __devexit_p(atmel_tsadcc_remove),
+ .driver = {
+ .name = "atmel_tsadcc",
+ },
+};
+module_platform_driver(atmel_tsadcc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel TouchScreen Driver");
+MODULE_AUTHOR("Dan Liang <dan.liang@atmel.com>");
+
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
new file mode 100644
index 00000000..c7047b6b
--- /dev/null
+++ b/drivers/input/touchscreen/auo-pixcir-ts.c
@@ -0,0 +1,642 @@
+/*
+ * Driver for AUO in-cell touchscreens
+ *
+ * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * loosely based on auo_touch.c from Dell Streak vendor-kernel
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input/auo-pixcir-ts.h>
+
+/*
+ * Coordinate calculation:
+ * X1 = X1_LSB + X1_MSB*256
+ * Y1 = Y1_LSB + Y1_MSB*256
+ * X2 = X2_LSB + X2_MSB*256
+ * Y2 = Y2_LSB + Y2_MSB*256
+ */
+#define AUO_PIXCIR_REG_X1_LSB 0x00
+#define AUO_PIXCIR_REG_X1_MSB 0x01
+#define AUO_PIXCIR_REG_Y1_LSB 0x02
+#define AUO_PIXCIR_REG_Y1_MSB 0x03
+#define AUO_PIXCIR_REG_X2_LSB 0x04
+#define AUO_PIXCIR_REG_X2_MSB 0x05
+#define AUO_PIXCIR_REG_Y2_LSB 0x06
+#define AUO_PIXCIR_REG_Y2_MSB 0x07
+
+#define AUO_PIXCIR_REG_STRENGTH 0x0d
+#define AUO_PIXCIR_REG_STRENGTH_X1_LSB 0x0e
+#define AUO_PIXCIR_REG_STRENGTH_X1_MSB 0x0f
+
+#define AUO_PIXCIR_REG_RAW_DATA_X 0x2b
+#define AUO_PIXCIR_REG_RAW_DATA_Y 0x4f
+
+#define AUO_PIXCIR_REG_X_SENSITIVITY 0x6f
+#define AUO_PIXCIR_REG_Y_SENSITIVITY 0x70
+#define AUO_PIXCIR_REG_INT_SETTING 0x71
+#define AUO_PIXCIR_REG_INT_WIDTH 0x72
+#define AUO_PIXCIR_REG_POWER_MODE 0x73
+
+#define AUO_PIXCIR_REG_VERSION 0x77
+#define AUO_PIXCIR_REG_CALIBRATE 0x78
+
+#define AUO_PIXCIR_REG_TOUCHAREA_X1 0x1e
+#define AUO_PIXCIR_REG_TOUCHAREA_Y1 0x1f
+#define AUO_PIXCIR_REG_TOUCHAREA_X2 0x20
+#define AUO_PIXCIR_REG_TOUCHAREA_Y2 0x21
+
+#define AUO_PIXCIR_REG_EEPROM_CALIB_X 0x42
+#define AUO_PIXCIR_REG_EEPROM_CALIB_Y 0xad
+
+#define AUO_PIXCIR_INT_TPNUM_MASK 0xe0
+#define AUO_PIXCIR_INT_TPNUM_SHIFT 5
+#define AUO_PIXCIR_INT_RELEASE (1 << 4)
+#define AUO_PIXCIR_INT_ENABLE (1 << 3)
+#define AUO_PIXCIR_INT_POL_HIGH (1 << 2)
+#define AUO_PIXCIR_INT_MODE_MASK 0x03
+
+/*
+ * Power modes:
+ * active: scan speed 60Hz
+ * sleep: scan speed 10Hz can be auto-activated, wakeup on 1st touch
+ * deep sleep: scan speed 1Hz can only be entered or left manually.
+ */
+#define AUO_PIXCIR_POWER_ACTIVE 0x00
+#define AUO_PIXCIR_POWER_SLEEP 0x01
+#define AUO_PIXCIR_POWER_DEEP_SLEEP 0x02
+#define AUO_PIXCIR_POWER_MASK 0x03
+
+#define AUO_PIXCIR_POWER_ALLOW_SLEEP (1 << 2)
+#define AUO_PIXCIR_POWER_IDLE_TIME(ms) ((ms & 0xf) << 4)
+
+#define AUO_PIXCIR_CALIBRATE 0x03
+
+#define AUO_PIXCIR_EEPROM_CALIB_X_LEN 62
+#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN 36
+
+#define AUO_PIXCIR_RAW_DATA_X_LEN 18
+#define AUO_PIXCIR_RAW_DATA_Y_LEN 11
+
+#define AUO_PIXCIR_STRENGTH_ENABLE (1 << 0)
+
+/* Touchscreen absolute values */
+#define AUO_PIXCIR_REPORT_POINTS 2
+#define AUO_PIXCIR_MAX_AREA 0xff
+#define AUO_PIXCIR_PENUP_TIMEOUT_MS 10
+
+struct auo_pixcir_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ char phys[32];
+
+ /* special handling for touch_indicate interupt mode */
+ bool touch_ind_mode;
+
+ wait_queue_head_t wait;
+ bool stopped;
+};
+
+struct auo_point_t {
+ int coord_x;
+ int coord_y;
+ int area_major;
+ int area_minor;
+ int orientation;
+};
+
+static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
+ struct auo_point_t *point)
+{
+ struct i2c_client *client = ts->client;
+ const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ uint8_t raw_coord[8];
+ uint8_t raw_area[4];
+ int i, ret;
+
+ /* touch coordinates */
+ ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB,
+ 8, raw_coord);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read coordinate, %d\n", ret);
+ return ret;
+ }
+
+ /* touch area */
+ ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1,
+ 4, raw_area);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not read touch area, %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+ point[i].coord_x =
+ raw_coord[4 * i + 1] << 8 | raw_coord[4 * i];
+ point[i].coord_y =
+ raw_coord[4 * i + 3] << 8 | raw_coord[4 * i + 2];
+
+ if (point[i].coord_x > pdata->x_max ||
+ point[i].coord_y > pdata->y_max) {
+ dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+ point[i].coord_x, point[i].coord_y);
+ point[i].coord_x = point[i].coord_y = 0;
+ }
+
+ /* determine touch major, minor and orientation */
+ point[i].area_major = max(raw_area[2 * i], raw_area[2 * i + 1]);
+ point[i].area_minor = min(raw_area[2 * i], raw_area[2 * i + 1]);
+ point[i].orientation = raw_area[2 * i] > raw_area[2 * i + 1];
+ }
+
+ return 0;
+}
+
+static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
+{
+ struct auo_pixcir_ts *ts = dev_id;
+ struct i2c_client *client = ts->client;
+ const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
+ int i;
+ int ret;
+ int fingers = 0;
+ int abs = -1;
+
+ while (!ts->stopped) {
+
+ /* check for up event in touch touch_ind_mode */
+ if (ts->touch_ind_mode) {
+ if (gpio_get_value(pdata->gpio_int) == 0) {
+ input_mt_sync(ts->input);
+ input_report_key(ts->input, BTN_TOUCH, 0);
+ input_sync(ts->input);
+ break;
+ }
+ }
+
+ ret = auo_pixcir_collect_data(ts, point);
+ if (ret < 0) {
+ /* we want to loop only in touch_ind_mode */
+ if (!ts->touch_ind_mode)
+ break;
+
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+ continue;
+ }
+
+ for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+ if (point[i].coord_x > 0 || point[i].coord_y > 0) {
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ point[i].coord_x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ point[i].coord_y);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+ point[i].area_major);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+ point[i].area_minor);
+ input_report_abs(ts->input, ABS_MT_ORIENTATION,
+ point[i].orientation);
+ input_mt_sync(ts->input);
+
+ /* use first finger as source for singletouch */
+ if (fingers == 0)
+ abs = i;
+
+ /* number of touch points could also be queried
+ * via i2c but would require an additional call
+ */
+ fingers++;
+ }
+ }
+
+ input_report_key(ts->input, BTN_TOUCH, fingers > 0);
+
+ if (abs > -1) {
+ input_report_abs(ts->input, ABS_X, point[abs].coord_x);
+ input_report_abs(ts->input, ABS_Y, point[abs].coord_y);
+ }
+
+ input_sync(ts->input);
+
+ /* we want to loop only in touch_ind_mode */
+ if (!ts->touch_ind_mode)
+ break;
+
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Set the power mode of the device.
+ * Valid modes are
+ * - AUO_PIXCIR_POWER_ACTIVE
+ * - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch
+ * - AUO_PIXCIR_POWER_DEEP_SLEEP
+ */
+static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+ AUO_PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ ret &= ~AUO_PIXCIR_POWER_MASK;
+ ret |= mode;
+
+ ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret);
+ if (ret) {
+ dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+ AUO_PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static __devinit int auo_pixcir_int_config(struct auo_pixcir_ts *ts,
+ int int_setting)
+{
+ struct i2c_client *client = ts->client;
+ struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ ret &= ~AUO_PIXCIR_INT_MODE_MASK;
+ ret |= int_setting;
+ ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */
+
+ ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+ ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND;
+
+ return 0;
+}
+
+/* control the generation of interrupts on the device side */
+static int auo_pixcir_int_toggle(struct auo_pixcir_ts *ts, bool enable)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ if (enable)
+ ret |= AUO_PIXCIR_INT_ENABLE;
+ else
+ ret &= ~AUO_PIXCIR_INT_ENABLE;
+
+ ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+ ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+ AUO_PIXCIR_REG_INT_SETTING, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int auo_pixcir_start(struct auo_pixcir_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not set power mode, %d\n",
+ ret);
+ return ret;
+ }
+
+ ts->stopped = false;
+ mb();
+ enable_irq(client->irq);
+
+ ret = auo_pixcir_int_toggle(ts, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not enable interrupt, %d\n",
+ ret);
+ disable_irq(client->irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int auo_pixcir_stop(struct auo_pixcir_ts *ts)
+{
+ struct i2c_client *client = ts->client;
+ int ret;
+
+ ret = auo_pixcir_int_toggle(ts, 0);
+ if (ret < 0) {
+ dev_err(&client->dev, "could not disable interrupt, %d\n",
+ ret);
+ return ret;
+ }
+
+ /* disable receiving of interrupts */
+ disable_irq(client->irq);
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+
+ return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP);
+}
+
+static int auo_pixcir_input_open(struct input_dev *dev)
+{
+ struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+ int ret;
+
+ ret = auo_pixcir_start(ts);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void auo_pixcir_input_close(struct input_dev *dev)
+{
+ struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+
+ auo_pixcir_stop(ts);
+
+ return;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int auo_pixcir_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ /* when configured as wakeup source, device should always wake system
+ * therefore start device if necessary
+ */
+ if (device_may_wakeup(&client->dev)) {
+ /* need to start device if not open, to be wakeup source */
+ if (!input->users) {
+ ret = auo_pixcir_start(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ enable_irq_wake(client->irq);
+ ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP);
+ } else if (input->users) {
+ ret = auo_pixcir_stop(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static int auo_pixcir_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(&client->dev)) {
+ disable_irq_wake(client->irq);
+
+ /* need to stop device if it was not open on suspend */
+ if (!input->users) {
+ ret = auo_pixcir_stop(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ /* device wakes automatically from SLEEP */
+ } else if (input->users) {
+ ret = auo_pixcir_start(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend,
+ auo_pixcir_resume);
+
+static int __devinit auo_pixcir_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+ struct auo_pixcir_ts *ts;
+ struct input_dev *input_dev;
+ int ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ ts = kzalloc(sizeof(struct auo_pixcir_ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int");
+ if (ret) {
+ dev_err(&client->dev, "request of gpio %d failed, %d\n",
+ pdata->gpio_int, ret);
+ goto err_gpio_int;
+ }
+
+ if (pdata->init_hw)
+ pdata->init_hw(client);
+
+ ts->client = client;
+ ts->touch_ind_mode = 0;
+ init_waitqueue_head(&ts->wait);
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&client->dev, "could not allocate input device\n");
+ goto err_input_alloc;
+ }
+
+ ts->input = input_dev;
+
+ input_dev->name = "AUO-Pixcir touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ input_dev->open = auo_pixcir_input_open;
+ input_dev->close = auo_pixcir_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0);
+
+ /* For multi touch */
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ AUO_PIXCIR_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+ AUO_PIXCIR_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+ ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
+ if (ret < 0)
+ goto err_fw_vers;
+ dev_info(&client->dev, "firmware version 0x%X\n", ret);
+
+ ret = auo_pixcir_int_config(ts, pdata->int_setting);
+ if (ret)
+ goto err_fw_vers;
+
+ input_set_drvdata(ts->input, ts);
+ ts->stopped = true;
+
+ ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ input_dev->name, ts);
+ if (ret) {
+ dev_err(&client->dev, "irq %d requested failed\n", client->irq);
+ goto err_fw_vers;
+ }
+
+ /* stop device and put it into deep sleep until it is opened */
+ ret = auo_pixcir_stop(ts);
+ if (ret < 0)
+ goto err_input_register;
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ dev_err(&client->dev, "could not register input device\n");
+ goto err_input_register;
+ }
+
+ i2c_set_clientdata(client, ts);
+
+ return 0;
+
+err_input_register:
+ free_irq(client->irq, ts);
+err_fw_vers:
+ input_free_device(input_dev);
+err_input_alloc:
+ if (pdata->exit_hw)
+ pdata->exit_hw(client);
+ gpio_free(pdata->gpio_int);
+err_gpio_int:
+ kfree(ts);
+
+ return ret;
+}
+
+static int __devexit auo_pixcir_remove(struct i2c_client *client)
+{
+ struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+ const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+
+ free_irq(client->irq, ts);
+
+ input_unregister_device(ts->input);
+
+ if (pdata->exit_hw)
+ pdata->exit_hw(client);
+
+ gpio_free(pdata->gpio_int);
+
+ kfree(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id auo_pixcir_idtable[] = {
+ { "auo_pixcir_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable);
+
+static struct i2c_driver auo_pixcir_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "auo_pixcir_ts",
+ .pm = &auo_pixcir_pm_ops,
+ },
+ .probe = auo_pixcir_probe,
+ .remove = __devexit_p(auo_pixcir_remove),
+ .id_table = auo_pixcir_idtable,
+};
+
+module_i2c_driver(auo_pixcir_driver);
+
+MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_Base.b b/drivers/input/touchscreen/aw5306_ts/AW5306_Base.b
new file mode 100755
index 00000000..e3e6c22a
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_Base.b
Binary files differ
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_Clb.b b/drivers/input/touchscreen/aw5306_ts/AW5306_Clb.b
new file mode 100755
index 00000000..40e49326
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_Clb.b
Binary files differ
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_Drv.b b/drivers/input/touchscreen/aw5306_ts/AW5306_Drv.b
new file mode 100755
index 00000000..03f070a1
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_Drv.b
Binary files differ
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_Drv.h b/drivers/input/touchscreen/aw5306_ts/AW5306_Drv.h
new file mode 100755
index 00000000..47042361
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_Drv.h
@@ -0,0 +1,158 @@
+/**************************************************************************
+* AW5306_Drv.h
+*
+* AW5306 Driver code version 1.0
+*
+* Create Date : 2012/06/25
+*
+* Modify Date :
+*
+* Create by : wuhaijun
+*
+**************************************************************************/
+
+#ifndef AW5306_DRV_H
+
+#define AW5306_DRV_H
+
+#define Release_Ver 219
+
+
+#define MAX_POINT 5
+
+#define NUM_TX 21 // TX number of TOUCH IC
+#define NUM_RX 12 // RX number of TOUCH IC
+
+//#define NEWBASE_PROCESS //new base process need test!!!
+
+#define ABS(X) ((X > 0) ? (X) : (-X))
+
+
+typedef enum{
+ RawDataMode = 0,
+ DeltaMode,
+ MonitorMode
+}enumWorkMode;
+
+typedef enum{
+ BASE_INITIAL,
+ BASE_FAST_TRACE,
+ BASE_STABLE,
+ TEMP_DRIFT
+} CompensateMode;
+
+typedef struct {
+ unsigned short Base[NUM_TX][NUM_RX];
+ unsigned short ChipBase[NUM_TX][NUM_RX];
+ signed char Flag[NUM_TX][NUM_RX];
+ signed char BaseCnt[NUM_TX][NUM_RX];
+ unsigned char CompensateFlag;
+ unsigned char TraceTempIncCnt;
+ unsigned char TraceTempDecCnt;
+ unsigned char CompensateStateFrameCnt;
+ short LastMaxDiff;
+ CompensateMode CompensateState;
+ unsigned int InitialFrameCnt;
+ unsigned char PosBigAreaTouchFlag;
+ unsigned char NegBigAreaTouchFlag;
+ unsigned char BigAreaFirstFlag;
+ unsigned char BigAreaChangeFlag;
+ unsigned short BigTouchFrame;
+ unsigned short FrameCnt;
+ unsigned char LongStableCnt;
+ unsigned char PosPeakCnt;
+ unsigned char NegPeakCnt;
+ unsigned char PeakCheckFrameCnt;
+ unsigned char BaseFrozen;
+ unsigned char PosPeakCompensateCnt[MAX_POINT];
+ unsigned char NegPeakCompensateCnt[MAX_POINT];
+}STRUCTBASE;
+
+typedef struct {
+ unsigned char Peak[MAX_POINT][2];
+ unsigned char LastPeak[MAX_POINT][2];
+ unsigned char NegPeak[MAX_POINT][2];
+ unsigned char CurrentPointNum;
+ unsigned char CurrentNegPointNum;
+ unsigned char LastPointNum;
+}STRUCTPEAK;
+
+typedef struct {
+ unsigned short X,Y; // X,Y coordinate
+ unsigned char PointID; // Assigned point ID
+ unsigned char Event; // Event of current point
+}STRUCTPOINT;
+
+typedef struct {
+ STRUCTPOINT PointInfo[MAX_POINT];
+ STRUCTPOINT RptPoint[MAX_POINT];
+ unsigned char PointNum;
+ unsigned char LastPointNum;
+ unsigned char NegPointNum;
+ unsigned char FilterPointCnt;
+ unsigned char FirstLiftUpFlag;
+ unsigned char TouchStatus;
+ unsigned char PointHoldCnt[MAX_POINT];
+ unsigned char PointPressCnt[MAX_POINT];
+
+}STRUCTFRAME;
+
+typedef struct {
+ unsigned char fileflag[14];
+ unsigned char TXOFFSET[(NUM_TX+1)/2];
+ unsigned char RXOFFSET[(NUM_RX+1)/2];
+ unsigned char TXCAC[NUM_TX];
+ unsigned char RXCAC[NUM_RX];
+ unsigned char TXGAIN[NUM_TX];
+ short SOFTOFFSET[NUM_TX][NUM_RX];
+}STRUCTCALI;
+
+#define NOISE_LISTENING 0
+#define NOISE_SCAN 1
+#define NOISE_FREQ_JUMP 2
+#define NOISE_SEEK_FAIL 3
+
+#define NOISE_FRM_NORMAL 0
+#define NOISE_FRM_PRE_MEASURE 1
+#define NOISE_FRM_MEASURE 2
+
+typedef struct {
+ unsigned char AllFrmCnt; // Frame counter to generate noise meaure frame indicator
+ unsigned char NoiseFrmCnt; // Frame counter for noise level checking
+ unsigned char IdleFrmCnt; // No touch frame counter
+ unsigned char State; // Noise checking state: LISTENING, SCAN, JUMP
+ unsigned char FrmState; // Frame type indicator: PRE_MEAUSRE, MEAUSRE, NORMAL
+ short NoiseNormal; // Noise in working freq
+ short NoiseScan; // Noise in scan freq
+ short Better_NoiseScan; //pfx:smaller Noise in Scan freq
+ unsigned char Better_ScanFreqID; //pfx:the Scan Freq for the smaller Noise
+ unsigned char ScanFreqID; // Scan freq ID
+ unsigned char WorkFreqID; // Current freq ID
+ short NoiseTh1; // Diff threshold for noise too high judgement
+ char JumpTh; // frame number threshold for freq jumping
+ char FailedFreqList [32]; // Searched freq indicator for freq scanning
+}STRUCTNOISE;
+
+
+void AW5306_TP_Init(void);
+void AW5306_TP_Reinit(void);
+void AW5306_Sleep(void);
+char AW5306_TouchProcess(void);
+void AW5306_ChargeMode(char mode);
+unsigned char AW5306_GetPointNum(void);
+unsigned char AW5306_GetPeakNum(void);
+char AW5306_GetPoint(int *x,int *y, int *id, int *event,char Index);
+void AW5306_GetBase(unsigned short *data, char x,char y);
+void AW5306_GetDiff(short *data, char x,char y);
+char AW5306_GetPeak(unsigned char *x,unsigned char *y,unsigned char Index);
+char AW5306_GetNegPeak(unsigned char *x,unsigned char *y,unsigned char Index);
+char AW5306_GetCalcPoint(unsigned short *x,unsigned short *y,unsigned char Index);
+char AW5306_CLB(void);
+void AW5306_CLB_GetCfg(void);
+void AW5306_CLB_WriteCfg(void);
+void TP_Force_Calibration(void);
+void FreqScan(unsigned char BaseFreq);
+
+
+
+#endif
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_Reg.h b/drivers/input/touchscreen/aw5306_ts/AW5306_Reg.h
new file mode 100755
index 00000000..2bbbeb00
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_Reg.h
@@ -0,0 +1,187 @@
+/**************************************************************************
+* AW5306_Reg.h
+*
+* AW5306 Driver code version 1.0
+*
+* Create Date : 2012/06/25
+*
+* Modify Date :
+*
+* Create by : wuhaijun
+*
+**************************************************************************/
+
+#ifndef AW5306_REG_H
+
+#define AW5306_REG_H
+
+#define SA_PAGE 0x00
+#define SA_IDRST 0x01
+#define SA_CTRL 0x02
+#define SA_SCANMD 0x03
+#define SA_IER 0x04
+#define SA_RX_NUM 0x05
+#define SA_RX_START 0x06
+#define SA_TX_NUM 0x07
+#define SA_TX_INDEX0 0x08
+#define SA_TX_INDEX1 0x09
+#define SA_TX_INDEX2 0x0A
+#define SA_TX_INDEX3 0x0B
+#define SA_TX_INDEX4 0x0C
+#define SA_TX_INDEX5 0x0D
+#define SA_TX_INDEX6 0x0E
+#define SA_TX_INDEX7 0x0F
+#define SA_TX_INDEX8 0x10
+#define SA_TX_INDEX9 0x11
+#define SA_TX_INDEX10 0x12
+#define SA_TX_INDEX11 0x13
+#define SA_TX_INDEX12 0x14
+#define SA_TX_INDEX13 0x15
+#define SA_TX_INDEX14 0x16
+#define SA_TX_INDEX15 0x17
+#define SA_TX_INDEX16 0x18
+#define SA_TX_INDEX17 0x19
+#define SA_TX_INDEX18 0x1A
+#define SA_TX_INDEX19 0x1B
+#define SA_TX_INDEX20 0x1C
+#define SA_TXCAC0 0x1D
+#define SA_TXCAC1 0x1E
+#define SA_TXCAC2 0x1F
+#define SA_TXCAC3 0x20
+#define SA_TXCAC4 0x21
+#define SA_TXCAC5 0x22
+#define SA_TXCAC6 0x23
+#define SA_TXCAC7 0x24
+#define SA_TXCAC8 0x25
+#define SA_TXCAC9 0x26
+#define SA_TXCAC10 0x27
+#define SA_TXCAC11 0x28
+#define SA_TXCAC12 0x29
+#define SA_TXCAC13 0x2A
+#define SA_TXCAC14 0x2B
+#define SA_TXCAC15 0x2C
+#define SA_TXCAC16 0x2D
+#define SA_TXCAC17 0x2E
+#define SA_TXCAC18 0x2F
+#define SA_TXCAC19 0x30
+#define SA_TXCAC20 0x31
+#define SA_TXOFFSET0 0x32
+#define SA_TXOFFSET1 0x33
+#define SA_TXOFFSET2 0x34
+#define SA_TXOFFSET3 0x35
+#define SA_TXOFFSET4 0x36
+#define SA_TXOFFSET5 0x37
+#define SA_TXOFFSET6 0x38
+#define SA_TXOFFSET7 0x39
+#define SA_TXOFFSET8 0x3A
+#define SA_TXOFFSET9 0x3B
+#define SA_TXOFFSET10 0x3C
+#define SA_RXCAC0 0x3E
+#define SA_RXCAC1 0x3F
+#define SA_RXCAC2 0x40
+#define SA_RXCAC3 0x41
+#define SA_RXCAC4 0x42
+#define SA_RXCAC5 0x43
+#define SA_RXCAC6 0x44
+#define SA_RXCAC7 0x45
+#define SA_RXCAC8 0x46
+#define SA_RXCAC9 0x47
+#define SA_RXCAC10 0x48
+#define SA_RXCAC11 0x49
+#define SA_RXOFFSET0 0x4A
+#define SA_RXOFFSET1 0x4B
+#define SA_RXOFFSET2 0x4C
+#define SA_RXOFFSET3 0x4D
+#define SA_RXOFFSET4 0x4E
+#define SA_RXOFFSET5 0x4F
+#define SA_DRV_VLT 0x51
+#define SA_SCANFREQ1 0x52
+#define SA_SCANFREQ2 0x53
+#define SA_SCANFREQ3 0x54
+#define SA_TXADCGAIN0 0x55
+#define SA_TXADCGAIN1 0x56
+#define SA_TXADCGAIN2 0x57
+#define SA_TXADCGAIN3 0x58
+#define SA_TXADCGAIN4 0x59
+#define SA_TXADCGAIN5 0x5A
+#define SA_TXADCGAIN6 0x5B
+#define SA_TXADCGAIN7 0x5C
+#define SA_TXADCGAIN8 0x5D
+#define SA_TXADCGAIN9 0x5E
+#define SA_TXADCGAIN10 0x5F
+#define SA_TXADCGAIN11 0x60
+#define SA_TXADCGAIN12 0x61
+#define SA_TXADCGAIN13 0x62
+#define SA_TXADCGAIN14 0x63
+#define SA_TXADCGAIN15 0x64
+#define SA_TXADCGAIN16 0x65
+#define SA_TXADCGAIN17 0x66
+#define SA_TXADCGAIN18 0x67
+#define SA_TXADCGAIN19 0x68
+#define SA_TXADCGAIN20 0x69
+#define SA_WAITTIME 0x6A
+#define SA_TCLKDLY 0x6B
+#define SA_FINEADJ 0x6C
+#define SA_TXCLKFREQ 0x6D
+#define SA_SCANTIM 0x6E
+#define SA_READSEL 0x70
+#define SA_ISR 0x71
+#define SA_STATE1 0x72
+#define SA_POSCNT 0x73
+#define SA_NEGCNT 0x74
+#define SA_VLDNUM 0x75
+#define SA_ADDRH 0x7D
+#define SA_ADDRL 0x7E
+#define SA_RAWDATA 0x7F
+////////////////////////
+// Page 2
+////////////////////////
+#define SA_SINETABE1 0x03
+#define SA_SINETABE2 0x04
+#define SA_DATAOFFSET 0x05
+#define SA_TRACECTRL1 0x10
+#define SA_TRACECTRL2 0x11
+#define SA_TRACECTRL3 0x12
+#define SA_TRACEST 0x13
+#define SA_RPTNEGTH 0x14
+#define SA_RPTPOSTH 0x15
+#define SA_TRACESTEP 0x16
+#define SA_TRCLVLLO 0x17
+#define SA_TRCLVLPOSHI 0x18
+#define SA_TRCLVLNEGHI 0x19
+#define SA_TRACEINTERVAL 0x1A
+#define SA_RXSTABLETH 0x1B
+#define SA_POSLEVELTH 0x1C
+#define SA_POSNUMTH 0x1D
+#define SA_NEGLEVELTH 0x1E
+#define SA_NEGNUMTH 0x1F
+#define SA_BIGPOINTTH 0x20
+#define SA_BIGPOSTIMTH 0x21
+#define SA_BIGNEGTIMTH 0x22
+#define SA_NEGTIMTH 0x23
+#define SA_TRACEHIGHTIM 0x24
+#define SA_INITPNTTH 0x25
+#define SA_TCHCLRTIMSET 0x26
+#define SA_INITLVTH 0x27
+#define SA_MAXCHKTH 0x28
+#define SA_MINCHKTH 0x29
+#define SA_INITFORCEQUIT 0x2A
+#define SA_CHAMPCFG 0x30
+#define SA_ADCCFG 0x31
+#define SA_IBCFG1 0x32
+#define SA_IBCFG2 0x33
+#define SA_LDOCFG 0x34
+#define SA_OSCCFG1 0x35
+#define SA_OSCCFG2 0x36
+#define SA_OSCCFG3 0x37
+#define SA_EN_CLK_QNTZ1 0x38
+#define SA_EN_CLK_QNTZ2 0x39
+#define SA_CPFREQ 0x3A
+#define SA_ATEST1 0x3B
+#define SA_ATEST2 0x3C
+#define SA_RAMTST 0x60
+#define SA_TESTCFG 0x61
+#define SA_TSTDATAH 0x62
+#define SA_TSTDATAL 0x63
+#endif
+
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_ts.c b/drivers/input/touchscreen/aw5306_ts/AW5306_ts.c
new file mode 100755
index 00000000..f623c646
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_ts.c
@@ -0,0 +1,1614 @@
+/*
+ * drivers/input/touchscreen/aw5306/aw5306.c
+ *
+ * FocalTech aw5306 TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * note: only support mulititouch Wenfs 2010-10-01
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/slab.h>
+#include "AW5306_Drv.h"
+#include "AW5306_userpara.h"
+#include "irq_gpio.h"
+
+#define CONFIG_AW5306_MULTITOUCH (1)
+#define DEV_AW5306 "touch_aw5306"
+#define TS_I2C_NAME "aw5306-ts"
+#define AW5306_I2C_ADDR 0x38
+#define AW5306_I2C_BUS 0x01
+
+//#define DEBUG_EN
+
+#undef dbg
+#ifdef DEBUG_EN
+ #define dbg(fmt,args...) printk("DBG:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+#else
+ #define dbg(fmt,args...)
+#endif
+
+#undef dbg_err
+#define dbg_err(fmt,args...) printk("ERR:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+
+
+
+struct ts_event {
+ int x[5];
+ int y[5];
+ int pressure;
+ int touch_ID[5];
+ int touch_point;
+ int pre_point;
+};
+
+struct AW5306_ts_data {
+ const char *name;
+ struct input_dev *input_dev;
+ struct ts_event event;
+ struct work_struct pen_event_work;
+ struct workqueue_struct *ts_workqueue;
+ struct kobject *kobj;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ struct timer_list touch_timer;
+
+ int irq;
+ int irqgpio;
+ int rstgpio;
+
+ int reslx;
+ int resly;
+ int nt;
+ int nb;
+ int xch;
+ int ych;
+ int swap;
+ int dbg;
+ int lcd_exchg;
+};
+
+
+struct AW5306_ts_data *pContext=NULL;
+static struct i2c_client *l_client=NULL;
+static unsigned char suspend_flag=0; //0: sleep out; 1: sleep in
+static short tp_idlecnt = 0;
+static char tp_SlowMode = 0;
+//static struct class *i2c_dev_class;
+
+extern char AW5306_CLB(void);
+extern void AW5306_CLB_GetCfg(void);
+extern STRUCTCALI AW_Cali;
+extern AW5306_UCF AWTPCfg;
+extern STRUCTBASE AW_Base;
+extern short Diff[NUM_TX][NUM_RX];
+extern short adbDiff[NUM_TX][NUM_RX];
+extern short AWDeltaData[32];
+
+char AW_CALI_FILENAME[50] = {0,};
+char AW_UCF_FILENAME[50] = {0,};
+
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+
+void __aeabi_unwind_cpp_pr0(void)
+{
+}
+
+void __aeabi_unwind_cpp_pr1(void)
+{
+}
+
+
+int AW_nvram_read(char *filename, char *buf, ssize_t len, int offset)
+{
+ struct file *fd;
+ //ssize_t ret;
+ int retLen = -1;
+
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ fd = filp_open(filename, O_RDONLY, 0);
+
+ if(IS_ERR(fd)) {
+ printk("[AW5306][nvram_read] : failed to open!!\n");
+ return -1;
+ }
+
+ do{
+ if ((fd->f_op == NULL) || (fd->f_op->read == NULL))
+ {
+ printk("[AW5306][nvram_read] : file can not be read!!\n");
+ break;
+ }
+
+ if (fd->f_pos != offset) {
+ if (fd->f_op->llseek) {
+ if(fd->f_op->llseek(fd, offset, 0) != offset) {
+ printk("[AW5306][nvram_read] : failed to seek!!\n");
+ break;
+ }
+ } else {
+ fd->f_pos = offset;
+ }
+ }
+
+ retLen = fd->f_op->read(fd,
+ buf,
+ len,
+ &fd->f_pos);
+
+ }while(false);
+
+ filp_close(fd, NULL);
+
+ set_fs(old_fs);
+
+
+ return retLen;
+}
+
+int AW_nvram_write(char *filename, char *buf, ssize_t len, int offset)
+{
+ struct file *fd;
+ //ssize_t ret;
+ int retLen = -1;
+
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ fd = filp_open(filename, O_WRONLY|O_CREAT, 0666);
+
+ if(IS_ERR(fd)) {
+ printk("[AW5306][nvram_write] : failed to open!!\n");
+ return -1;
+ }
+
+ do{
+ if ((fd->f_op == NULL) || (fd->f_op->write == NULL))
+ {
+ printk("[AW5306][nvram_write] : file can not be write!!\n");
+ break;
+ } /* End of if */
+
+ if (fd->f_pos != offset) {
+ if (fd->f_op->llseek) {
+ if(fd->f_op->llseek(fd, offset, 0) != offset) {
+ printk("[AW5306][nvram_write] : failed to seek!!\n");
+ break;
+ }
+ } else {
+ fd->f_pos = offset;
+ }
+ }
+
+ retLen = fd->f_op->write(fd,
+ buf,
+ len,
+ &fd->f_pos);
+
+ }while(false);
+
+ filp_close(fd, NULL);
+
+ set_fs(old_fs);
+
+ return retLen;
+}
+
+
+int AW_I2C_WriteByte(u8 addr, u8 para)
+{
+ int ret;
+ u8 buf[3];
+ struct i2c_msg msg[] = {
+ {
+ .addr = l_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf,
+ },
+ };
+ buf[0] = addr;
+ buf[1] = para;
+ ret = i2c_transfer(l_client->adapter, msg, 1);
+ return ret;
+}
+
+
+unsigned char AW_I2C_ReadByte(u8 addr)
+{
+ int ret;
+ u8 buf[2] = {0};
+ struct i2c_msg msgs[] = {
+ {
+ .addr = l_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buf,
+ },
+ {
+ .addr = l_client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ },
+ };
+ buf[0] = addr;
+ //msleep(1);
+ ret = i2c_transfer(l_client->adapter, msgs, 2);
+ return buf[0];
+}
+
+unsigned char AW_I2C_ReadXByte( unsigned char *buf, unsigned char addr, unsigned short len)
+{
+ int ret,i;
+ u8 rdbuf[512] = {0};
+ struct i2c_msg msgs[] = {
+ {
+ .addr = l_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = rdbuf,
+ },
+ {
+ .addr = l_client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = rdbuf,
+ },
+ };
+ rdbuf[0] = addr;
+ //msleep(1);
+ ret = i2c_transfer(l_client->adapter, msgs, 2);
+ if (ret < 0)
+ pr_err("msg %s i2c read error: %d\n", __func__, ret);
+ for(i = 0; i < len; i++)
+ {
+ buf[i] = rdbuf[i];
+ }
+ return ret;
+}
+
+unsigned char AW_I2C_WriteXByte( unsigned char *buf, unsigned char addr, unsigned short len)
+{
+ int ret,i;
+ u8 wdbuf[512] = {0};
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = l_client->addr,
+ .flags = 0,
+ .len = len+1,
+ .buf = wdbuf,
+ }
+ };
+
+ wdbuf[0] = addr;
+ for(i = 0; i < len; i++)
+ {
+ wdbuf[i+1] = buf[i];
+ }
+ //msleep(1);
+ ret = i2c_transfer(l_client->adapter, msgs, 1);
+ if (ret < 0)
+ pr_err("msg %s i2c read error: %d\n", __func__, ret);
+ return ret;
+}
+
+
+void AW_Sleep(unsigned int msec)
+{
+ msleep(msec);
+}
+
+static ssize_t AW5306_get_Cali(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t AW5306_set_Cali(struct device* cd,struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t AW5306_get_reg(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t AW5306_write_reg(struct device* cd,struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t AW5306_get_Base(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t AW5306_get_Diff(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t AW5306_get_adbBase(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t AW5306_get_adbDiff(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t AW5306_get_FreqScan(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t AW5306_Set_FreqScan(struct device* cd, struct device_attribute *attr,const char* buf, size_t len);
+static ssize_t AW5306_GetUcf(struct device* cd,struct device_attribute *attr, char* buf);
+
+
+
+static DEVICE_ATTR(cali, S_IRUGO | S_IWUGO, AW5306_get_Cali, AW5306_set_Cali);
+static DEVICE_ATTR(readreg, S_IRUGO | S_IWUGO, AW5306_get_reg, AW5306_write_reg);
+static DEVICE_ATTR(base, S_IRUGO | S_IWUSR, AW5306_get_Base, NULL);
+static DEVICE_ATTR(diff, S_IRUGO | S_IWUSR, AW5306_get_Diff, NULL);
+static DEVICE_ATTR(adbbase, S_IRUGO | S_IWUSR, AW5306_get_adbBase, NULL);
+static DEVICE_ATTR(adbdiff, S_IRUGO | S_IWUSR, AW5306_get_adbDiff, NULL);
+static DEVICE_ATTR(freqscan, S_IRUGO | S_IWUGO, AW5306_get_FreqScan, AW5306_Set_FreqScan);
+static DEVICE_ATTR(getucf, S_IRUGO | S_IWUSR, AW5306_GetUcf, NULL);
+
+
+static ssize_t AW5306_get_Cali(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ unsigned char i,j;
+ ssize_t len = 0;
+
+ len += snprintf(buf+len, PAGE_SIZE-len,"AWINIC RELEASE CODE VER = %d\n", Release_Ver);
+
+ len += snprintf(buf+len, PAGE_SIZE-len,"*****AW5306 Calibrate data*****\n");
+ len += snprintf(buf+len, PAGE_SIZE-len,"TXOFFSET:");
+
+ for(i=0;i<11;i++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "0x%02X ", AW_Cali.TXOFFSET[i]);
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ len += snprintf(buf+len, PAGE_SIZE-len, "RXOFFSET:");
+
+ for(i=0;i<6;i++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "0x%02X ", AW_Cali.RXOFFSET[i]);
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ len += snprintf(buf+len, PAGE_SIZE-len, "TXCAC:");
+
+ for(i=0;i<21;i++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "0x%02X ", AW_Cali.TXCAC[i]);
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ len += snprintf(buf+len, PAGE_SIZE-len, "RXCAC:");
+
+ for(i=0;i<12;i++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "0x%02X ", AW_Cali.RXCAC[i]);
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ len += snprintf(buf+len, PAGE_SIZE-len, "TXGAIN:");
+
+ for(i=0;i<21;i++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "0x%02X ", AW_Cali.TXGAIN[i]);
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+
+ for(i=0;i<AWTPCfg.TX_LOCAL;i++)
+ {
+ for(j=0;j<AWTPCfg.RX_LOCAL;j++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "%4d ", AW_Cali.SOFTOFFSET[i][j]);
+ }
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ }
+ return len;
+
+}
+
+static ssize_t AW5306_set_Cali(struct device* cd,struct device_attribute *attr, const char* buf, size_t count)
+{
+ struct AW5306_ts_data *data = i2c_get_clientdata(l_client);
+
+ unsigned long on_off = simple_strtoul(buf, NULL, 10);
+
+ if(on_off == 1)
+ {
+ #ifdef INTMODE
+ wmt_disable_gpirq(data ->irqgpio);
+ AW5306_Sleep();
+ suspend_flag = 1;
+ AW_Sleep(50);
+
+ TP_Force_Calibration();
+
+ AW5306_TP_Reinit();
+ wmt_enable_gpirq(data->irqgpio);
+ suspend_flag = 0;
+
+ #else
+ suspend_flag = 1;
+ AW_Sleep(50);
+
+ TP_Force_Calibration();
+
+ AW5306_TP_Reinit();
+ tp_idlecnt = 0;
+ tp_SlowMode = 0;
+ suspend_flag = 0;
+ data->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ add_timer(&data->touch_timer);
+ #endif
+ }
+
+ return count;
+}
+
+
+static ssize_t AW5306_get_adbBase(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ unsigned char i,j;
+ ssize_t len = 0;
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "base: \n");
+ for(i=0;i< AWTPCfg.TX_LOCAL;i++)
+ {
+ for(j=0;j<AWTPCfg.RX_LOCAL;j++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "%4d, ",AW_Base.Base[i][j]+AW_Cali.SOFTOFFSET[i][j]);
+ }
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ }
+
+ return len;
+}
+
+static ssize_t AW5306_get_Base(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ unsigned char i,j;
+ ssize_t len = 0;
+
+ *(buf+len) = AWTPCfg.TX_LOCAL;
+ len++;
+ *(buf+len) = AWTPCfg.RX_LOCAL;
+ len++;
+
+ for(i=0;i< AWTPCfg.TX_LOCAL;i++)
+ {
+ for(j=0;j<AWTPCfg.RX_LOCAL;j++)
+ {
+ *(buf+len) = (char)(((AW_Base.Base[i][j]+AW_Cali.SOFTOFFSET[i][j]) & 0xFF00)>>8);
+ len++;
+ *(buf+len) = (char)((AW_Base.Base[i][j]+AW_Cali.SOFTOFFSET[i][j]) & 0x00FF);
+ len++;
+ }
+ }
+ return len;
+
+}
+
+static ssize_t AW5306_get_adbDiff(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ unsigned char i,j;
+ ssize_t len = 0;
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "Diff: \n");
+ for(i=0;i< AWTPCfg.TX_LOCAL;i++)
+ {
+ for(j=0;j<AWTPCfg.RX_LOCAL;j++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "%4d, ",adbDiff[i][j]);
+ }
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ }
+
+ return len;
+}
+
+static ssize_t AW5306_get_Diff(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ unsigned char i,j;
+ ssize_t len = 0;
+
+ *(buf+len) = AWTPCfg.TX_LOCAL;
+ len++;
+ *(buf+len) = AWTPCfg.RX_LOCAL;
+ len++;
+
+ for(i=0;i< AWTPCfg.TX_LOCAL;i++)
+ {
+ for(j=0;j<AWTPCfg.RX_LOCAL;j++)
+ {
+ *(buf+len) = (char)((adbDiff[i][j] & 0xFF00)>>8);
+ len++;
+ *(buf+len) = (char)(adbDiff[i][j] & 0x00FF);
+ len++;
+ }
+ }
+ return len;
+}
+
+static ssize_t AW5306_get_FreqScan(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ unsigned char i;
+ ssize_t len = 0;
+
+ for(i=0;i< 32;i++)
+ {
+ //*(buf+len) = (char)((AWDeltaData[i] & 0xFF00)>>8);
+ //len++;
+ //*(buf+len) = (char)(AWDeltaData[i] & 0x00FF);
+ //len++;
+ len += snprintf(buf+len, PAGE_SIZE-len, "%4d, ",AWDeltaData[i]);
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "\n");
+ return len;
+}
+
+static ssize_t AW5306_Set_FreqScan(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct AW5306_ts_data *data = i2c_get_clientdata(l_client);
+ unsigned long Basefreq = simple_strtoul(buf, NULL, 10);
+
+ if(Basefreq < 16)
+ {
+ #ifdef INTMODE
+ wmt_disable_gpirq(data ->irqgpio);
+ AW5306_Sleep();
+ suspend_flag = 1;
+ AW_Sleep(50);
+
+ FreqScan(Basefreq);
+
+ AW5306_TP_Reinit();
+ wmt_enable_gpirq(data ->irqgpio);
+ suspend_flag = 0;
+ #else
+ suspend_flag = 1;
+ AW_Sleep(50);
+
+ FreqScan(Basefreq);
+
+ AW5306_TP_Reinit();
+ tp_idlecnt = 0;
+ tp_SlowMode = 0;
+ suspend_flag = 0;
+ data->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ add_timer(&data->touch_timer);
+ #endif
+ }
+
+ return len;
+}
+
+static ssize_t AW5306_get_reg(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ struct AW5306_ts_data *data = i2c_get_clientdata(l_client);
+ u8 reg_val[128];
+ ssize_t len = 0;
+ u8 i;
+
+ if(suspend_flag != 1)
+ {
+#ifdef INTMODE
+ wmt_disable_gpirq(data ->irqgpio);
+ AW5306_Sleep();
+ suspend_flag = 1;
+ AW_Sleep(50);
+
+ AW_I2C_ReadXByte(reg_val,0,127);
+
+ AW5306_TP_Reinit();
+ wmt_enable_gpirq(data->irqgpio);
+ suspend_flag = 0;
+#else
+ suspend_flag = 1;
+
+ AW_Sleep(50);
+
+ AW_I2C_ReadXByte(reg_val,0,127);
+
+ AW5306_TP_Reinit();
+ tp_idlecnt = 0;
+ tp_SlowMode = 0;
+ suspend_flag = 0;
+ data->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ add_timer(&data->touch_timer);
+#endif
+ }
+ else
+ {
+ AW_I2C_ReadXByte(reg_val,0,127);
+ }
+ for(i=0;i<0x7F;i++)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "reg%02X = 0x%02X, ", i,reg_val[i]);
+ }
+
+ return len;
+
+}
+
+static ssize_t AW5306_write_reg(struct device* cd,struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct AW5306_ts_data *data = i2c_get_clientdata(l_client);
+ int databuf[2];
+
+ if(2 == sscanf(buf, "%d %d", &databuf[0], &databuf[1]))
+ {
+ if(suspend_flag != 1)
+ {
+ #ifdef INTMODE
+ wmt_disable_gpirq(data ->irqgpio);
+ AW5306_Sleep();
+ suspend_flag = 1;
+ AW_Sleep(50);
+
+ AW_I2C_WriteByte((u8)databuf[0],(u8)databuf[1]);
+
+ AW5306_TP_Reinit();
+ //ctp_enable_irq();
+ wmt_enable_gpirq(data->irqgpio);
+ suspend_flag = 0;
+ #else
+ suspend_flag = 1;
+ AW_Sleep(50);
+
+ AW_I2C_WriteByte((u8)databuf[0],(u8)databuf[1]);
+
+ AW5306_TP_Reinit();
+ tp_idlecnt = 0;
+ tp_SlowMode = 0;
+ suspend_flag = 0;
+ data->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ add_timer(&data->touch_timer);
+ #endif
+ }
+ else
+ {
+ AW_I2C_WriteByte((u8)databuf[0],(u8)databuf[1]);
+ }
+ }
+ else
+ {
+ printk("invalid content: '%s', length = %d\n", buf, count);
+ }
+ return count;
+}
+
+static ssize_t AW5306_GetUcf(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ ssize_t len = 0;
+
+ len += snprintf(buf+len, PAGE_SIZE-len,"*****AW5306 UCF DATA*****\n");
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.TX_LOCAL,AWTPCfg.RX_LOCAL);
+ len += snprintf(buf+len, PAGE_SIZE-len,"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d}\n",
+ AWTPCfg.TX_ORDER[0],AWTPCfg.TX_ORDER[1],AWTPCfg.TX_ORDER[2],AWTPCfg.TX_ORDER[3],AWTPCfg.TX_ORDER[4],
+ AWTPCfg.TX_ORDER[5],AWTPCfg.TX_ORDER[6],AWTPCfg.TX_ORDER[7],AWTPCfg.TX_ORDER[8],AWTPCfg.TX_ORDER[9],
+ AWTPCfg.TX_ORDER[10],AWTPCfg.TX_ORDER[11],AWTPCfg.TX_ORDER[12],AWTPCfg.TX_ORDER[13],AWTPCfg.TX_ORDER[14],
+ AWTPCfg.TX_ORDER[15],AWTPCfg.TX_ORDER[16],AWTPCfg.TX_ORDER[17],AWTPCfg.TX_ORDER[19],AWTPCfg.TX_ORDER[19],
+ AWTPCfg.TX_ORDER[20]);
+ len += snprintf(buf+len, PAGE_SIZE-len,"{%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d},\n",
+ AWTPCfg.RX_ORDER[0],AWTPCfg.RX_ORDER[1],AWTPCfg.RX_ORDER[2],AWTPCfg.RX_ORDER[3],
+ AWTPCfg.RX_ORDER[4],AWTPCfg.RX_ORDER[5],AWTPCfg.RX_ORDER[6],AWTPCfg.RX_ORDER[7],
+ AWTPCfg.RX_ORDER[8],AWTPCfg.RX_ORDER[9],AWTPCfg.RX_ORDER[10],AWTPCfg.RX_ORDER[11]);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.RX_START,AWTPCfg.HAVE_KEY_LINE);
+ len += snprintf(buf+len, PAGE_SIZE-len,"{%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d},\n",
+ AWTPCfg.KeyLineValid[0],AWTPCfg.KeyLineValid[1],AWTPCfg.KeyLineValid[2],AWTPCfg.KeyLineValid[3],
+ AWTPCfg.KeyLineValid[4],AWTPCfg.KeyLineValid[5],AWTPCfg.KeyLineValid[6],AWTPCfg.KeyLineValid[7],
+ AWTPCfg.KeyLineValid[8],AWTPCfg.KeyLineValid[9],AWTPCfg.KeyLineValid[10],AWTPCfg.KeyLineValid[11]);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.MAPPING_MAX_X,AWTPCfg.MAPPING_MAX_Y);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.GainClbDeltaMax,AWTPCfg.GainClbDeltaMin);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.KeyLineDeltaMax,AWTPCfg.KeyLineDeltaMin);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.OffsetClbExpectedMax,AWTPCfg.OffsetClbExpectedMin);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.RawDataDeviation,AWTPCfg.CacMultiCoef);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.RawDataCheckMin,AWTPCfg.RawDataCheckMax);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,%d,\n",AWTPCfg.FLYING_TH,AWTPCfg.MOVING_TH,AWTPCfg.MOVING_ACCELER);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.PEAK_TH,AWTPCfg.GROUP_TH);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,%d,\n",AWTPCfg.BIGAREA_TH,AWTPCfg.BIGAREA_CNT,AWTPCfg.BIGAREA_FRESHCNT);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,%d,\n",AWTPCfg.CACULATE_COEF,AWTPCfg.FIRST_CALI,AWTPCfg.RAWDATA_DUMP_SWITCH);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,0x%x,\n",AWTPCfg.MULTI_SCANFREQ,AWTPCfg.BASE_FREQ,AWTPCfg.FREQ_OFFSET);
+ len += snprintf(buf+len, PAGE_SIZE-len,"0x%x,0x%x,0x%x,\n",AWTPCfg.WAIT_TIME,AWTPCfg.CHAMP_CFG,AWTPCfg.POSLEVEL_TH);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,\n",AWTPCfg.ESD_PROTECT);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,%d,%d,%d,\n",AWTPCfg.MARGIN_COMPENSATE,AWTPCfg.MARGIN_COMP_DATA_UP,
+ AWTPCfg.MARGIN_COMP_DATA_DOWN,AWTPCfg.MARGIN_COMP_DATA_LEFT,AWTPCfg.MARGIN_COMP_DATA_RIGHT);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,%d,%d,\n",AWTPCfg.POINT_RELEASEHOLD,AWTPCfg.MARGIN_RELEASEHOLD,
+ AWTPCfg.POINT_PRESSHOLD,AWTPCfg.KEY_PRESSHOLD);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,%d,\n",AWTPCfg.PEAK_ROW_COMPENSATE,AWTPCfg.PEAK_COL_COMPENSATE,
+ AWTPCfg.PEAK_COMPENSATE_COEF);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.LCD_NOISE_PROCESS,AWTPCfg.LCD_NOISETH);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.FALSE_PEAK_PROCESS,AWTPCfg.FALSE_PEAK_TH);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.STABLE_DELTA_X,AWTPCfg.STABLE_DELTA_Y);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,%d,\n",AWTPCfg.DEBUG_LEVEL,AWTPCfg.FAST_FRAME,AWTPCfg.SLOW_FRAME);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.GAIN_CLB_SEPERATE,AWTPCfg.MARGIN_PREFILTER);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.BIGAREA_HOLDPOINT,AWTPCfg.CHARGE_NOISE);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.FREQ_JUMP,AWTPCfg.PEAK_VALID_CHECK);
+ len += snprintf(buf+len, PAGE_SIZE-len,"%d,%d,\n",AWTPCfg.WATER_REMOVE,AWTPCfg.INT_MODE);
+
+ return len;
+
+}
+
+
+static int AW5306_create_sysfs(struct i2c_client *client)
+{
+ int err;
+ struct device *dev = &(client->dev);
+
+ //TS_DBG("%s", __func__);
+
+ err = device_create_file(dev, &dev_attr_cali);
+ err = device_create_file(dev, &dev_attr_readreg);
+ err = device_create_file(dev, &dev_attr_base);
+ err = device_create_file(dev, &dev_attr_diff);
+ err = device_create_file(dev, &dev_attr_adbbase);
+ err = device_create_file(dev, &dev_attr_adbdiff);
+ err = device_create_file(dev, &dev_attr_freqscan);
+ err = device_create_file(dev, &dev_attr_getucf);
+ return err;
+}
+
+static void AW5306_ts_release(void)
+{
+ struct AW5306_ts_data *data = pContext;
+#ifdef CONFIG_AW5306_MULTITOUCH
+ #ifdef TOUCH_KEY_SUPPORT
+ if(1 == key_tp){
+ if(key_val == 1){
+ input_report_key(data->input_dev, KEY_MENU, 0);
+ input_sync(data->input_dev);
+ }
+ else if(key_val == 2){
+ input_report_key(data->input_dev, KEY_BACK, 0);
+ input_sync(data->input_dev);
+ // printk("===KEY 2 upupupupupu===++=\n");
+ }
+ else if(key_val == 3){
+ input_report_key(data->input_dev, KEY_SEARCH, 0);
+ input_sync(data->input_dev);
+ // printk("===KEY 3 upupupupupu===++=\n");
+ }
+ else if(key_val == 4){
+ input_report_key(data->input_dev, KEY_HOMEPAGE, 0);
+ input_sync(data->input_dev);
+ // printk("===KEY 4 upupupupupu===++=\n");
+ }
+ else if(key_val == 5){
+ input_report_key(data->input_dev, KEY_VOLUMEDOWN, 0);
+ input_sync(data->input_dev);
+ // printk("===KEY 5 upupupupupu===++=\n");
+ }
+ else if(key_val == 6){
+ input_report_key(data->input_dev, KEY_VOLUMEUP, 0);
+ input_sync(data->input_dev);
+ // printk("===KEY 6 upupupupupu===++=\n");
+ }
+// input_report_key(data->input_dev, key_val, 0);
+ //printk("Release Key = %d\n",key_val);
+ //printk("Release Keyi+++++++++++++++++++++++++++++\n");
+ } else{
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ }
+ #else
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ #endif
+
+#else
+ input_report_abs(data->input_dev, ABS_PRESSURE, 0);
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+#endif
+
+ input_mt_sync(data->input_dev);
+ input_sync(data->input_dev);
+ return;
+
+}
+
+
+static void Point_adjust(int *x, int *y)
+{
+ struct AW5306_ts_data *AW5306_ts = pContext;
+ int temp;
+
+ if (AW5306_ts->swap) {
+ temp = *x;
+ *x = *y;
+ *y = temp;
+ }
+ if (AW5306_ts->xch)
+ *x = AW5306_ts->reslx - *x;
+ if (AW5306_ts->ych)
+ *y = AW5306_ts->resly - *y;
+
+ if (AW5306_ts->lcd_exchg) {
+ int tmp;
+ tmp = *x;
+ *x = *y;
+ *y = AW5306_ts->reslx - tmp;
+ }
+}
+
+
+static int AW5306_read_data(void)
+{
+ struct AW5306_ts_data *data = pContext;
+ struct ts_event *event = &data->event;
+ int Pevent;
+ int i = 0;
+
+ AW5306_TouchProcess();
+
+ //memset(event, 0, sizeof(struct ts_event));
+ event->touch_point = AW5306_GetPointNum();
+
+ for(i=0;i<event->touch_point;i++)
+ {
+ AW5306_GetPoint(&event->x[i],&event->y[i],&event->touch_ID[i],&Pevent,i);
+ //swap(event->x[i], event->y[i]);
+ Point_adjust(&event->x[i], &event->y[i]);
+// printk("key%d = %d,%d,%d \n",i,event->x[i],event->y[i],event->touch_ID[i] );
+ }
+
+ if (event->touch_point == 0)
+ {
+ if(tp_idlecnt <= AWTPCfg.FAST_FRAME*5)
+ {
+ tp_idlecnt++;
+ }
+ if(tp_idlecnt > AWTPCfg.FAST_FRAME*5)
+ {
+ tp_SlowMode = 1;
+ }
+
+ if (event->pre_point != 0)
+ {
+ AW5306_ts_release();
+ event->pre_point = 0;
+ }
+ return 1;
+ }
+ else
+ {
+ tp_SlowMode = 0;
+ tp_idlecnt = 0;
+ event->pre_point = event->touch_point;
+ event->pressure = 200;
+ dbg("%s: 1:%d %d 2:%d %d \n", __func__,
+ event->x[0], event->y[0], event->x[1], event->y[1]);
+
+ return 0;
+ }
+}
+
+static void AW5306_report_multitouch(void)
+{
+ struct AW5306_ts_data *data = pContext;
+ struct ts_event *event = &data->event;
+
+#ifdef TOUCH_KEY_SUPPORT
+ if(1 == key_tp){
+ return;
+ }
+#endif
+
+ switch(event->touch_point) {
+ case 5:
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID[4]);
+ //input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x[4]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y[4]);
+ //input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(data->input_dev);
+ // printk("=++==x5 = %d,y5 = %d ====\n",event->x[4],event->y[4]);
+ case 4:
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID[3]);
+ //input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x[3]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y[3]);
+ //input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(data->input_dev);
+ // printk("===x4 = %d,y4 = %d ====\n",event->x[3],event->y[3]);
+ case 3:
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID[2]);
+ //input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x[2]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y[2]);
+ //input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(data->input_dev);
+ // printk("===x3 = %d,y3 = %d ====\n",event->x[2],event->y[2]);
+ case 2:
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID[1]);
+ //input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x[1]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y[1]);
+ //input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(data->input_dev);
+ // printk("===x2 = %d,y2 = %d ====\n",event->x[1],event->y[1]);
+ case 1:
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID[0]);
+ //input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->x[0]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y[0]);
+ //input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(data->input_dev);
+ // printk("===x1 = %d,y1 = %d ====\n",event->x[0],event->y[0]);
+ break;
+ default:
+// print_point_info("==touch_point default =\n");
+ break;
+ }
+
+ input_sync(data->input_dev);
+ dbg("%s: 1:%d %d 2:%d %d \n", __func__,
+ event->x[0], event->y[0], event->x[1], event->y[1]);
+ return;
+}
+
+#ifdef TOUCH_KEY_SUPPORT
+static void AW5306_report_touchkey(void)
+{
+ struct AW5306_ts_data *data = pContext;
+ struct ts_event *event = &data->event;
+ //printk("x=%d===Y=%d\n",event->x[0],event->y[0]);
+
+#ifdef TOUCH_KEY_FOR_ANGDA
+ if((1==event->touch_point)&&(event->x1 > TOUCH_KEY_X_LIMIT)){
+ key_tp = 1;
+ if(event->x1 < 40){
+ key_val = 1;
+ input_report_key(data->input_dev, key_val, 1);
+ input_sync(data->input_dev);
+ // print_point_info("===KEY 1====\n");
+ }else if(event->y1 < 90){
+ key_val = 2;
+ input_report_key(data->input_dev, key_val, 1);
+ input_sync(data->input_dev);
+ // print_point_info("===KEY 2 ====\n");
+ }else{
+ key_val = 3;
+ input_report_key(data->input_dev, key_val, 1);
+ input_sync(data->input_dev);
+ // print_point_info("===KEY 3====\n");
+ }
+ } else{
+ key_tp = 0;
+ }
+#endif
+#ifdef TOUCH_KEY_FOR_EVB13
+ if((1==event->touch_point)&&((event->y[0] > 510)&&(event->y[0]<530)))
+ {
+ if(key_tp != 1)
+ {
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_sync(data->input_dev);
+ }
+ else
+ {
+ //printk("===KEY touch ++++++++++====++=\n");
+
+ if(event->x[0] < 90){
+ key_val = 1;
+ input_report_key(data->input_dev, KEY_MENU, 1);
+ input_sync(data->input_dev);
+ // printk("===KEY 1===++=\n");
+ }else if((event->x[0] < 230)&&(event->x[0]>185)){
+ key_val = 2;
+ input_report_key(data->input_dev, KEY_BACK, 1);
+ input_sync(data->input_dev);
+ // printk("===KEY 2 ====\n");
+ }else if((event->x[0] < 355)&&(event->x[0]>305)){
+ key_val = 3;
+ input_report_key(data->input_dev, KEY_SEARCH, 1);
+ input_sync(data->input_dev);
+ // print_point_info("===KEY 3====\n");
+ }else if ((event->x[0] < 497)&&(event->x[0]>445)) {
+ key_val = 4;
+ input_report_key(data->input_dev, KEY_HOMEPAGE, 1);
+ input_sync(data->input_dev);
+ // print_point_info("===KEY 4====\n");
+ }else if ((event->x[0] < 615)&&(event->x[0]>570)) {
+ key_val = 5;
+ input_report_key(data->input_dev, KEY_VOLUMEDOWN, 1);
+ input_sync(data->input_dev);
+ // print_point_info("===KEY 5====\n");
+ }else if ((event->x[0] < 750)&&(event->x[0]>705)) {
+ key_val = 6;
+ input_report_key(data->input_dev, KEY_VOLUMEUP, 1);
+ input_sync(data->input_dev);
+ // print_point_info("===KEY 6====\n");
+ }
+ }
+ key_tp = 1;
+ }
+ else
+ {
+ key_tp = 0;
+ }
+#endif
+
+#ifdef TOUCH_KEY_LIGHT_SUPPORT
+ AW5306_lighting();
+#endif
+ return;
+}
+#endif
+
+
+static void AW5306_report_value(void)
+{
+ AW5306_report_multitouch();
+#ifdef TOUCH_KEY_SUPPORT
+ AW5306_report_touchkey();
+#endif
+ return;
+} /*end AW5306_report_value*/
+
+
+#ifdef INTMODE
+static void AW5306_ts_pen_irq_work(struct work_struct *work)
+{
+ int ret = -1;
+
+ ret = AW5306_read_data();
+ if (ret == 0)
+ AW5306_report_value();
+
+ wmt_enable_gpirq(pContext->irqgpio);
+
+ return;
+}
+#else
+static void AW5306_ts_pen_irq_work(struct work_struct *work)
+{
+ int ret = -1;
+
+ if(suspend_flag != 1)
+ {
+ ret = AW5306_read_data();
+ if (ret == 0) {
+ AW5306_report_value();
+ }
+ }
+ else
+ {
+ AW5306_Sleep();
+ }
+}
+
+#endif
+
+
+
+static irqreturn_t aw5306_interrupt(int irq, void *dev)
+{
+ struct AW5306_ts_data *AW5306_ts= dev;
+
+//printk("I\n");
+ if (wmt_is_tsint(AW5306_ts->irqgpio))
+ {
+ wmt_clr_int(AW5306_ts->irqgpio);
+ if (wmt_is_tsirq_enable(AW5306_ts->irqgpio))
+ {
+ wmt_disable_gpirq(AW5306_ts->irqgpio);
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ if(!AW5306_ts->earlysus) queue_work(AW5306_ts->ts_workqueue , &AW5306_ts->pen_event_work);
+ #else
+ queue_work(AW5306_ts->ts_workqueue , &AW5306_ts->pen_event_work);
+ #endif
+
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+/*
+static void aw5306_reset(struct AW5306_ts_data *aw5306)
+{
+ gpio_set_value(aw5306->rstgpio, 0);
+ mdelay(5);
+ gpio_set_value(aw5306->rstgpio, 1);
+ mdelay(5);
+ gpio_set_value(aw5306->rstgpio, 0);
+ mdelay(5);
+
+ return;
+}
+*/
+
+void AW5306_tpd_polling(unsigned long data)
+ {
+ struct AW5306_ts_data *AW5306_ts = i2c_get_clientdata(l_client);
+
+#ifdef INTMODE
+ if (!work_pending(&AW5306_ts->pen_event_work)) {
+ queue_work(AW5306_ts->ts_workqueue, &AW5306_ts->pen_event_work);
+ }
+#else
+
+ if (!work_pending(&AW5306_ts->pen_event_work)) {
+ queue_work(AW5306_ts->ts_workqueue, &AW5306_ts->pen_event_work);
+ }
+ if(suspend_flag != 1)
+ {
+ #ifdef AUTO_RUDUCEFRAME
+ if(tp_SlowMode)
+ {
+ AW5306_ts->touch_timer.expires = jiffies + HZ/AWTPCfg.SLOW_FRAME;
+ }
+ else
+ {
+ AW5306_ts->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ }
+ #else
+ AW5306_ts->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ #endif
+ add_timer(&AW5306_ts->touch_timer);
+ }
+#endif
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void aw5306_early_suspend(struct early_suspend *handler)
+{
+#ifdef INTMODE
+ if(suspend_flag != 1)
+ {
+ wmt_disable_gpirq(AW5306_ts->irqgpio);
+ AW5306_Sleep();
+ suspend_flag = 1;
+ }
+#else
+ if(suspend_flag != 1)
+ {
+ printk("AW5306 SLEEP!!!");
+ suspend_flag = 1;
+ }
+#endif
+
+ return;
+}
+
+static void aw5306_late_resume(struct early_suspend *handler)
+{
+ struct AW5306_ts_data *AW5306_ts= container_of(handler, struct AW5306_ts_data , early_suspend);
+#ifdef INTMODE
+ if(suspend_flag != 0)
+ {
+ gpio_direction_output(AW5306_ts->rstgpio, 0);
+ AW5306_User_Cfg1();
+ AW5306_TP_Reinit();
+ wmt_enable_gpirq(AW5306_ts->irqgpio);
+ suspend_flag = 0;
+ }
+#else
+ if(suspend_flag != 0)
+ {
+ gpio_direction_output(AW5306_ts->rstgpio, 0);
+ AW5306_User_Cfg1();
+ AW5306_TP_Reinit();
+ tp_idlecnt = 0;
+ tp_SlowMode = 0;
+ suspend_flag = 0;
+ printk("AW5306 WAKE UP!!!");
+ AW5306_ts->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ add_timer(&AW5306_ts->touch_timer);
+ }
+#endif
+
+ return;
+}
+#endif //CONFIG_HAS_EARLYSUSPEND
+
+#ifdef CONFIG_PM
+static int aw5306_suspend(struct platform_device *pdev, pm_message_t state)
+{
+#ifdef INTMODE
+ if(suspend_flag != 1)
+ {
+ wmt_disable_gpirq(pContext->irqgpio);
+ AW5306_Sleep();
+ suspend_flag = 1;
+ }
+#else
+ if(suspend_flag != 1)
+ {
+ printk("AW5306 SLEEP!!!");
+ suspend_flag = 1;
+ }
+#endif
+ return 0;
+
+}
+
+static int aw5306_resume(struct platform_device *pdev)
+{
+ struct AW5306_ts_data *AW5306_ts= dev_get_drvdata(&pdev->dev);
+
+#ifdef INTMODE
+ if(suspend_flag != 0)
+ {
+ gpio_direction_output(AW5306_ts->rstgpio, 0);
+ AW5306_User_Cfg1();
+ AW5306_TP_Reinit();
+ suspend_flag = 0;
+ printk("AW5306 WAKE UP_intmode!!!");
+ wmt_enable_gpirq(AW5306_ts->irqgpio);
+ }
+#else
+ if(suspend_flag != 0)
+ {
+ gpio_direction_output(AW5306_ts->rstgpio, 0);
+ AW5306_User_Cfg1();
+ AW5306_TP_Reinit();
+ tp_idlecnt = 0;
+ tp_SlowMode = 0;
+ suspend_flag = 0;
+ printk("AW5306 WAKE UP!!!");
+ AW5306_ts->touch_timer.expires = jiffies + HZ/AWTPCfg.FAST_FRAME;
+ add_timer(&AW5306_ts->touch_timer);
+ }
+#endif
+
+ return 0;
+}
+
+#else
+#define aw5306_suspend NULL
+#define aw5306_resume NULL
+#endif
+
+static int aw5306_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct AW5306_ts_data *AW5306_ts = platform_get_drvdata( pdev);
+ u8 reg_value;
+
+ //aw5306_reset(AW5306_ts);
+
+ reg_value = AW_I2C_ReadByte(0x01);
+ if(reg_value != 0xA8)
+ {
+ //l_client->addr = 0x39;
+ dbg_err("AW5306_ts_probe: CHIP ID NOT CORRECT\n");
+ return -ENODEV;
+ }
+
+ i2c_set_clientdata(l_client, AW5306_ts);
+
+ INIT_WORK(&AW5306_ts->pen_event_work, AW5306_ts_pen_irq_work);
+ AW5306_ts->ts_workqueue = create_singlethread_workqueue(AW5306_ts->name);
+ if (!AW5306_ts->ts_workqueue ) {
+ err = -ESRCH;
+ goto exit_create_singlethread;
+ }
+
+ AW5306_ts->input_dev = input_allocate_device();
+ if (!AW5306_ts->input_dev) {
+ err = -ENOMEM;
+ dbg_err("failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ AW5306_ts->input_dev->name = AW5306_ts->name;
+ AW5306_ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(INPUT_PROP_DIRECT, AW5306_ts->input_dev->propbit);
+
+ if (AW5306_ts->lcd_exchg) {
+ input_set_abs_params(AW5306_ts->input_dev,
+ ABS_MT_POSITION_X, 0, AW5306_ts->resly, 0, 0);
+ input_set_abs_params(AW5306_ts->input_dev,
+ ABS_MT_POSITION_Y, 0, AW5306_ts->reslx, 0, 0);
+ } else {
+ input_set_abs_params(AW5306_ts->input_dev,
+ ABS_MT_POSITION_X, 0, AW5306_ts->reslx, 0, 0);
+ input_set_abs_params(AW5306_ts->input_dev,
+ ABS_MT_POSITION_Y, 0, AW5306_ts->resly, 0, 0);
+ }
+ input_set_abs_params(AW5306_ts->input_dev,
+ ABS_MT_TRACKING_ID, 0, 4, 0, 0);
+
+ err = input_register_device(AW5306_ts->input_dev);
+ if (err) {
+ dbg_err("aw5306_ts_probe: failed to register input device.\n");
+ goto exit_input_register_device_failed;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ AW5306_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ AW5306_ts->early_suspend.suspend = aw5306_early_suspend;
+ AW5306_ts->early_suspend.resume = aw5306_late_resume;
+ register_early_suspend(&AW5306_ts->early_suspend);
+#endif
+
+ AW5306_create_sysfs(l_client);
+ memcpy(AW_CALI_FILENAME,"/data/tpcali",12);
+ //memcpy(AW_UCF_FILENAME,"/data/AWTPucf",13);
+ printk("ucf file: %s\n", AW_UCF_FILENAME);
+
+ AW5306_TP_Init();
+
+ AW5306_ts->touch_timer.function = AW5306_tpd_polling;
+ AW5306_ts->touch_timer.data = 0;
+ init_timer(&AW5306_ts->touch_timer);
+ AW5306_ts->touch_timer.expires = jiffies + HZ*10;
+ add_timer(&AW5306_ts->touch_timer);
+
+#ifdef INTMODE
+
+ if(request_irq(AW5306_ts->irq, aw5306_interrupt, IRQF_SHARED, AW5306_ts->name, AW5306_ts) < 0){
+ dbg_err("Could not allocate irq for ts_aw5306 !\n");
+ err = -1;
+ goto exit_register_irq;
+ }
+
+ wmt_set_gpirq(AW5306_ts->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_enable_gpirq(AW5306_ts->irqgpio);
+
+#endif
+
+
+
+
+ return 0;
+
+exit_register_irq:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&AW5306_ts->early_suspend);
+#endif
+exit_input_register_device_failed:
+ input_free_device(AW5306_ts->input_dev);
+exit_input_dev_alloc_failed:
+//exit_create_group:
+ cancel_work_sync(&AW5306_ts->pen_event_work);
+ destroy_workqueue(AW5306_ts->ts_workqueue );
+exit_create_singlethread:
+ return err;
+}
+
+static int aw5306_remove(struct platform_device *pdev)
+{
+ struct AW5306_ts_data *AW5306_ts= platform_get_drvdata( pdev);
+
+ del_timer(&AW5306_ts->touch_timer);
+
+
+#ifdef INTMODE
+ wmt_disable_gpirq(AW5306_ts->irqgpio);
+ free_irq(AW5306_ts->irq, AW5306_ts);
+#endif
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&AW5306_ts->early_suspend);
+#endif
+ input_unregister_device(AW5306_ts->input_dev);
+ input_free_device(AW5306_ts->input_dev);
+
+ cancel_work_sync(&AW5306_ts->pen_event_work);
+ flush_workqueue(AW5306_ts->ts_workqueue);
+ destroy_workqueue(AW5306_ts->ts_workqueue);
+
+ dbg("remove...\n");
+ return 0;
+}
+
+static void aw5306_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device aw5306_device = {
+ .name = DEV_AW5306,
+ .id = 0,
+ .dev = {.release = aw5306_release},
+};
+
+static struct platform_driver aw5306_driver = {
+ .driver = {
+ .name = DEV_AW5306,
+ .owner = THIS_MODULE,
+ },
+ .probe = aw5306_probe,
+ .remove = aw5306_remove,
+ .suspend = aw5306_suspend,
+ .resume = aw5306_resume,
+};
+
+static int check_touch_env(struct AW5306_ts_data *AW5306_ts)
+{
+ int ret = 0;
+ int len = 96;
+ int Enable;
+ char retval[96] = {0};
+ char ucfname[20] = {0};
+ char *p=NULL, *s=NULL;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ //printk("MST FT5x0x:Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ sscanf(retval,"%d:",&Enable);
+ //check touch enable
+ if(Enable == 0){
+ //printk("FT5x0x Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(retval,':');
+ p++;
+ if(strncmp(p,"aw5306",6)) return -ENODEV;
+ AW5306_ts->name = DEV_AW5306;
+ s = strchr(p, ':');
+ p = p + 7;
+ if (s <= p)
+ return -ENODEV;
+ strncpy(ucfname, p, s-p);
+ sprintf(AW_UCF_FILENAME, "/lib/firmware/%s", ucfname);
+
+ s++;
+ sscanf(s,"%d:%d:%d:%d:%d:%d:%d:%d", &AW5306_ts->irqgpio, &AW5306_ts->reslx, &AW5306_ts->resly, &AW5306_ts->rstgpio, &AW5306_ts->swap, &AW5306_ts->xch, &AW5306_ts->ych, &AW5306_ts->nt);
+
+ AW5306_ts->irq = IRQ_GPIO;
+
+ printk("%s irqgpio=%d, reslx=%d, resly=%d, rstgpio=%d, swap=%d, xch=%d, ych=%d, nt=%d\n", AW5306_ts->name, AW5306_ts->irqgpio, AW5306_ts->reslx, AW5306_ts->resly, AW5306_ts->rstgpio, AW5306_ts->swap, AW5306_ts->xch, AW5306_ts->ych, AW5306_ts->nt);
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ AW5306_ts->lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = TS_I2C_NAME,
+ .flags = 0x00,
+ .addr = AW5306_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+
+ //ts_i2c_board_info.addr = AW5306_I2C_ADDR;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(AW5306_I2C_BUS);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+static int __init aw5306_init(void)
+{
+ int ret = -ENOMEM;
+ struct AW5306_ts_data *AW5306_ts=NULL;
+
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ AW5306_ts = kzalloc(sizeof(struct AW5306_ts_data), GFP_KERNEL);
+ if(!AW5306_ts){
+ dbg_err("mem alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ pContext = AW5306_ts;
+ ret = check_touch_env(AW5306_ts);
+ if(ret < 0)
+ goto exit_free_mem;
+
+ ret = gpio_request(AW5306_ts->irqgpio, "ts_irq");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen irq request fail\n", AW5306_ts->irqgpio);
+ goto exit_free_mem;
+ }
+ //wmt_gpio_setpull(AW5306_ts->irqgpio, WMT_GPIO_PULL_UP);
+ gpio_direction_input(AW5306_ts->irqgpio);
+
+ ret = gpio_request(AW5306_ts->rstgpio, "ts_rst");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen reset request fail\n", AW5306_ts->rstgpio);
+ goto exit_free_irqgpio;
+ }
+ gpio_direction_output(AW5306_ts->rstgpio, 0);
+
+
+ ret = platform_device_register(&aw5306_device);
+ if(ret){
+ dbg_err("register platform drivver failed!\n");
+ goto exit_free_gpio;
+ }
+ platform_set_drvdata(&aw5306_device, AW5306_ts);
+
+ ret = platform_driver_register(&aw5306_driver);
+ if(ret){
+ dbg_err("register platform device failed!\n");
+ goto exit_unregister_pdev;
+ }
+
+/*
+ i2c_dev_class = class_create(THIS_MODULE,"aw_i2c_dev");
+ if (IS_ERR(i2c_dev_class)) {
+ ret = PTR_ERR(i2c_dev_class);
+ class_destroy(i2c_dev_class);
+ }
+*/
+
+
+ return ret;
+
+exit_unregister_pdev:
+ platform_device_unregister(&aw5306_device);
+exit_free_gpio:
+ gpio_free(AW5306_ts->rstgpio);
+exit_free_irqgpio:
+ gpio_free(AW5306_ts->irqgpio);
+exit_free_mem:
+ kfree(AW5306_ts);
+ pContext = NULL;
+ return ret;
+}
+
+static void aw5306_exit(void)
+{
+ if(!pContext) return;
+
+ platform_driver_unregister(&aw5306_driver);
+ platform_device_unregister(&aw5306_device);
+ gpio_free(pContext->rstgpio);
+ gpio_free(pContext->irqgpio);
+ kfree(pContext);
+ ts_i2c_unregister_device();
+ return;
+}
+
+late_initcall(aw5306_init);
+module_exit(aw5306_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("FocalTech.Touch");
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_userpara.c b/drivers/input/touchscreen/aw5306_ts/AW5306_userpara.c
new file mode 100755
index 00000000..99f65f87
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_userpara.c
@@ -0,0 +1,196 @@
+#include "AW5306_Reg.h"
+#include "AW5306_Drv.h"
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "AW5306_userpara.h"
+
+#define POS_PRECISION 64
+
+extern AW5306_UCF AWTPCfg;
+extern STRUCTCALI AW_Cali;
+extern char AW5306_WorkMode;
+extern STRUCTNOISE AW_Noise;
+
+extern void AW5306_CLB_WriteCfg(void);
+extern int AW_I2C_WriteByte(unsigned char addr, unsigned char data);
+extern unsigned char AW_I2C_ReadByte(unsigned char addr);
+extern unsigned char AW_I2C_ReadXByte( unsigned char *buf, unsigned char addr, unsigned short len);
+extern unsigned char AW5306_RAWDATACHK(void);
+
+const STRUCTCALI Default_Cali1 =
+{
+ "AWINIC TP CALI",
+ //{0x33,0x23,0x22,0x22,0x22,0x22,0x22,0x02,0x22,0x22}, //TXOFFSET
+ {0x32,0x32,0x23,0x32,0x33,0x33,0x33,0x03,0x22,0x22}, //TXOFFSET
+ //{0x9A,0xA9,0xAA,0xA9,0x9B,0x00}, //RXOFFSET
+ {0x35,0x44,0x55,0x54,0x34,0x00}, //RXOFFSET
+ //{0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c},//TXCAC
+ {0x2C,0x2B,0x2B,0x2A,0x2A,0x2C,0x2C,0x2C,0x2C,0x2C,0x2D,0x2D,0x2D,0x2D,0x31,0x2C,0x2C,0x2C,0x2C,0x2C},//TXCAC
+ //{0x3d,0x3c,0x3c,0x3c,0x3e,0x3a,0x3a,0x3e,0x3c,0x3b,0x3c,0x3c},//RXCAC
+ {0x84,0x84,0x82,0x82,0x80,0x86,0x86,0x80,0x8C,0x82,0x84,0x84},//RXCAC
+ //{0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x2e,0x2e,0x0e,0x0e,0x0e,0x0e,0x0e},//TXGAIN
+ {0x88,0x88,0x88,0x88,0x88,0x68,0x68,0x68,0x68,0x68,0x48,0x48,0x48,0x48,0x28,0x08,0x08,0x08,0x08,0x08},//TXGAIN
+};
+
+const AW5306_UCF Default_UCF =
+{
+ 15, //TX_NUM
+ 10, //RX_NUM
+ {17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0}, //TX_ORDER
+ {9,8,7,6,5,4,3,2,1,0,0,0}, //RX_ORDER
+ 0, //RX_START
+ 0, //HAVE_KEY_LINE
+ {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, //KeyLineValid
+
+ 600, //MAPPING_MAX_X
+ 1024, //MAPPING_MAX_Y
+
+ 350, //GainClbDeltaMin
+ 450, //GainClbDeltaMax
+ 550, //KeyLineDeltaMin
+ 650, //KeyLineDeltaMax
+ 8300, //OffsetClbExpectedMin
+ 8500, //OffsetClbExpectedMax
+ 300, //RawDataDeviation
+ 8, //CacMultiCoef
+
+ 7000, //RawDataCheckMin
+ 10000, //RawDataCheckMax
+
+ 200, //FLYING_TH
+ 100, //MOVING_TH
+ 50, //MOVING_ACCELER
+
+ 70, //PEAK_TH
+ 80, //GROUP_TH
+ 90, //BIGAREA_TH
+ 25, //BIGAREA_CNT
+ 20, //BIGAREA_FRESHCNT
+
+ 1, //CACULATE_COEF
+
+ 1, //FIRST_CALI
+ 0, //RAWDATA_DUMP_SWITCH
+
+ 1, //MULTI_SCANFREQ
+ 5, //BASE_FREQ
+ 0x83, //FREQ_OFFSET
+ 0x00, //WAIT_TIME
+ 0x2b, //CHAMP_CFG
+ 0x10, //POSLEVEL_TH
+
+ 1, //ESD_PROTECT
+
+ 0, //MARGIN_COMPENSATE
+ 0, //MARGIN_COMP_DATA_UP
+ 0, //MARGIN_COMP_DATA_DOWN
+ 0, //MARGIN_COMP_DATA_LEFT
+ 0, //MARGIN_COMP_DATA_RIGHT
+
+ 1, //POINT_RELEASEHOLD
+ 0, //MARGIN_RELEASEHOLD
+ 0, //POINT_PRESSHOLD
+ 1, //KEY_PRESSHOLD
+
+ 0, //PEAK_ROW_COMPENSATE
+ 1, //PEAK_COL_COMPENSATE
+ 3, //PEAK_COMPENSATE_COEF
+
+ 0, //LCD_NOISE_PROCESS
+ 50, //LCD_NOISETH
+
+ 0, //FALSE_PEAK_PROCESS
+ 100, //FALSE_PEAK_TH
+
+ 6, //STABLE_DELTA_X
+ 6, //STABLE_DELTA_Y
+
+ 0, //DEBUG_LEVEL
+
+ 50, //FAST_FRAME
+ 20, //SLOW_FRAME
+
+ 0, //GAIN_CLB_SEPERATE
+ 5, //MARGIN_PREFILTER
+ 0, //BIGAREA_HOLDPOINT
+ 50, //CHARGE_NOISE
+ 0, //FREQ_JUMP
+ 0, //PEAK_VALID_CHECK
+ 1, //WATER_REMOVE
+
+#ifdef INTMODE
+ 1 //INT_MODE
+#else
+ 0 //POLL_MODE
+#endif
+};
+
+void AW5306_User_Cfg1(void)
+{
+ unsigned char i;
+
+
+ for(i=0;i<AWTPCfg.TX_LOCAL;i++)
+ {
+ AW_I2C_WriteByte(SA_TX_INDEX0+i,AWTPCfg.TX_ORDER[i]); //TX REVERT
+ }
+
+ AW_I2C_WriteByte(SA_TX_NUM,AWTPCfg.TX_LOCAL);
+ AW_I2C_WriteByte(SA_RX_NUM,AWTPCfg.RX_LOCAL);
+
+ if(1 == AWTPCfg.MULTI_SCANFREQ)
+ {
+ AW_I2C_WriteByte(SA_SCANFREQ1,AWTPCfg.BASE_FREQ);
+ AW_I2C_WriteByte(SA_SCANFREQ2,AWTPCfg.BASE_FREQ);
+ AW_I2C_WriteByte(SA_SCANFREQ3,AWTPCfg.BASE_FREQ);
+ }
+ else
+ {
+ AW_I2C_WriteByte(SA_SCANFREQ1,AWTPCfg.BASE_FREQ); //3-5
+ }
+
+
+ AW_I2C_WriteByte(SA_WAITTIME,AWTPCfg.WAIT_TIME);
+ AW_I2C_WriteByte(SA_RX_START,AWTPCfg.RX_START);
+ AW_I2C_WriteByte(SA_SCANTIM,4); // set to 32 TX cycles mode
+
+ AW_I2C_WriteByte(SA_PAGE,1);
+ AW_I2C_WriteByte(SA_CHAMPCFG,AWTPCfg.CHAMP_CFG); //
+ AW_I2C_WriteByte(SA_OSCCFG1,AWTPCfg.FREQ_OFFSET); //
+ AW_I2C_WriteByte(SA_OSCCFG2,0x10); //TRIM register
+ AW_I2C_WriteByte(SA_POSLEVELTH,AWTPCfg.POSLEVEL_TH);
+
+ AW_I2C_WriteByte(SA_CPFREQ,0x00); //for AW256
+ AW_I2C_WriteByte(SA_PAGE,0);
+
+ AW5306_CLB_WriteCfg();
+ //printk("AW5306 user config finished TXCAC0 = %x",AW_Cali.TXCAC[0] );
+}
+
+void AW5306_User_Init(void)
+{
+ unsigned char ret;
+
+ ret = 0;
+
+ AW5306_WorkMode = DeltaMode; //DeltaMode: chip output delta data RawDataMode: chip output rawdata
+
+ memcpy(&AWTPCfg,&Default_UCF,sizeof(AW5306_UCF));
+ memcpy(&AW_Cali,&Default_Cali1,sizeof(STRUCTCALI)); //load default cali value
+
+
+ //AW_I2C_WriteByte(SA_PAGE,0);
+ //AW_I2C_WriteByte(SA_IDRST,0x55);
+
+ AW5306_User_Cfg1();
+
+ AW_Noise.FrmState = NOISE_FRM_NORMAL;
+ AW_Noise.WorkFreqID = 16;
+ AW_Noise.ScanFreqID = AW_Noise.WorkFreqID;
+ AW_Noise.State = NOISE_LISTENING;
+ AW_Noise.NoiseTh1 = 60;
+ AW_Noise.JumpTh = 5;
+ AW_Noise.Better_NoiseScan = 1000;
+ //ret = AW5306_RAWDATACHK();
+
+}
diff --git a/drivers/input/touchscreen/aw5306_ts/AW5306_userpara.h b/drivers/input/touchscreen/aw5306_ts/AW5306_userpara.h
new file mode 100755
index 00000000..15d5c180
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/AW5306_userpara.h
@@ -0,0 +1,99 @@
+#ifndef AW5306_USERPARA_H
+
+#define AW5306_USERPARA_H
+
+#define INTMODE
+
+typedef struct {
+ unsigned char TX_LOCAL; // 15 //TX number of TP
+ unsigned char RX_LOCAL; // 10 //RX number of TP
+ unsigned char TX_ORDER[22]; // TX ORDER
+ unsigned char RX_ORDER[12]; // RX mapping in inverted order
+ unsigned char RX_START; //RX START LINE
+ unsigned char HAVE_KEY_LINE; // 0: no KEY line, 1: have key line on TX line TX_LOCAL-1
+ unsigned char KeyLineValid[16];
+
+ unsigned short MAPPING_MAX_X; // 320
+ unsigned short MAPPING_MAX_Y; // 460
+
+ unsigned short GainClbDeltaMin; // Expected minimum delta for GAIN calibration
+ unsigned short GainClbDeltaMax; // Expected maximum delta for GAIN calibration
+ unsigned short KeyLineDeltaMin;
+ unsigned short KeyLineDeltaMax;
+ unsigned short OffsetClbExpectedMin; // Expected minimum data for OFFSET calibration
+ unsigned short OffsetClbExpectedMax; // Expected minimum data for OFFSET calibration
+ unsigned short RawDataDeviation; // Maximum deviation in a frame
+ unsigned short CacMultiCoef;
+
+ unsigned short RawDataCheckMin;
+ unsigned short RawDataCheckMax;
+
+ unsigned short FLYING_TH;
+ unsigned short MOVING_TH;
+ unsigned short MOVING_ACCELER;
+
+ unsigned char PEAK_TH;
+ unsigned char GROUP_TH;
+ unsigned char BIGAREA_TH;
+ unsigned char BIGAREA_CNT;
+ unsigned char BIGAREA_FRESHCNT;
+
+ unsigned char CACULATE_COEF;
+
+ unsigned char FIRST_CALI;
+ unsigned char RAWDATA_DUMP_SWITCH;
+ unsigned char MULTI_SCANFREQ;
+ unsigned char BASE_FREQ;
+ unsigned char FREQ_OFFSET;
+ unsigned char WAIT_TIME;
+ unsigned char CHAMP_CFG;
+ unsigned char POSLEVEL_TH;
+
+ unsigned char ESD_PROTECT;
+
+ unsigned char MARGIN_COMPENSATE;
+ unsigned char MARGIN_COMP_DATA_UP;
+ unsigned char MARGIN_COMP_DATA_DOWN;
+ unsigned char MARGIN_COMP_DATA_LEFT;
+ unsigned char MARGIN_COMP_DATA_RIGHT;
+
+ unsigned char POINT_RELEASEHOLD;
+ unsigned char MARGIN_RELEASEHOLD;
+ unsigned char POINT_PRESSHOLD;
+ unsigned char KEY_PRESSHOLD;
+
+ unsigned char PEAK_ROW_COMPENSATE;
+ unsigned char PEAK_COL_COMPENSATE;
+ unsigned char PEAK_COMPENSATE_COEF;
+
+ unsigned char LCD_NOISE_PROCESS;
+ unsigned char LCD_NOISETH;
+
+ unsigned char FALSE_PEAK_PROCESS;
+ unsigned char FALSE_PEAK_TH;
+
+ unsigned char STABLE_DELTA_X;
+ unsigned char STABLE_DELTA_Y;
+
+ unsigned char DEBUG_LEVEL;
+
+ unsigned char FAST_FRAME;
+ unsigned char SLOW_FRAME;
+
+ unsigned char GAIN_CLB_SEPERATE;
+
+ unsigned char MARGIN_PREFILTER;
+
+ unsigned char BIGAREA_HOLDPOINT;
+ unsigned char CHARGE_NOISE;
+ unsigned char FREQ_JUMP;
+ unsigned char PEAK_VALID_CHECK;
+ unsigned char WATER_REMOVE;
+ unsigned char INT_MODE;
+
+}AW5306_UCF;
+
+void AW5306_User_Init(void);
+void AW5306_User_Cfg1(void);
+
+#endif
diff --git a/drivers/input/touchscreen/aw5306_ts/Kconfig b/drivers/input/touchscreen/aw5306_ts/Kconfig
new file mode 100755
index 00000000..32029915
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/Kconfig
@@ -0,0 +1,11 @@
+config TOUCHSCREEN_AW5306
+ tristate "AW5306 Capacity Touchscreen Device Support"
+ default y
+ depends on ARCH_WMT
+ ---help---
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+ If unsure, say N.
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_aw5306.
+
diff --git a/drivers/input/touchscreen/aw5306_ts/Makefile b/drivers/input/touchscreen/aw5306_ts/Makefile
new file mode 100755
index 00000000..2a79cbe6
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/Makefile
@@ -0,0 +1,35 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+ #DEBFLAGS = -O2 -Wall -L./libAW5306.a
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_aw5306
+
+#obj-$(CONFIG_TOUCHSCREEN_FT5X0X) := $(MY_MODULE_NAME).o
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := AW5306_ts.o irq_gpio.o AW5306_userpara.o AW5306_Base.b AW5306_Clb.b AW5306_Drv.b
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+# @rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ @rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers modules.builtin
+
diff --git a/drivers/input/touchscreen/aw5306_ts/irq_gpio.c b/drivers/input/touchscreen/aw5306_ts/irq_gpio.c
new file mode 100755
index 00000000..8bdf9f20
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/irq_gpio.c
@@ -0,0 +1,149 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include "irq_gpio.h"
+
+int wmt_enable_gpirq(int num)
+{
+ if(num > 15)
+ return -1;
+
+ if(num < 4)
+ REG32_VAL(__GPIO_BASE+0x0300) |= 1<<(num*8+7); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) |= 1<<((num-4)*8+7); //enable interrupt
+ else if(num >= 8 && num < 12)
+ REG32_VAL(__GPIO_BASE+0x0308) |= 1<<((num-8)*8+7); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x030C) |= 1<<((num-12)*8+7); //enable interrupt
+
+ return 0;
+}
+
+int wmt_disable_gpirq(int num)
+{
+ if(num > 15)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) &= ~(1<<(num*8+7)); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) &= ~(1<<((num-4)*8+7)); //enable interrupt
+ else if(num >= 8 && num < 12)
+ REG32_VAL(__GPIO_BASE+0x0308) &= ~(1<<((num-8)*8+7)); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x030C) &= ~(1<<((num-12)*8+7)); //enable interrupt
+
+ return 0;
+}
+
+int wmt_is_tsirq_enable(int num)
+{
+ int val = 0;
+
+ if(num > 15)
+ return 0;
+
+ if(num<4)
+ val = REG32_VAL(__GPIO_BASE+0x0300) & (1<<(num*8+7));
+ else if(num >= 4 && num < 8)
+ val = REG32_VAL(__GPIO_BASE+0x0304) & (1<<((num-4)*8+7));
+ else if(num >= 8 && num < 12)
+ val = REG32_VAL(__GPIO_BASE+0x0308) & (1<<((num-8)*8+7));
+ else
+ val = REG32_VAL(__GPIO_BASE+0x030C) & (1<<((num-12)*8+7));
+
+ return val?1:0;
+
+}
+
+int wmt_is_tsint(int num)
+{
+ if (num > 15)
+ {
+ return 0;
+ }
+ return (REG32_VAL(__GPIO_BASE+0x0360) & (1<<num)) ? 1: 0;
+}
+
+void wmt_clr_int(int num)
+{
+ if (num > 15)
+ {
+ return;
+ }
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num;
+}
+
+int wmt_set_gpirq(int num, int type)
+{
+ int shift;
+ int offset;
+ unsigned long reg;
+
+ if(num >15)
+ return -1;
+ //if (num > 9)
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; // gpio10,11 as gpio
+
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num);//|=(1<<num);// //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull down
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down
+
+ //set gpio irq triger type
+ if(num < 4){//[0,3]
+ shift = num;
+ offset = 0x0300;
+ }else if(num >= 4 && num < 8){//[4,7]
+ shift = num-4;
+ offset = 0x0304;
+ }else if(num >= 8 && num < 12){//[8,11]
+ shift = num-8;
+ offset = 0x0308;
+ }else{// [12,15]
+ shift = num-12;
+ offset = 0x030C;
+ }
+
+ reg = REG32_VAL(__GPIO_BASE + offset);
+
+ switch(type){
+ case IRQ_TYPE_LEVEL_LOW:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ default://both edge
+ reg |= (1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+
+ }
+ //reg |= 1<<(shift*8+7);//enable interrupt
+ reg &= ~(1<<(shift*8+7)); //disable int
+
+ REG32_VAL(__GPIO_BASE + offset) = reg;
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num; //clear interrupt status
+ msleep(5);
+ return 0;
+}
+
+
diff --git a/drivers/input/touchscreen/aw5306_ts/irq_gpio.h b/drivers/input/touchscreen/aw5306_ts/irq_gpio.h
new file mode 100755
index 00000000..0232bd04
--- /dev/null
+++ b/drivers/input/touchscreen/aw5306_ts/irq_gpio.h
@@ -0,0 +1,13 @@
+#ifndef _LINUX_IRQ_GPIO_H
+#define _LINUX_IRQ_GPIO_H
+
+
+extern int wmt_enable_gpirq(int num);
+extern int wmt_disable_gpirq(int num);
+extern int wmt_is_tsirq_enable(int num);
+extern int wmt_is_tsint(int num);
+extern void wmt_clr_int(int num);
+extern int wmt_set_gpirq(int num, int type);
+
+
+#endif
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
new file mode 100644
index 00000000..f2d03c06
--- /dev/null
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/input/bu21013.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+
+#define PEN_DOWN_INTR 0
+#define MAX_FINGERS 2
+#define RESET_DELAY 30
+#define PENUP_TIMEOUT (10)
+#define DELTA_MIN 16
+#define MASK_BITS 0x03
+#define SHIFT_8 8
+#define SHIFT_2 2
+#define LENGTH_OF_BUFFER 11
+#define I2C_RETRY_COUNT 5
+
+#define BU21013_SENSORS_BTN_0_7_REG 0x70
+#define BU21013_SENSORS_BTN_8_15_REG 0x71
+#define BU21013_SENSORS_BTN_16_23_REG 0x72
+#define BU21013_X1_POS_MSB_REG 0x73
+#define BU21013_X1_POS_LSB_REG 0x74
+#define BU21013_Y1_POS_MSB_REG 0x75
+#define BU21013_Y1_POS_LSB_REG 0x76
+#define BU21013_X2_POS_MSB_REG 0x77
+#define BU21013_X2_POS_LSB_REG 0x78
+#define BU21013_Y2_POS_MSB_REG 0x79
+#define BU21013_Y2_POS_LSB_REG 0x7A
+#define BU21013_INT_CLR_REG 0xE8
+#define BU21013_INT_MODE_REG 0xE9
+#define BU21013_GAIN_REG 0xEA
+#define BU21013_OFFSET_MODE_REG 0xEB
+#define BU21013_XY_EDGE_REG 0xEC
+#define BU21013_RESET_REG 0xED
+#define BU21013_CALIB_REG 0xEE
+#define BU21013_DONE_REG 0xEF
+#define BU21013_SENSOR_0_7_REG 0xF0
+#define BU21013_SENSOR_8_15_REG 0xF1
+#define BU21013_SENSOR_16_23_REG 0xF2
+#define BU21013_POS_MODE1_REG 0xF3
+#define BU21013_POS_MODE2_REG 0xF4
+#define BU21013_CLK_MODE_REG 0xF5
+#define BU21013_IDLE_REG 0xFA
+#define BU21013_FILTER_REG 0xFB
+#define BU21013_TH_ON_REG 0xFC
+#define BU21013_TH_OFF_REG 0xFD
+
+
+#define BU21013_RESET_ENABLE 0x01
+
+#define BU21013_SENSORS_EN_0_7 0x3F
+#define BU21013_SENSORS_EN_8_15 0xFC
+#define BU21013_SENSORS_EN_16_23 0x1F
+
+#define BU21013_POS_MODE1_0 0x02
+#define BU21013_POS_MODE1_1 0x04
+#define BU21013_POS_MODE1_2 0x08
+
+#define BU21013_POS_MODE2_ZERO 0x01
+#define BU21013_POS_MODE2_AVG1 0x02
+#define BU21013_POS_MODE2_AVG2 0x04
+#define BU21013_POS_MODE2_EN_XY 0x08
+#define BU21013_POS_MODE2_EN_RAW 0x10
+#define BU21013_POS_MODE2_MULTI 0x80
+
+#define BU21013_CLK_MODE_DIV 0x01
+#define BU21013_CLK_MODE_EXT 0x02
+#define BU21013_CLK_MODE_CALIB 0x80
+
+#define BU21013_IDLET_0 0x01
+#define BU21013_IDLET_1 0x02
+#define BU21013_IDLET_2 0x04
+#define BU21013_IDLET_3 0x08
+#define BU21013_IDLE_INTERMIT_EN 0x10
+
+#define BU21013_DELTA_0_6 0x7F
+#define BU21013_FILTER_EN 0x80
+
+#define BU21013_INT_MODE_LEVEL 0x00
+#define BU21013_INT_MODE_EDGE 0x01
+
+#define BU21013_GAIN_0 0x01
+#define BU21013_GAIN_1 0x02
+#define BU21013_GAIN_2 0x04
+
+#define BU21013_OFFSET_MODE_DEFAULT 0x00
+#define BU21013_OFFSET_MODE_MOVE 0x01
+#define BU21013_OFFSET_MODE_DISABLE 0x02
+
+#define BU21013_TH_ON_0 0x01
+#define BU21013_TH_ON_1 0x02
+#define BU21013_TH_ON_2 0x04
+#define BU21013_TH_ON_3 0x08
+#define BU21013_TH_ON_4 0x10
+#define BU21013_TH_ON_5 0x20
+#define BU21013_TH_ON_6 0x40
+#define BU21013_TH_ON_7 0x80
+#define BU21013_TH_ON_MAX 0xFF
+
+#define BU21013_TH_OFF_0 0x01
+#define BU21013_TH_OFF_1 0x02
+#define BU21013_TH_OFF_2 0x04
+#define BU21013_TH_OFF_3 0x08
+#define BU21013_TH_OFF_4 0x10
+#define BU21013_TH_OFF_5 0x20
+#define BU21013_TH_OFF_6 0x40
+#define BU21013_TH_OFF_7 0x80
+#define BU21013_TH_OFF_MAX 0xFF
+
+#define BU21013_X_EDGE_0 0x01
+#define BU21013_X_EDGE_1 0x02
+#define BU21013_X_EDGE_2 0x04
+#define BU21013_X_EDGE_3 0x08
+#define BU21013_Y_EDGE_0 0x10
+#define BU21013_Y_EDGE_1 0x20
+#define BU21013_Y_EDGE_2 0x40
+#define BU21013_Y_EDGE_3 0x80
+
+#define BU21013_DONE 0x01
+#define BU21013_NUMBER_OF_X_SENSORS (6)
+#define BU21013_NUMBER_OF_Y_SENSORS (11)
+
+#define DRIVER_TP "bu21013_tp"
+
+/**
+ * struct bu21013_ts_data - touch panel data structure
+ * @client: pointer to the i2c client
+ * @wait: variable to wait_queue_head_t structure
+ * @touch_stopped: touch stop flag
+ * @chip: pointer to the touch panel controller
+ * @in_dev: pointer to the input device structure
+ * @intr_pin: interrupt pin value
+ * @regulator: pointer to the Regulator used for touch screen
+ *
+ * Touch panel device data structure
+ */
+struct bu21013_ts_data {
+ struct i2c_client *client;
+ wait_queue_head_t wait;
+ bool touch_stopped;
+ const struct bu21013_platform_device *chip;
+ struct input_dev *in_dev;
+ unsigned int intr_pin;
+ struct regulator *regulator;
+};
+
+/**
+ * bu21013_read_block_data(): read the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ * @buf: byte pointer
+ *
+ * Read the touch co-ordinates using i2c read block into buffer
+ * and returns integer.
+ */
+static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
+{
+ int ret, i;
+
+ for (i = 0; i < I2C_RETRY_COUNT; i++) {
+ ret = i2c_smbus_read_i2c_block_data
+ (data->client, BU21013_SENSORS_BTN_0_7_REG,
+ LENGTH_OF_BUFFER, buf);
+ if (ret == LENGTH_OF_BUFFER)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * bu21013_do_touch_report(): Get the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ *
+ * Get the touch co-ordinates from touch sensor registers and writes
+ * into device structure and returns integer.
+ */
+static int bu21013_do_touch_report(struct bu21013_ts_data *data)
+{
+ u8 buf[LENGTH_OF_BUFFER];
+ unsigned int pos_x[2], pos_y[2];
+ bool has_x_sensors, has_y_sensors;
+ int finger_down_count = 0;
+ int i;
+
+ if (data == NULL)
+ return -EINVAL;
+
+ if (bu21013_read_block_data(data, buf) < 0)
+ return -EINVAL;
+
+ has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
+ has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
+ ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
+ if (!has_x_sensors || !has_y_sensors)
+ return 0;
+
+ for (i = 0; i < MAX_FINGERS; i++) {
+ const u8 *p = &buf[4 * i + 3];
+ unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
+ unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
+ if (x == 0 || y == 0)
+ continue;
+ pos_x[finger_down_count] = x;
+ pos_y[finger_down_count] = y;
+ finger_down_count++;
+ }
+
+ if (finger_down_count) {
+ if (finger_down_count == 2 &&
+ (abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
+ abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) {
+ return 0;
+ }
+
+ for (i = 0; i < finger_down_count; i++) {
+ if (data->chip->x_flip)
+ pos_x[i] = data->chip->touch_x_max - pos_x[i];
+ if (data->chip->y_flip)
+ pos_y[i] = data->chip->touch_y_max - pos_y[i];
+
+ input_report_abs(data->in_dev,
+ ABS_MT_POSITION_X, pos_x[i]);
+ input_report_abs(data->in_dev,
+ ABS_MT_POSITION_Y, pos_y[i]);
+ input_mt_sync(data->in_dev);
+ }
+ } else
+ input_mt_sync(data->in_dev);
+
+ input_sync(data->in_dev);
+
+ return 0;
+}
+/**
+ * bu21013_gpio_irq() - gpio thread function for touch interrupt
+ * @irq: irq value
+ * @device_data: void pointer
+ *
+ * This gpio thread function for touch interrupt
+ * and returns irqreturn_t.
+ */
+static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
+{
+ struct bu21013_ts_data *data = device_data;
+ struct i2c_client *i2c = data->client;
+ int retval;
+
+ do {
+ retval = bu21013_do_touch_report(data);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
+ return IRQ_NONE;
+ }
+
+ data->intr_pin = data->chip->irq_read_val();
+ if (data->intr_pin == PEN_DOWN_INTR)
+ wait_event_timeout(data->wait, data->touch_stopped,
+ msecs_to_jiffies(2));
+ } while (!data->intr_pin && !data->touch_stopped);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * bu21013_init_chip() - power on sequence for the bu21013 controller
+ * @data: device structure pointer
+ *
+ * This function is used to power on
+ * the bu21013 controller and returns integer.
+ */
+static int bu21013_init_chip(struct bu21013_ts_data *data)
+{
+ int retval;
+ struct i2c_client *i2c = data->client;
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
+ BU21013_RESET_ENABLE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
+ return retval;
+ }
+ msleep(RESET_DELAY);
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
+ BU21013_SENSORS_EN_0_7);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
+ BU21013_SENSORS_EN_8_15);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
+ BU21013_SENSORS_EN_16_23);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
+ (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
+ (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
+ BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
+ BU21013_POS_MODE2_MULTI));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
+ return retval;
+ }
+
+ if (data->chip->ext_clk)
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
+ else
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
+ (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
+ BU21013_INT_MODE_LEVEL);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
+ (BU21013_DELTA_0_6 |
+ BU21013_FILTER_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
+ BU21013_TH_ON_5);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
+ BU21013_TH_OFF_4 | BU21013_TH_OFF_3);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
+ (BU21013_GAIN_0 | BU21013_GAIN_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
+ BU21013_OFFSET_MODE_DEFAULT);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
+ (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
+ BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
+ BU21013_DONE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+/**
+ * bu21013_free_irq() - frees IRQ registered for touchscreen
+ * @bu21013_data: device structure pointer
+ *
+ * This function signals interrupt thread to stop processing and
+ * frees interrupt.
+ */
+static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)
+{
+ bu21013_data->touch_stopped = true;
+ wake_up(&bu21013_data->wait);
+ free_irq(bu21013_data->chip->irq, bu21013_data);
+}
+
+/**
+ * bu21013_probe() - initializes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ * @id: i2c device id pointer
+ *
+ * This function used to initializes the i2c-client touchscreen
+ * driver and returns integer.
+ */
+static int __devinit bu21013_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bu21013_ts_data *bu21013_data;
+ struct input_dev *in_dev;
+ const struct bu21013_platform_device *pdata =
+ client->dev.platform_data;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "i2c smbus byte data not supported\n");
+ return -EIO;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data not defined\n");
+ return -EINVAL;
+ }
+
+ bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
+ in_dev = input_allocate_device();
+ if (!bu21013_data || !in_dev) {
+ dev_err(&client->dev, "device memory alloc failed\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ bu21013_data->in_dev = in_dev;
+ bu21013_data->chip = pdata;
+ bu21013_data->client = client;
+
+ bu21013_data->regulator = regulator_get(&client->dev, "V-TOUCH");
+ if (IS_ERR(bu21013_data->regulator)) {
+ dev_err(&client->dev, "regulator_get failed\n");
+ error = PTR_ERR(bu21013_data->regulator);
+ goto err_free_mem;
+ }
+
+ error = regulator_enable(bu21013_data->regulator);
+ if (error < 0) {
+ dev_err(&client->dev, "regulator enable failed\n");
+ goto err_put_regulator;
+ }
+
+ bu21013_data->touch_stopped = false;
+ init_waitqueue_head(&bu21013_data->wait);
+
+ /* configure the gpio pins */
+ if (pdata->cs_en) {
+ error = pdata->cs_en(pdata->cs_pin);
+ if (error < 0) {
+ dev_err(&client->dev, "chip init failed\n");
+ goto err_disable_regulator;
+ }
+ }
+
+ /* configure the touch panel controller */
+ error = bu21013_init_chip(bu21013_data);
+ if (error) {
+ dev_err(&client->dev, "error in bu21013 config\n");
+ goto err_cs_disable;
+ }
+
+ /* register the device to input subsystem */
+ in_dev->name = DRIVER_TP;
+ in_dev->id.bustype = BUS_I2C;
+ in_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, in_dev->evbit);
+ __set_bit(EV_KEY, in_dev->evbit);
+ __set_bit(EV_ABS, in_dev->evbit);
+
+ input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
+ pdata->touch_x_max, 0, 0);
+ input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
+ pdata->touch_y_max, 0, 0);
+ input_set_drvdata(in_dev, bu21013_data);
+
+ error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq,
+ IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ DRIVER_TP, bu21013_data);
+ if (error) {
+ dev_err(&client->dev, "request irq %d failed\n", pdata->irq);
+ goto err_cs_disable;
+ }
+
+ error = input_register_device(in_dev);
+ if (error) {
+ dev_err(&client->dev, "failed to register input device\n");
+ goto err_free_irq;
+ }
+
+ device_init_wakeup(&client->dev, pdata->wakeup);
+ i2c_set_clientdata(client, bu21013_data);
+
+ return 0;
+
+err_free_irq:
+ bu21013_free_irq(bu21013_data);
+err_cs_disable:
+ pdata->cs_dis(pdata->cs_pin);
+err_disable_regulator:
+ regulator_disable(bu21013_data->regulator);
+err_put_regulator:
+ regulator_put(bu21013_data->regulator);
+err_free_mem:
+ input_free_device(in_dev);
+ kfree(bu21013_data);
+
+ return error;
+}
+/**
+ * bu21013_remove() - removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This function uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int __devexit bu21013_remove(struct i2c_client *client)
+{
+ struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
+
+ bu21013_free_irq(bu21013_data);
+
+ bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin);
+
+ input_unregister_device(bu21013_data->in_dev);
+
+ regulator_disable(bu21013_data->regulator);
+ regulator_put(bu21013_data->regulator);
+
+ kfree(bu21013_data);
+
+ device_init_wakeup(&client->dev, false);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * bu21013_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int bu21013_suspend(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+ struct i2c_client *client = bu21013_data->client;
+
+ bu21013_data->touch_stopped = true;
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(bu21013_data->chip->irq);
+ else
+ disable_irq(bu21013_data->chip->irq);
+
+ regulator_disable(bu21013_data->regulator);
+
+ return 0;
+}
+
+/**
+ * bu21013_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int bu21013_resume(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+ struct i2c_client *client = bu21013_data->client;
+ int retval;
+
+ retval = regulator_enable(bu21013_data->regulator);
+ if (retval < 0) {
+ dev_err(&client->dev, "bu21013 regulator enable failed\n");
+ return retval;
+ }
+
+ retval = bu21013_init_chip(bu21013_data);
+ if (retval < 0) {
+ dev_err(&client->dev, "bu21013 controller config failed\n");
+ return retval;
+ }
+
+ bu21013_data->touch_stopped = false;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(bu21013_data->chip->irq);
+ else
+ enable_irq(bu21013_data->chip->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops bu21013_dev_pm_ops = {
+ .suspend = bu21013_suspend,
+ .resume = bu21013_resume,
+};
+#endif
+
+static const struct i2c_device_id bu21013_id[] = {
+ { DRIVER_TP, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bu21013_id);
+
+static struct i2c_driver bu21013_driver = {
+ .driver = {
+ .name = DRIVER_TP,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &bu21013_dev_pm_ops,
+#endif
+ },
+ .probe = bu21013_probe,
+ .remove = __devexit_p(bu21013_remove),
+ .id_table = bu21013_id,
+};
+
+module_i2c_driver(bu21013_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
+MODULE_DESCRIPTION("bu21013 touch screen controller driver");
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
new file mode 100644
index 00000000..237753ad
--- /dev/null
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -0,0 +1,357 @@
+/*
+ * Driver for cypress touch screen controller
+ *
+ * Copyright (c) 2009 Aava Mobile
+ *
+ * Some cleanups by Alan Cox <alan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/input/cy8ctmg110_pdata.h>
+
+#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;
+ 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 != len + 1) {
+ dev_err(&client->dev, "i2c write data cmd failed\n");
+ return ret < 0 ? ret : -EIO;
+ }
+
+ 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;
+ 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;
+ ts->reset_pin = pdata->reset_pin;
+ ts->irq_pin = pdata->irq_pin;
+
+ 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, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 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 device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ 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 device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ 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;
+}
+
+static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);
+#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 const 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,
+#ifdef CONFIG_PM
+ .pm = &cy8ctmg110_pm,
+#endif
+ },
+ .id_table = cy8ctmg110_idtable,
+ .probe = cy8ctmg110_probe,
+ .remove = __devexit_p(cy8ctmg110_remove),
+};
+
+module_i2c_driver(cy8ctmg110_driver);
+
+MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>");
+MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/cyp140_ts/Kconfig b/drivers/input/touchscreen/cyp140_ts/Kconfig
new file mode 100755
index 00000000..3a8e1de0
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/Kconfig
@@ -0,0 +1,16 @@
+#
+# CYP140 capacity touch screen driver configuration
+#
+config TOUCHSCREEN_CYP140
+ tristate "CYPRESS CYP140 I2C Capacitive Touchscreen Input Driver Support"
+ depends on ARCH_WMT
+ default m
+ help
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_cyp140
+
diff --git a/drivers/input/touchscreen/cyp140_ts/Makefile b/drivers/input/touchscreen/cyp140_ts/Makefile
new file mode 100755
index 00000000..46229059
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/Makefile
@@ -0,0 +1,33 @@
+#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_cyp140
+
+obj-$(CONFIG_TOUCHSCREEN_CYP140) := $(MY_MODULE_NAME).o
+#obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := cyp140_i2c.o wmt_ts.o cyttsp_fw_upgrade.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
diff --git a/drivers/input/touchscreen/cyp140_ts/cyp140_i2c.c b/drivers/input/touchscreen/cyp140_ts/cyp140_i2c.c
new file mode 100755
index 00000000..ca4717ac
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/cyp140_i2c.c
@@ -0,0 +1,1412 @@
+/* drivers/input/touchscreen/cyp140_i2c.c
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ZEITEC Semiconductor Co., Ltd
+ * Tel: +886-3-579-0045
+ * Fax: +886-3-579-9960
+ * http://www.zeitecsemi.com
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+
+#include "wmt_ts.h"
+#define TP_POINTS_CNT 5
+#define U8 unsigned char
+//fw update.
+//#include "cyp140_fw.h"
+
+//****************************add for cyp140 2013-1-6
+//extern struct tpd_device *tpd;
+static struct i2c_client *i2c_client = NULL;
+static struct task_struct *thread = NULL;
+
+static DECLARE_WAIT_QUEUE_HEAD(waiter);
+
+#define TPD_DEVICE "cyp140"
+static int tpd_load_status = 0;//add !!!2013-1-6
+//static struct early_suspend early_suspend;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static struct early_suspend early_suspend;
+static void tpd_early_suspend(struct early_suspend *handler);
+static void tpd_late_resume(struct early_suspend *handler);
+#endif
+
+static int tilt = 1, rev_x = -1, rev_y = 1;
+static int max_x = 1024, max_y = 600;
+//static int max_x = 800, max_y = 480;
+
+//extern void mt65xx_eint_unmask(unsigned int line);
+//extern void mt65xx_eint_mask(unsigned int line);
+//extern void mt65xx_eint_set_hw_debounce(kal_uint8 eintno, kal_uint32 ms);
+//extern kal_uint32 mt65xx_eint_set_sens(kal_uint8 eintno, kal_bool sens);
+//extern mt65xx_eint_set_polarity(unsigned int eint_num, unsigned int pol);
+//extern void mt65xx_eint_registration(kal_uint8 eintno, kal_bool Dbounce_En,
+ // kal_bool ACT_Polarity, void (EINT_FUNC_PTR)(void),
+ // kal_bool auto_umask);
+
+
+static irqreturn_t tpd_eint_interrupt_handler(int irq, void *dev_id);
+//static int tpd_get_bl_info(int show);
+static int __devinit tpd_probe(struct i2c_client *client);
+//static int tpd_detect(struct i2c_client *client, int kind, struct i2c_board_info *info);
+//static int __devexit tpd_remove(struct i2c_client *client);
+static int touch_event_handler(void *unused);
+//static int tpd_initialize(struct i2c_client * client);
+
+
+volatile static int tpd_flag = 0;//0; debug 2013-5-6
+
+#ifdef TPD_HAVE_BUTTON
+static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
+static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
+#endif
+
+#define TPD_OK 0
+//#define TPD_EREA_Y 799
+//#define TPD_EREA_X 479
+#define TPD_EREA_Y 479
+#define TPD_EREA_X 319
+
+#define TPD_DISTANCE_LIMIT 100
+
+#define TPD_REG_BASE 0x00
+#define TPD_SOFT_RESET_MODE 0x01
+#define TPD_OP_MODE 0x00
+#define TPD_LOW_PWR_MODE 0x04
+#define TPD_SYSINFO_MODE 0x10
+#define GET_HSTMODE(reg) ((reg & 0x70) >> 4) // in op mode or not
+#define GET_BOOTLOADERMODE(reg) ((reg & 0x10) >> 4) // in bl mode
+//#define GPIO_CTP_EN_PIN_M_GPIO 0
+//#define GPIO_CTP_EN_PIN 0xff
+
+static u8 bl_cmd[] = {
+ 0x00, 0x00, 0xFF, 0xA5,
+ 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05,
+ 0x06, 0x07};
+//exit bl mode
+struct tpd_operation_data_t{
+ U8 hst_mode;
+ U8 tt_mode;
+ U8 tt_stat;
+
+ U8 x1_M,x1_L;
+ U8 y1_M,y1_L;
+ U8 x5_M;
+ U8 touch12_id;
+
+ U8 x2_M,x2_L;
+ U8 y2_M,y2_L;
+ U8 x5_L;
+ U8 gest_cnt;
+ U8 gest_id;
+ //U8 gest_set;
+
+
+ U8 x3_M,x3_L;
+ U8 y3_M,y3_L;
+ U8 y5_M;
+ U8 touch34_id;
+
+ U8 x4_M,x4_L;
+ U8 y4_M,y4_L;
+ U8 y5_L;
+
+ //U8 x5_M,x5_L;
+ U8 Undefinei1B;
+ U8 Undefined1C;
+ U8 Undefined1D;
+ U8 GEST_SET;
+ U8 touch5_id;
+};
+
+struct tpd_bootloader_data_t{
+ U8 bl_file;
+ U8 bl_status;
+ U8 bl_error;
+ U8 blver_hi,blver_lo;
+ U8 bld_blver_hi,bld_blver_lo;
+
+ U8 ttspver_hi,ttspver_lo;
+ U8 appid_hi,appid_lo;
+ U8 appver_hi,appver_lo;
+
+ U8 cid_0;
+ U8 cid_1;
+ U8 cid_2;
+
+};
+
+struct tpd_sysinfo_data_t{
+ U8 hst_mode;
+ U8 mfg_cmd;
+ U8 mfg_stat;
+ U8 cid[3];
+ u8 tt_undef1;
+
+ u8 uid[8];
+ U8 bl_verh;
+ U8 bl_verl;
+
+ u8 tts_verh;
+ u8 tts_verl;
+
+ U8 app_idh;
+ U8 app_idl;
+ U8 app_verh;
+ U8 app_verl;
+
+ u8 tt_undef2[6];
+ U8 act_intrvl;
+ U8 tch_tmout;
+ U8 lp_intrvl;
+
+};
+
+struct touch_info {
+ int x[5];
+ int y[5];
+ int p[5];
+ int id[5];
+ int count;
+};
+
+struct id_info{
+ int pid1;
+ int pid2;
+ int reportid1;
+ int reportid2;
+ int id1;
+ int id2;
+
+};
+static struct tpd_operation_data_t g_operation_data;
+//static struct tpd_bootloader_data_t g_bootloader_data;
+//static struct tpd_sysinfo_data_t g_sysinfo_data;
+
+//********************************************************
+
+/* -------------- global variable definition -----------*/
+#define _MACH_MSM_TOUCH_H_
+
+#define ZET_TS_ID_NAME "cyp140-ts"
+
+#define MJ5_TS_NAME "cyp140_touchscreen"
+
+//#define TS_INT_GPIO S3C64XX_GPN(9) /*s3c6410*/
+//#define TS1_INT_GPIO AT91_PIN_PB17 /*AT91SAM9G45 external*/
+//#define TS1_INT_GPIO AT91_PIN_PA27 /*AT91SAM9G45 internal*/
+//#define TS_RST_GPIO S3C64XX_GPN(10)
+
+#define TS_RST_GPIO
+#define TPINFO 1
+#define X_MAX 800 //1024
+#define Y_MAX 480 //576
+#define FINGER_NUMBER 5
+#define KEY_NUMBER 3 //0
+#define P_MAX 1
+#define D_POLLING_TIME 25000
+#define U_POLLING_TIME 25000
+#define S_POLLING_TIME 100
+#define REPORT_POLLING_TIME 5
+
+#define MAX_KEY_NUMBER 8
+#define MAX_FINGER_NUMBER 16
+#define TRUE 1
+#define FALSE 0
+
+//#define debug_mode 1
+//#define DPRINTK(fmt,args...) do { if (debug_mode) printk(KERN_EMERG "[%s][%d] "fmt"\n", __FUNCTION__, __LINE__, ##args);} while(0)
+
+//#define TRANSLATE_ENABLE 1
+#define TOPRIGHT 0
+#define TOPLEFT 1
+#define BOTTOMRIGHT 2
+#define BOTTOMLEFT 3
+#define ORIGIN BOTTOMRIGHT
+
+#define TIME_CHECK_CHARGE 3000
+
+struct msm_ts_platform_data {
+ unsigned int x_max;
+ unsigned int y_max;
+ unsigned int pressure_max;
+};
+
+struct tpd_device{
+ struct i2c_client * client;//i2c_ts;
+ struct work_struct work1;
+ struct input_dev *input;
+ struct timer_list polling_timer;
+ struct delayed_work work; // for polling
+ struct workqueue_struct *queue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ unsigned int gpio; /* GPIO used for interrupt of TS1*/
+ unsigned int irq;
+ unsigned int x_max;
+ unsigned int y_max;
+ unsigned int pressure_max;
+};
+//
+struct tpd_device *tpd;
+
+
+//static int l_suspend = 0; // 1:suspend, 0:normal state
+
+//static int resetCount = 0; //albert++ 20120807
+
+
+//static u16 polling_time = S_POLLING_TIME;
+
+//static int l_powermode = -1;
+//static struct mutex i2c_mutex;
+
+
+//static int __devinit cyp140_ts_probe(struct i2c_client *client, const struct i2c_device_id *id);
+//static int __devexit cyp140_ts_remove(struct i2c_client *dev);
+
+
+
+
+
+//static int filterCount = 0;
+//static u32 filterX[MAX_FINGER_NUMBER][2], filterY[MAX_FINGER_NUMBER][2];
+
+//static u8 key_menu_pressed = 0x1;
+//static u8 key_back_pressed = 0x1;
+//static u8 key_search_pressed = 0x1;
+
+//static u16 ResolutionX=X_MAX;
+//static u16 ResolutionY=Y_MAX;
+//static u16 FingerNum=0;
+//static u16 KeyNum=0;
+//static int bufLength=0;
+//static u8 xyExchange=0;
+//static u16 inChargerMode = 0;
+//static struct i2c_client *this_client;
+struct workqueue_struct *ts_wq = NULL;
+#if 0
+static int l_tskey[4][2] = {
+ {KEY_BACK,0},
+ {KEY_MENU,0},
+ {KEY_HOME,0},
+ {KEY_SEARCH,0},
+};
+#endif
+u8 pc[8];
+// {IC Model, FW Version, FW version,Codebase Type=0x08, Customer ID, Project ID, Config Board No, Config Serial No}
+
+//Touch Screen
+/*static const struct i2c_device_id cyp140_ts_idtable[] = {
+ { ZET_TS_ID_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver cyp140_ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = ZET_TS_ID_NAME,
+ },
+ .probe = cyp140_ts_probe,
+ .remove = __devexit_p(cyp140_ts_remove),
+ .id_table = cyp140_ts_idtable,
+};
+*/
+
+
+/***********************************************************************
+ [function]:
+ callback: Timer Function if there is no interrupt fuction;
+ [parameters]:
+ arg[in]: arguments;
+ [return]:
+ NULL;
+************************************************************************/
+
+
+//extern int wmt_i2c_xfer_continue_if_4(struct i2c_msg *msg, unsigned int num,int bus_id);
+/***********************************************************************
+ [function]:
+ callback: read data by i2c interface;
+ [parameters]:
+ client[in]: struct i2c_client — represent an I2C slave device;
+ data [out]: data buffer to read;
+ length[in]: data length to read;
+ [return]:
+ Returns negative errno, else the number of messages executed;
+************************************************************************/
+int cyp140_i2c_read_tsdata(struct i2c_client *client, u8 *data, u8 length)
+{
+ struct i2c_msg msg;
+ msg.addr = client->addr;
+ msg.flags = I2C_M_RD;
+ msg.len = length;
+ msg.buf = data;
+ return i2c_transfer(client->adapter,&msg, 1);
+
+ /*int rc = 0;
+
+ memset(data, 0, length);
+ rc = i2c_master_recv(client, data, length);
+ if (rc <= 0)
+ {
+ errlog("error!\n");
+ return -EINVAL;
+ } else if (rc != length)
+ {
+ dbg("want:%d,real:%d\n", length, rc);
+ }
+ return rc;*/
+}
+
+/***********************************************************************
+ [function]:
+ callback: write data by i2c interface;
+ [parameters]:
+ client[in]: struct i2c_client — represent an I2C slave device;
+ data [out]: data buffer to write;
+ length[in]: data length to write;
+ [return]:
+ Returns negative errno, else the number of messages executed;
+************************************************************************/
+int cyp140_i2c_write_tsdata(struct i2c_client *client, u8 *data, u8 length)
+{
+ struct i2c_msg msg;
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = length;
+ msg.buf = data;
+ return i2c_transfer(client->adapter,&msg, 1);
+
+ /*int ret = i2c_master_recv(client, data, length);
+ if (ret <= 0)
+ {
+ errlog("error!\n");
+ }
+ return ret;
+ */
+}
+
+/***********************************************************************
+ [function]:
+ callback: coordinate traslating;
+ [parameters]:
+ px[out]: value of X axis;
+ py[out]: value of Y axis;
+ p [in]: pressed of released status of fingers;
+ [return]:
+ NULL;
+************************************************************************/
+void touch_coordinate_traslating(u32 *px, u32 *py, u8 p)
+{
+ int i;
+ u8 pressure;
+
+ #if ORIGIN == TOPRIGHT
+ for(i=0;i<MAX_FINGER_NUMBER;i++){
+ pressure = (p >> (MAX_FINGER_NUMBER-i-1)) & 0x1;
+ if(pressure)
+ {
+ px[i] = X_MAX - px[i];
+ }
+ }
+ #elif ORIGIN == BOTTOMRIGHT
+ for(i=0;i<MAX_FINGER_NUMBER;i++){
+ pressure = (p >> (MAX_FINGER_NUMBER-i-1)) & 0x1;
+ if(pressure)
+ {
+ px[i] = X_MAX - px[i];
+ py[i] = Y_MAX - py[i];
+ }
+ }
+ #elif ORIGIN == BOTTOMLEFT
+ for(i=0;i<MAX_FINGER_NUMBER;i++){
+ pressure = (p >> (MAX_FINGER_NUMBER-i-1)) & 0x1;
+ if(pressure)
+ {
+ py[i] = Y_MAX - py[i];
+ }
+ }
+ #endif
+}
+
+/***********************************************************************
+ [function]:
+ callback: reset function;
+ [parameters]:
+ void;
+ [return]:
+ void;
+************************************************************************/
+void ctp_reset(void)
+{
+#if defined(TS_RST_GPIO)
+ //reset mcu
+ /* gpio_direction_output(TS_RST_GPIO, 1);
+ msleep(1);
+ gpio_direction_output(TS_RST_GPIO, 0);
+ msleep(10);
+ gpio_direction_output(TS_RST_GPIO, 1);
+ msleep(20);*/
+ wmt_rst_output(1);
+ msleep(1);
+ wmt_rst_output(0);
+ msleep(10);
+ wmt_rst_output(1);
+ msleep(20);
+ dbg("has done\n");
+#else
+ u8 ts_reset_cmd[1] = {0xb0};
+ cyp140_i2c_write_tsdata(this_client, ts_reset_cmd, 1);
+#endif
+
+}
+
+//*************************************************
+#if 1
+#include <linux/sched.h> //wake_up_process()
+#include <linux/kthread.h> //kthread_create()ã€kthread_run()
+//#include <err.h> //IS_ERR()ã€PTR_ERR()
+
+void cyttsp_sw_reset(void);
+//static struct task_struct *esd_task;
+volatile bool need_rst_flag = 0;
+volatile int tp_interrupt_flag = 0;
+volatile int tp_suspend_flag = 0;
+volatile int tp_reseting_flag = 0;
+
+void cyttsp_print_reg(struct i2c_client *client)
+{
+#if 1
+ char buffer[20];
+ int status=0;
+ int i;
+
+ status = i2c_smbus_read_i2c_block_data(i2c_client, 0x00, 16, &(buffer[0]));
+
+ printk("++++cyttsp_print_reg=%d: ",status);
+ for(i = 0; i<16;i++)
+ printk(" %02x", buffer[i]);
+ printk("\n");
+#endif
+
+}
+
+int exit_boot_mode(void)
+{
+
+ //int retval = TPD_OK;
+
+ char buffer[2];
+ int status=0;
+ status = i2c_smbus_read_i2c_block_data(i2c_client, 0x01, 1, &(buffer[0]));
+ if(status<0) {
+ printk ("++++exit_boot_mode failed---1\n");
+ return status;
+ }
+ else
+ {
+ if(buffer[0] & 0x10)
+ {
+ status = i2c_master_send(i2c_client, bl_cmd, 12);
+ if( status < 0)
+ {
+ printk ("++++exit_boot_mode failed---2\n");
+ return status;
+ }
+ else
+ {
+ //printk("++++exit_boot_mode ok\n");
+ }
+ msleep(300);
+ status = i2c_smbus_read_i2c_block_data(i2c_client, 0x01, 1, &(buffer[0]));
+ if(status<0) {
+ printk ("++++exit_boot_mode set failed\n");
+ return status;
+ }
+// printk("++++exit_boot_mode set: 0x%x\n",buffer[0]);
+ cyttsp_print_reg(i2c_client);
+ }
+ else
+ {
+ // printk("++++exit_boot_mode-- not in bootmode\n");
+ }
+
+ }
+ return 0;
+
+}
+
+void esd_check(void)
+{
+ if(need_rst_flag)
+ {
+ if(tp_suspend_flag == 0)
+ {
+ printk("++++esd_check---rst\n");
+ //mt65xx_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+ tp_reseting_flag = 1;
+ cyttsp_sw_reset();
+ tp_reseting_flag = 0;
+ //mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+ }
+ need_rst_flag = 0;
+ }
+}
+static int fp_count = 0;
+#if 0 //close 2013-1-6
+void esd_thread(void)
+{
+ static int i = 0, j = 0;
+ while(1)
+ {
+ printk("++++esd_thread, need_rst_flag=%d, fp_count=%d\n", need_rst_flag,fp_count);
+ fp_count = 0;
+ if(need_rst_flag)
+ {
+ j = 0;
+ while(tp_interrupt_flag==1 && j<200) //wujinyou
+ {
+ j ++;
+ if(tp_suspend_flag)
+ msleep(1000);
+ else
+ msleep(10);
+ }
+ if(tp_suspend_flag == 0)
+ {
+ printk("++++esd_thread, start reset, mask int\n");
+ //mt65xx_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+ tp_reseting_flag = 1;
+ cyttsp_sw_reset();
+ i = 0;
+ need_rst_flag = 0;
+ tp_reseting_flag = 0;
+ //mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+ }
+ }
+ msleep(1000);
+ i ++;
+ if(i == 10)
+ {
+ i = 0;
+ //cyttsp_sw_reset();
+ //need_rst_flag = 1;
+ }
+ }
+}
+static int esd_init_thread(void)
+{
+ int err;
+ printk("++++%s, line %d----\n", __FUNCTION__, __LINE__);
+
+ esd_task = kthread_create(esd_thread, NULL, "esd_task");
+
+ if(IS_ERR(esd_task)){
+ printk("++++Unable to start kernel thread.\n");
+ err = PTR_ERR(esd_task);
+ esd_task = NULL;
+ return err;
+ }
+
+ wake_up_process(esd_task);
+
+ return 0;
+
+}
+#endif //close 2013-1-6
+
+#endif
+
+static void tpd_down(int x, int y, int p) {
+
+
+ //printk("<<<<<<x,y (%d, %d)\n", x, y);//debug 2013-5-6
+
+//printk("++++tpd_down: %d,%d,%d\n", x, y, p);
+#if 0 //def TPD_HAVE_BUTTON
+ if (boot_mode != NORMAL_BOOT) {
+ if(y > 480) {
+ tpd_button(x, y, 1);
+ }
+ }
+#endif
+ //*****here process x y coord and then report!!!! 2013-1-7
+
+#if 1//0
+ int tmp;
+ if (tilt)
+ {
+ tmp = x;
+ x = y;
+ y =tmp;
+ }
+ if (rev_x < 0)
+ x = max_x -x;
+ if (rev_y < 0)
+ y = max_y -y;
+
+
+#endif
+ if (wmt_ts_get_lcdexchg()) {
+ int t;
+ t = x;
+ x = y;
+ y = max_x - t;
+ }
+
+ //printk("<<<<<< transfer x,y (%d, %d)\n", x, y);//debug 2013-5-6
+
+ input_report_abs(tpd->input, ABS_PRESSURE,p);
+ input_report_key(tpd->input, BTN_TOUCH, 1);
+ //input_report_abs(tpd->input,ABS_MT_TRACKING_ID,i);
+ input_report_abs(tpd->input, ABS_MT_TOUCH_MAJOR, 1);
+ input_report_abs(tpd->input, ABS_MT_POSITION_X, x);
+ input_report_abs(tpd->input, ABS_MT_POSITION_Y, y);
+ ////TPD_DEBUG("Down x:%4d, y:%4d, p:%4d \n ", x, y, p);
+ input_mt_sync(tpd->input);
+ //TPD_DOWN_DEBUG_TRACK(x,y);
+ fp_count ++;
+}
+
+static void tpd_up(int x, int y,int p) {
+
+ input_report_abs(tpd->input, ABS_PRESSURE, 0);
+ input_report_key(tpd->input, BTN_TOUCH, 0);
+ // input_report_abs(tpd->input,ABS_MT_TRACKING_ID,i);
+ input_report_abs(tpd->input, ABS_MT_TOUCH_MAJOR, 0);
+ //input_report_abs(tpd->input, ABS_MT_POSITION_X, x);
+ //input_report_abs(tpd->input, ABS_MT_POSITION_Y, y); //!!!!
+ //TPD_DEBUG("Up x:%4d, y:%4d, p:%4d \n", x, y, 0);
+ input_mt_sync(tpd->input);
+ // TPD_UP_DEBUG_TRACK(x,y);
+}
+void test_retval(s32 ret)
+{
+#if 1
+ if(ret<0)
+ {
+ need_rst_flag = 1;
+ printk("++++test_retval=1-------\n");
+ }
+#endif
+}
+static int tpd_touchinfo(struct touch_info *cinfo, struct touch_info *pinfo)
+{
+
+ s32 retval;
+ static u8 tt_mode;
+ //pinfo->count = cinfo->count;
+ u8 data0,data1;
+
+ memcpy(pinfo, cinfo, sizeof(struct touch_info));
+ memset(cinfo, 0, sizeof(struct touch_info));
+// printk("pinfo->count =%d\n",pinfo->count);
+
+ retval = i2c_smbus_read_i2c_block_data(i2c_client, TPD_REG_BASE, 8, (u8 *)&g_operation_data);
+ retval += i2c_smbus_read_i2c_block_data(i2c_client, TPD_REG_BASE + 8, 8, (((u8 *)(&g_operation_data)) + 8));
+ retval += i2c_smbus_read_i2c_block_data(i2c_client, TPD_REG_BASE + 16, 8, (((u8 *)(&g_operation_data)) + 16));
+ retval += i2c_smbus_read_i2c_block_data(i2c_client, TPD_REG_BASE + 24, 8, (((u8 *)(&g_operation_data)) + 24));
+
+
+ //cyttsp_print_reg(i2c_client);
+ ////TPD_DEBUG("received raw data from touch panel as following:\n");
+
+ /*("hst_mode = %02X, tt_mode = %02X, tt_stat = %02X\n", \
+ g_operation_data.hst_mode,\
+ g_operation_data.tt_mode,\
+ g_operation_data.tt_stat); */
+
+ cinfo->count = (g_operation_data.tt_stat & 0x0f) ; //point count
+
+ //TPD_DEBUG("cinfo->count =%d\n",cinfo->count);
+
+ //TPD_DEBUG("Procss raw data...\n");
+
+ cinfo->x[0] = (( g_operation_data.x1_M << 8) | ( g_operation_data.x1_L)); //point 1
+ cinfo->y[0] = (( g_operation_data.y1_M << 8) | ( g_operation_data.y1_L));
+ cinfo->p[0] = 0;//g_operation_data.z1;
+
+ //printk("Before: cinfo->x0 = %3d, cinfo->y0 = %3d, cinfo->p0 = %3d cinfo->id0 = %3d\n", cinfo->x[0] ,cinfo->y[0] ,cinfo->p[0], cinfo->id[0]);
+ if(cinfo->x[0] < 1) cinfo->x[0] = 1;
+ if(cinfo->y[0] < 1) cinfo->y[0] = 1;
+ cinfo->id[0] = ((g_operation_data.touch12_id & 0xf0) >>4) -1;
+ //printk("After: cinfo->x0 = %3d, cinfo->y0 = %3d, cinfo->p0 = %3d cinfo->id0 = %3d\n", cinfo->x[0] ,cinfo->y[0] ,cinfo->p[0], cinfo->id[0]);
+
+ if(cinfo->count >1)
+ {
+ cinfo->x[1] = (( g_operation_data.x2_M << 8) | ( g_operation_data.x2_L)); //point 2
+ cinfo->y[1] = (( g_operation_data.y2_M << 8) | ( g_operation_data.y2_L));
+ cinfo->p[1] = 0;//g_operation_data.z2;
+
+ //printk("before: cinfo->x2 = %3d, cinfo->y2 = %3d, cinfo->p2 = %3d\n", cinfo->x2, cinfo->y2, cinfo->p2);
+ if(cinfo->x[1] < 1) cinfo->x[1] = 1;
+ if(cinfo->y[1] < 1) cinfo->y[1] = 1;
+ cinfo->id[1] = ((g_operation_data.touch12_id & 0x0f)) -1;
+ //printk("After: cinfo->x[1] = %3d, cinfo->y[1] = %3d, cinfo->p[1] = %3d, cinfo->id[1] = %3d\n", cinfo->x[1], cinfo->y[1], cinfo->p[1], cinfo->id[1]);
+
+ if (cinfo->count > 2)
+ {
+ cinfo->x[2]= (( g_operation_data.x3_M << 8) | ( g_operation_data.x3_L)); //point 3
+ cinfo->y[2] = (( g_operation_data.y3_M << 8) | ( g_operation_data.y3_L));
+ cinfo->p[2] = 0;//g_operation_data.z3;
+ cinfo->id[2] = ((g_operation_data.touch34_id & 0xf0) >> 4) -1;
+
+ //printk("before: cinfo->x[2] = %3d, cinfo->y[2] = %3d, cinfo->p[2] = %3d\n", cinfo->x[2], cinfo->y[2], cinfo->p[2]);
+ if(cinfo->x[2] < 1) cinfo->x[2] = 1;
+ if(cinfo->y[2]< 1) cinfo->y[2] = 1;
+ //printk("After: cinfo->x[2]= %3d, cinfo->y[2] = %3d, cinfo->p[2]= %3d, cinfo->id[2] = %3d\n", cinfo->x[2], cinfo->y[2], cinfo->p[2], cinfo->id[2]);
+
+ if (cinfo->count > 3)
+ {
+ cinfo->x[3] = (( g_operation_data.x4_M << 8) | ( g_operation_data.x4_L)); //point 3
+ cinfo->y[3] = (( g_operation_data.y4_M << 8) | ( g_operation_data.y4_L));
+ cinfo->p[3] = 0;//g_operation_data.z4;
+ cinfo->id[3] = ((g_operation_data.touch34_id & 0x0f)) -1;
+
+ //printk("before: cinfo->x[3] = %3d, cinfo->y[3] = %3d, cinfo->p[3] = %3d, cinfo->id[3] = %3d\n", cinfo->x[3], cinfo->y[3], cinfo->p[3], cinfo->id[3]);
+ //printk("before: x4_M = %3d, x4_L = %3d\n", g_operation_data.x4_M, g_operation_data.x4_L);
+ if(cinfo->x[3] < 1) cinfo->x[3] = 1;
+ if(cinfo->y[3] < 1) cinfo->y[3] = 1;
+ //printk("After: cinfo->x[3] = %3d, cinfo->y[3] = %3d, cinfo->p[3]= %3d, cinfo->id[3] = %3d\n", cinfo->x[3], cinfo->y[3], cinfo->p[3], cinfo->id[3]);
+ }
+ if (cinfo->count > 4)
+ {
+ cinfo->x[4] = (( g_operation_data.x5_M << 8) | ( g_operation_data.x5_L)); //point 3
+ cinfo->y[4] = (( g_operation_data.y5_M << 8) | ( g_operation_data.y5_L));
+ cinfo->p[4] = 0;//g_operation_data.z4;
+ cinfo->id[4] = ((g_operation_data.touch5_id & 0xf0) >> 4) -1;
+
+ //printk("before: cinfo->x[4] = %3d, cinfo->y[4] = %3d, cinfo->id[4] = %3d\n", cinfo->x[4], cinfo->y[4], cinfo->id[4]);
+ //printk("before: x5_M = %3d, x5_L = %3d\n", g_operation_data.x5_M, g_operation_data.x5_L);
+ if(cinfo->x[4] < 1) cinfo->x[4] = 1;
+ if(cinfo->y[4] < 1) cinfo->y[4] = 1;
+ //printk("After: cinfo->x[4] = %3d, cinfo->y[4] = %3d, cinfo->id[4] = %3d\n", cinfo->x[4], cinfo->y[4], cinfo->id[4]);
+ }
+ }
+
+ }
+
+ if (!cinfo->count) return true; // this is a touch-up event
+
+ if (g_operation_data.tt_mode & 0x20) {
+ //TPD_DEBUG("uffer is not ready for use!\n");
+ memcpy(cinfo, pinfo, sizeof(struct touch_info));
+ return false;
+ }//return false; // buffer is not ready for use// buffer is not ready for use
+
+ // data toggle
+
+ data0 = i2c_smbus_read_i2c_block_data(i2c_client, TPD_REG_BASE, 1, (u8*)&g_operation_data);
+ ////TPD_DEBUG("before hst_mode = %02X \n", g_operation_data.hst_mode);
+
+ if((g_operation_data.hst_mode & 0x80)==0)
+ g_operation_data.hst_mode = g_operation_data.hst_mode|0x80;
+ else
+ g_operation_data.hst_mode = g_operation_data.hst_mode & (~0x80);
+
+ ////TPD_DEBUG("after hst_mode = %02X \n", g_operation_data.hst_mode);
+ data1 = i2c_smbus_write_i2c_block_data(i2c_client, TPD_REG_BASE, sizeof(g_operation_data.hst_mode), &g_operation_data.hst_mode);
+
+
+ if (tt_mode == g_operation_data.tt_mode) {
+ //TPD_DEBUG("sampling not completed!\n");
+ memcpy(cinfo, pinfo, sizeof(struct touch_info));
+ return false;
+ }// sampling not completed
+ else
+ tt_mode = g_operation_data.tt_mode;
+
+ return true;
+
+};
+
+static int touch_event_handler(void *unused)
+{
+ int i,j;
+ int keeppoint[5];
+ struct touch_info cinfo, pinfo;
+ struct sched_param param = { .sched_priority = 70/*RTPM_PRIO_TPD*/ };
+ sched_setscheduler(current, SCHED_RR, &param);
+
+ do
+ {
+ //printk("++++%s, line %d----unmask int\n", __FUNCTION__, __LINE__);
+ // mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+ wmt_enable_gpirq();
+ set_current_state(TASK_INTERRUPTIBLE);
+ tp_interrupt_flag = 0;
+ //printk("++++%s, line %d----end\n", __FUNCTION__, __LINE__);
+ wait_event_interruptible(waiter,tpd_flag!=0);
+// printk("++++%s, line %d----start\n", __FUNCTION__, __LINE__);
+
+ tpd_flag = 0; //debg 2013-5-6
+ set_current_state(TASK_RUNNING);
+
+ exit_boot_mode();
+ if (tpd_touchinfo(&cinfo, &pinfo))
+ {
+ memset(keeppoint, 0x0, sizeof(keeppoint));
+ if(cinfo.count >0 && cinfo.count < (TP_POINTS_CNT+1))
+ {
+ switch(cinfo.count)
+ {
+ case 5:
+ {
+ tpd_down(cinfo.x[4], cinfo.y[4], cinfo.p[4]);
+ }
+ case 4:
+ {
+ tpd_down(cinfo.x[3], cinfo.y[3], cinfo.p[3]);
+ }
+ case 3:
+ {
+ tpd_down(cinfo.x[2], cinfo.y[2], cinfo.p[2]);
+ }
+ case 2:
+ {
+ tpd_down(cinfo.x[1], cinfo.y[1], cinfo.p[1]);
+ }
+ case 1:
+ {
+ tpd_down(cinfo.x[0], cinfo.y[0], cinfo.p[0]);
+ }
+ default:
+ break;
+ }
+ for(i = 0; i < cinfo.count; i++)
+ for(j = 0; j < pinfo.count; j++)
+ {
+ if(cinfo.id[i] == pinfo.id[j])keeppoint[j] = 1;
+ else if(keeppoint[j] != 1)keeppoint[j] = 0;
+ }
+
+ for(j = 0; j < pinfo.count; j++)
+ {
+ if(keeppoint[j] != 1)
+ {
+ tpd_up(pinfo.x[j], pinfo.y[j], pinfo.p[j]);
+ }
+ }
+
+ }
+ else if(cinfo.count == 0 && pinfo.count !=0)
+ {
+ switch(pinfo.count )
+ {
+ case 5:
+ {
+ tpd_up(pinfo.x[4], pinfo.y[4], pinfo.p[4]);
+ }
+ case 4:
+ {
+ tpd_up(pinfo.x[3], pinfo.y[3], pinfo.p[3]);
+ }
+ case 3:
+ {
+ tpd_up(pinfo.x[2], pinfo.y[2], pinfo.p[2]);
+ }
+ case 2:
+ {
+ tpd_up(pinfo.x[1], pinfo.y[1], pinfo.p[1]);
+ }
+ case 1:
+ {
+ tpd_up(pinfo.x[0], pinfo.y[0], pinfo.p[0]);
+ }
+ default:
+ break;
+ }
+ }
+
+ input_sync(tpd->input);
+
+ }
+
+
+
+ }while(!kthread_should_stop());
+ tp_interrupt_flag = 0;
+
+ return 0;
+}
+
+
+
+
+static irqreturn_t tpd_eint_interrupt_handler(int irq, void *dev_id)
+{
+ static int i = 0;
+ i ++;
+ //printk("++++eint=%d\n",i);
+
+ //mt65xx_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+// printk("++++%s, line %d, tpd_flag=%d,i=%d\n", __FUNCTION__, __LINE__, tpd_flag,i);
+
+ if (wmt_is_tsint())
+ {
+ // printk("<<<<in %s\n", __FUNCTION__);
+ wmt_clr_int();
+ //return IRQ_HANDLED;//!!!!!
+ if (wmt_is_tsirq_enable())
+ {
+ wmt_disable_gpirq();
+ }
+ tp_interrupt_flag = 1;
+ ////TPD_DEBUG("TPD interrupt has been triggered\n");
+ //if(tpd_flag)
+ //return;
+ tpd_flag = 1;
+ wake_up_interruptible(&waiter);
+
+ return IRQ_HANDLED;
+
+ }
+ return IRQ_NONE;
+}
+static void ctp_power_on(int on)
+{
+ printk("++++ctp_power_on = %d\n",on);
+ //return ;
+
+ if(on == 1)
+ {
+ //mt_set_gpio_mode(GPIO_CTP_EN_PIN, GPIO_CTP_EN_PIN_M_GPIO);
+ //mt_set_gpio_dir(GPIO_CTP_EN_PIN, GPIO_DIR_OUT);
+ //mt_set_gpio_out(GPIO_CTP_EN_PIN, GPIO_OUT_ONE);
+ ;
+
+ }
+ else
+ {
+ //return -EIO;
+ ;
+ }
+}
+//}
+
+#include "cyttsp.h"
+extern void cyttsp_fw_upgrade(void);
+void cyttsp_hw_reset(void)
+{
+ ctp_power_on(0); //wujinyou
+ msleep(200);
+
+ ctp_power_on(1); //wujinyou
+ msleep(100);
+ //mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
+// mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
+ //mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
+ msleep(100);
+ //mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
+ msleep(100);
+}
+void cyttsp_sw_reset(void)
+{
+ //int retval = TPD_OK;
+// int status = 0;
+ printk("++++cyttsp_sw_reset---------start\n");
+#if 0//1
+ mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
+ mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
+ mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
+ msleep(20);
+ ctp_power_on(0);
+ msleep(200);
+ #if 1
+ ctp_power_on(1);
+ #endif
+
+ msleep(20);
+ mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
+ msleep(100);
+
+ ////TPD_DEBUG("TPD wake up\n");
+ status = i2c_master_send(i2c_client, bl_cmd, 12);
+ if( status < 0)
+ {
+ printk("++++ [cyttsp_sw_reset], cyttsp tpd exit bootloader mode failed--tpd_resume!\n");
+ return status;
+ }
+ msleep(300);
+ //exit_boot_mode();
+ //mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#endif
+ printk("++++cyttsp_sw_reset---------end\n");
+ //return retval;
+}
+
+
+//***************cyp140 probe 2013-1-6
+// wmtenv set wmt.io.touch 1:cyp140:7:600:1024:4:0:1:-1:5 //ok 2013-5-8
+static int __devinit tpd_probe(struct i2c_client *client)
+{
+ struct input_dev *input_dev;
+ int retval = TPD_OK;
+ int gpio_irq = wmt_ts_get_irqgpnum();
+ int gpio_rst = wmt_ts_get_resetgpnum();
+ //int result;
+ i2c_client = client;
+
+ retval = gpio_request(gpio_irq, "ts_irq");
+ if (retval < 0) {
+ printk("gpio(%d) touchscreen irq request fail\n", gpio_irq);
+ return retval;
+ }
+
+ retval = gpio_request(gpio_rst, "ts_rst");
+ if (retval < 0) {
+ printk("gpio(%d) touchscreen reset request fail\n", gpio_rst);
+ goto Fail_request_rstgpio;
+ }
+ //char buffer[2];
+ //int status=0;
+
+ //int res_x, res_y;
+
+ printk("<<< enter %s: %d\n",__FUNCTION__, __LINE__);
+
+#if 1 //0
+ tilt = wmt_ts_get_xaxis();
+ rev_x = wmt_ts_get_xdir();
+ rev_y = wmt_ts_get_ydir();
+#if 0
+ if (tilt){
+ max_y = wmt_ts_get_resolvX();
+ max_x = wmt_ts_get_resolvY();
+ }
+ else
+ {
+ max_x = wmt_ts_get_resolvX();
+ max_y =wmt_ts_get_resolvY();
+ }
+#else
+ max_x = wmt_ts_get_resolvX();
+ max_y =wmt_ts_get_resolvY();
+#endif
+
+#endif
+#if 0
+ if (0)
+ {
+ res_x = max_y;
+ res_y = max_x;
+ }
+ else
+ {
+ res_x = max_x;
+ res_y = max_y;
+ }
+ max_x = res_x;
+ max_y = res_y;
+#endif
+ //************************add input device 2013-1-6
+ tpd = kzalloc(sizeof(struct tpd_device), GFP_KERNEL);
+
+ input_dev = input_allocate_device();
+ if (!input_dev || !tpd) {
+ return -ENOMEM;
+ }
+
+ tpd->client/*i2c_ts*/ = client;
+ i2c_set_clientdata(client, tpd);
+ tpd->input = input_dev;
+
+ input_dev->name = "touch_cyp140"; //MJ5_TS_NAME;
+ input_dev->phys = "cyp140_touch/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0002;
+ input_dev->id.version = 0x0100;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ if (wmt_ts_get_lcdexchg()) {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, max_y/*480*//*600*//*ResolutionX*//*ResolutionX*/, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, max_x /*ResolutionY*//*800*//* 1024*/, 0, 0);
+ } else {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, max_x/*480*//*600*//*ResolutionX*//*ResolutionX*/, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, max_y /*ResolutionY*//*800*//* 1024*/, 0, 0);
+ }
+
+ set_bit(KEY_BACK, input_dev->keybit);
+ set_bit(KEY_HOME, input_dev->keybit);
+ set_bit(KEY_MENU, input_dev->keybit);
+ retval = input_register_device(input_dev);
+ if (retval)
+ {
+ printk("%s input register device error!!\n", __FUNCTION__);
+ goto E_REG_INPUT;
+ }
+ //****************************
+ //ctp_power_on(1); //wujinyou //!!!!2013-1-6
+
+ msleep(1000);
+
+ //printk("<<<<here ??/\n");
+ //************************add for wmt 2013-1-6
+ wmt_tsreset_init();
+ wmt_set_rst_pull(1);
+ //wmt_enable_rst_pull(1);
+ wmt_rst_output(1);
+
+ wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);
+ //wmt_set_gpirq(IRQ_TYPE_EDGE_RISING); //debug 2013-5-8 also no interrupt
+ wmt_disable_gpirq();
+
+ tpd->irq = wmt_get_tsirqnum();
+ retval = request_irq(tpd->irq, tpd_eint_interrupt_handler,IRQF_SHARED, "cypcm", tpd);
+ //****************************************
+
+ printk("tpd_probe request_irq retval=%d!\n",retval);
+ msleep(100);
+ msleep(1000);
+
+ cust_ts.client = i2c_client;
+// cyttsp_fw_upgrade(); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+#if 0 //by linda 20130126
+ status = i2c_smbus_read_i2c_block_data(i2c_client, 0x01, 1, &(buffer[0]));
+ printk("tpd_probe request_irq status=%d!\n",status);
+
+ retval = i2c_master_send(i2c_client, bl_cmd, 12);
+ if( retval < 0)
+ {
+ printk("tpd_probe i2c_master_send retval=%d!\n",retval);
+
+ //return retval;
+ goto I2C_ERR;
+ }
+#else
+ retval = exit_boot_mode();
+ if (retval)
+ {
+ printk("%s exit_boot_mod error!\n", __FUNCTION__);
+ goto I2C_ERR;
+ }
+
+#endif
+/*
+ msleep(1000);
+ retval = i2c_smbus_read_i2c_block_data(i2c_client, 0x00, 1, &(buffer[0]));
+ if(retval<0) {
+ retval = i2c_smbus_read_i2c_block_data(i2c_client, 0x00, 1, &(buffer[0]));
+ if(retval<0) {
+ printk("error read !%d\n", __LINE__);
+
+ goto I2C_ERR;
+ }
+ }
+*/
+ //TPD_DEBUG("[mtk-tpd], cyttsp tpd_i2c_probe success!!\n");
+ tpd_load_status = 1;
+ thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);
+ if (IS_ERR(thread)) {
+ retval = PTR_ERR(thread);
+ return retval;
+
+ }
+
+
+ msleep(100);
+ printk("++++tpd_probe,retval=%d\n", retval);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ tpd->early_suspend.suspend = tpd_early_suspend,
+ tpd->early_suspend.resume = tpd_late_resume,
+ tpd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;//,EARLY_SUSPEND_LEVEL_DISABLE_FB + 2;
+ register_early_suspend(&tpd->early_suspend);
+#endif
+ //disable_irq(cyp140_ts->irq);
+
+ wmt_enable_gpirq();
+
+//cust_timer_init();
+// esd_init_thread(); //close it 2013-1-6
+ return 0; //retval;
+ I2C_ERR:
+ free_irq(tpd->irq, tpd);
+ input_unregister_device(input_dev); //
+ E_REG_INPUT:
+ input_free_device(input_dev);
+ kfree(tpd);
+ //return retval;
+ Fail_request_rstgpio:
+ gpio_free(gpio_rst);
+ gpio_free(gpio_irq);
+ return retval;
+
+}
+//*******************************
+
+//module_init(cyp140_ts_init);
+static int tpd_local_init(void)
+{
+ if (tpd_probe(ts_get_i2c_client()))// ????
+ {
+ return -1;
+ }
+
+
+ if(tpd_load_status == 0){
+ //return -1;
+ ;
+ }
+
+#ifdef TPD_HAVE_BUTTON
+ tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);// initialize tpd button data
+ boot_mode = get_boot_mode();
+#endif
+ return 0;//!!!!2013-1-7
+}
+
+//**********************suspend & resume
+static int tpd_resume(/*struct i2c_client *client*/struct platform_device *pdev)
+{
+ int retval = TPD_OK;
+ int status = 0;
+ printk("++++%s, line %d----end\n", __FUNCTION__, __LINE__);
+ //mt65xx_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+ msleep(100);
+ #if 1
+ ctp_power_on(1);
+ #endif
+
+ msleep(1);
+ wmt_rst_output(0);
+ msleep(1);
+ wmt_rst_output(1);
+ msleep(100);
+
+ wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);//sometimes gpio7 will in low after resume 2013-5-9
+ wmt_enable_gpirq();
+ printk("++++%s, line %d----end\n", __FUNCTION__, __LINE__);
+
+ //TPD_DEBUG("TPD wake up\n");
+
+ #if 0 //0 // by linda 20120126 change rambo 2013-5-6
+ status = i2c_master_send(i2c_client, bl_cmd, 12);
+ #else
+ exit_boot_mode();
+ #endif
+ printk("++++%s, line %d----end\n", __FUNCTION__, __LINE__);
+
+ if( status < 0)
+ {
+ printk("++++ [mtk-tpd], cyttsp tpd exit bootloader mode failed--tpd_resume!\n");
+ return status;
+ }
+ //exit_boot_mode();
+ printk("++++%s, line %d----end\n", __FUNCTION__, __LINE__);
+ msleep(300);
+ //wmt_enable_gpirq(); //debg 2013-5-6
+ tp_suspend_flag = 0;
+ return retval;
+}
+
+static int tpd_suspend(/*struct i2c_client *client*/struct platform_device *pdev, pm_message_t message)
+{
+ int i = 0;
+ int retval = TPD_OK;
+ //u8 sleep_mode = 0x02; // 0x02--CY_DEEP_SLEEP_MODE, 0x04--CY_LOW_PWR_MODE
+ //TPD_DEBUG("TPD enter sleep\n");
+ //u8 sleep_reg[2] = {0, 2};
+ printk("++++%s, line %d----end\n", __FUNCTION__, __LINE__);
+ wmt_disable_gpirq();
+ //wmt_disable_gpirq(); //dbg 2013-5-6
+
+ while((tp_reseting_flag || tp_interrupt_flag) && i<30)
+ {
+ i ++;
+ msleep(100);
+ }
+ tp_suspend_flag = 1;
+#if 1
+ //retval = i2c_smbus_write_i2c_block_data(i2c_client,0x00,sizeof(sleep_mode), &sleep_mode);
+ //retval = i2c_master_send(i2c_client, sleep_reg, 2); //send cmd error -5!
+ msleep(1);
+ ctp_power_on(0);
+ mdelay(1);
+#else
+ mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
+ mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
+ mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
+#endif
+
+ return retval;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void tpd_early_suspend(struct early_suspend *handler)
+{
+ tpd_suspend(i2c_client, PMSG_SUSPEND);
+}
+
+static void tpd_late_resume(struct early_suspend *handler)
+{
+ tpd_resume(i2c_client);
+}
+#endif
+//****************************
+
+
+static void cyp140_ts_exit(void)
+{
+ printk("<<<%s\n", __FUNCTION__);
+
+ wmt_disable_gpirq();
+ free_irq(tpd->irq, tpd);
+ //kthread_stop(thread); // halt rmmod??
+ input_unregister_device(tpd->input); //
+
+ input_free_device(tpd->input);
+ kfree(tpd);
+ gpio_free(wmt_ts_get_irqgpnum());
+ gpio_free(wmt_ts_get_resetgpnum());
+}
+//module_exit(cyp140_ts_exit);
+
+void cyp140_set_ts_mode(u8 mode)
+{
+ dbg( "[Touch Screen]ts mode = %d \n", mode);
+}
+//EXPORT_SYMBOL_GPL(cyp140_set_ts_mode);
+
+struct wmtts_device cyp140_tsdev = {
+ .driver_name = WMT_TS_I2C_NAME,
+ .ts_id = "cyp140",
+ .init = tpd_local_init,
+ .exit = cyp140_ts_exit,
+ .suspend = tpd_suspend,
+ .resume = tpd_resume,
+};
+
+
+
+MODULE_DESCRIPTION("cyp140 I2C Touch Screen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/cyp140_ts/cyttsp.h b/drivers/input/touchscreen/cyp140_ts/cyttsp.h
new file mode 100755
index 00000000..6020018f
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/cyttsp.h
@@ -0,0 +1,696 @@
+/* Header file for:
+ * Cypress TrueTouch(TM) Standard Product touchscreen drivers.
+ * include/linux/cyttsp.h
+ *
+ * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Cypress reserves the right to make changes without further notice
+ * to the materials described herein. Cypress does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com
+ *
+ */
+
+
+#ifndef __CYTTSP_H__
+#define __CYTTSP_H__
+
+#include <linux/input.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <asm/mach-types.h>
+
+#define CYPRESS_TTSP_NAME "cyttsp"
+#define CY_I2C_NAME "cyttsp-i2c"
+#define CY_SPI_NAME "cyttsp-spi"
+
+#ifdef CY_DECLARE_GLOBALS
+ uint32_t cyttsp_tsdebug;
+ module_param_named(tsdebug, cyttsp_tsdebug, uint, 0664);
+ uint32_t cyttsp_tsxdebug;
+ module_param_named(tsxdebug, cyttsp_tsxdebug, uint, 0664);
+
+ uint32_t cyttsp_disable_touch;
+ module_param_named(disable_touch, cyttsp_disable_touch, uint, 0664);
+#else
+ extern uint32_t cyttsp_tsdebug;
+ extern uint32_t cyttsp_tsxdebug;
+ extern uint32_t cyttsp_disable_touch;
+#endif
+
+
+
+/******************************************************************************
+ * Global Control, Used to control the behavior of the driver
+ */
+
+/* defines for Gen2 (Txx2xx); Gen3 (Txx3xx)
+ * use these defines to set cyttsp_platform_data.gen in board config file
+ */
+#define CY_GEN2 2
+#define CY_GEN3 3
+
+/* define for using I2C driver
+ */
+#define CY_USE_I2C_DRIVER
+
+/* defines for using SPI driver */
+/*
+#define CY_USE_SPI_DRIVER
+ */
+#define CY_SPI_DFLT_SPEED_HZ 1000000
+#define CY_SPI_MAX_SPEED_HZ 4000000
+#define CY_SPI_SPEED_HZ CY_SPI_DFLT_SPEED_HZ
+#define CY_SPI_BITS_PER_WORD 8
+#define CY_SPI_DAV 139 /* set correct gpio id */
+#define CY_SPI_BUFSIZE 512
+
+/* Voltage and Current ratings */
+#define CY_TMA300_VTG_MAX_UV 5500000
+#define CY_TMA300_VTG_MIN_UV 1710000
+#define CY_TMA300_CURR_24HZ_UA 17500
+#define CY_I2C_VTG_MAX_UV 1800000
+#define CY_I2C_VTG_MIN_UV 1800000
+#define CY_I2C_CURR_UA 9630
+
+
+/* define for inclusion of TTSP App Update Load File
+ * use this define if update to the TTSP Device is desired
+ */
+/*
+#define CY_INCLUDE_LOAD_FILE
+*/
+
+/* define if force new load file for bootloader load */
+/*
+#define CY_FORCE_FW_UPDATE
+*/
+
+/* undef for production use */
+/*
+#define CY_USE_DEBUG
+*/
+
+/* undef for irq use; use this define in the board configuration file */
+/*
+#define CY_USE_TIMER
+ */
+
+/* undef to allow use of extra debug capability */
+/*
+#define CY_ALLOW_EXTRA_DEBUG
+*/
+
+/* undef to remove additional debug prints */
+/*
+#define CY_USE_EXTRA_DEBUG
+*/
+
+/* undef to remove additional debug prints */
+/*
+#define CY_USE_EXTRA_DEBUG1
+ */
+
+/* undef to use operational touch timer jiffies; else use test jiffies */
+/*
+ */
+ /*
+#define CY_USE_TIMER_DEBUG
+*/
+/* define to use canned test data */
+/*
+#define CY_USE_TEST_DATA
+ */
+
+/* define if gesture signaling is used
+ * and which gesture groups to use
+ */
+/*
+#define CY_USE_GEST
+#define CY_USE_GEST_GRP1
+#define CY_USE_GEST_GRP2
+#define CY_USE_GEST_GRP3
+#define CY_USE_GEST_GRP4
+ */
+/* Active distance in pixels for a gesture to be reported
+ * if set to 0, then all gesture movements are reported
+ */
+#define CY_ACT_DIST_DFLT 8
+#define CY_ACT_DIST CY_ACT_DIST_DFLT
+
+/* define if MT signals are desired */
+/*
+*/
+#define CY_USE_MT_SIGNALS
+
+/* define if MT tracking id signals are used */
+/*
+#define CY_USE_MT_TRACK_ID
+ */
+
+/* define if ST signals are required */
+/*
+*/
+//#define CY_USE_ST_SIGNALS
+
+/* define to send handshake to device */
+/*
+*/
+#define CY_USE_HNDSHK
+
+/* define if log all raw motion signals to a sysfs file */
+/*
+#define CY_LOG_TO_FILE
+*/
+
+
+/* End of the Global Control section
+ ******************************************************************************
+ */
+#define CY_DIFF(m, n) ((m) != (n))
+
+#ifdef CY_LOG_TO_FILE
+ #define cyttsp_openlog() /* use sysfs */
+#else
+ #define cyttsp_openlog()
+#endif /* CY_LOG_TO_FILE */
+
+/* see kernel.h for pr_xxx def'ns */
+#define cyttsp_info(f, a...) pr_info("%s:" f, __func__ , ## a)
+#define cyttsp_error(f, a...) pr_err("%s:" f, __func__ , ## a)
+#define cyttsp_alert(f, a...) pr_alert("%s:" f, __func__ , ## a)
+
+#ifdef CY_USE_DEBUG
+ #define cyttsp_debug(f, a...) pr_alert("%s:" f, __func__ , ## a)
+#else
+ #define cyttsp_debug(f, a...) {if (cyttsp_tsdebug) \
+ pr_alert("%s:" f, __func__ , ## a); }
+#endif /* CY_USE_DEBUG */
+
+#ifdef CY_ALLOW_EXTRA_DEBUG
+#ifdef CY_USE_EXTRA_DEBUG
+ #define cyttsp_xdebug(f, a...) pr_alert("%s:" f, __func__ , ## a)
+#else
+ #define cyttsp_xdebug(f, a...) {if (cyttsp_tsxdebug) \
+ pr_alert("%s:" f, __func__ , ## a); }
+#endif /* CY_USE_EXTRA_DEBUG */
+
+#ifdef CY_USE_EXTRA_DEBUG1
+ #define cyttsp_xdebug1(f, a...) pr_alert("%s:" f, __func__ , ## a)
+#else
+ #define cyttsp_xdebug1(f, a...)
+#endif /* CY_USE_EXTRA_DEBUG1 */
+#else
+ #define cyttsp_xdebug(f, a...)
+ #define cyttsp_xdebug1(f, a...)
+#endif /* CY_ALLOW_EXTRA_DEBUG */
+
+#ifdef CY_USE_TIMER_DEBUG
+ #define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(1000))
+#else
+ #define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(28))
+#endif
+
+/* reduce extra signals in MT only build
+ * be careful not to lose backward compatibility for pre-MT apps
+ */
+#ifdef CY_USE_ST_SIGNALS
+ #define CY_USE_ST 1
+#else
+ #define CY_USE_ST 0
+#endif /* CY_USE_ST_SIGNALS */
+
+/* rely on kernel input.h to define Multi-Touch capability */
+/* if input.h defines the Multi-Touch signals, then use MT */
+#if defined(ABS_MT_TOUCH_MAJOR) && defined(CY_USE_MT_SIGNALS)
+ #define CY_USE_MT 1
+ #define CY_MT_SYNC(input) input_mt_sync(input)
+#else
+ #define CY_USE_MT 0
+ #define CY_MT_SYNC(input)
+ /* the following includes are provided to ensure a compile;
+ * the code that compiles with these defines will not be executed if
+ * the CY_USE_MT is properly used in the platform structure init
+ */
+ #ifndef ABS_MT_TOUCH_MAJOR
+ #define ABS_MT_TOUCH_MAJOR 0x30 /* touching ellipse */
+ #define ABS_MT_TOUCH_MINOR 0x31 /* (omit if circular) */
+ #define ABS_MT_WIDTH_MAJOR 0x32 /* approaching ellipse */
+ #define ABS_MT_WIDTH_MINOR 0x33 /* (omit if circular) */
+ #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
+ #define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
+ #define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
+ #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
+ #define ABS_MT_BLOB_ID 0x38 /* Group set of pkts as blob */
+ #endif /* ABS_MT_TOUCH_MAJOR */
+#endif /* ABS_MT_TOUCH_MAJOR and CY_USE_MT_SIGNALS */
+#if defined(ABS_MT_TRACKING_ID) && defined(CY_USE_MT_TRACK_ID)
+ #define CY_USE_TRACKING_ID 1
+#else
+ #define CY_USE_TRACKING_ID 0
+/* define only if not defined already by system;
+ * value based on linux kernel 2.6.30.10
+ */
+#ifndef ABS_MT_TRACKING_ID
+ #define ABS_MT_TRACKING_ID (ABS_MT_BLOB_ID+1)
+#endif
+#endif /* ABS_MT_TRACKING_ID */
+
+#define CY_USE_DEEP_SLEEP_SEL 0x80
+#define CY_USE_LOW_POWER_SEL 0x01
+
+#ifdef CY_USE_TEST_DATA
+ #define cyttsp_testdat(ray1, ray2, sizeofray) \
+ { \
+ int i; \
+ u8 *up1 = (u8 *)ray1; \
+ u8 *up2 = (u8 *)ray2; \
+ for (i = 0; i < sizeofray; i++) { \
+ up1[i] = up2[i]; \
+ } \
+ }
+#else
+ #define cyttsp_testdat(xy, test_xy, sizeofray)
+#endif /* CY_USE_TEST_DATA */
+
+/* helper macros */
+#define GET_NUM_TOUCHES(x) ((x) & 0x0F)
+#define GET_TOUCH1_ID(x) (((x) & 0xF0) >> 4)
+#define GET_TOUCH2_ID(x) ((x) & 0x0F)
+#define GET_TOUCH3_ID(x) (((x) & 0xF0) >> 4)
+#define GET_TOUCH4_ID(x) ((x) & 0x0F)
+#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4)
+#define FLIP_DATA_FLAG 0x01
+#define REVERSE_X_FLAG 0x02
+#define REVERSE_Y_FLAG 0x04
+#define FLIP_DATA(flags) ((flags) & FLIP_DATA_FLAG)
+#define REVERSE_X(flags) ((flags) & REVERSE_X_FLAG)
+#define REVERSE_Y(flags) ((flags) & REVERSE_Y_FLAG)
+#define FLIP_XY(x, y) { \
+ u16 tmp; \
+ tmp = (x); \
+ (x) = (y); \
+ (y) = tmp; \
+ }
+#define INVERT_X(x, xmax) ((xmax) - (x))
+#define INVERT_Y(y, maxy) ((maxy) - (y))
+#define SET_HSTMODE(reg, mode) ((reg) & (mode))
+#define GET_HSTMODE(reg) ((reg & 0x70) >> 4)
+#define GET_BOOTLOADERMODE(reg) ((reg & 0x10) >> 4)
+
+/* constant definitions */
+/* maximum number of concurrent ST track IDs */
+#define CY_NUM_ST_TCH_ID 2
+
+/* maximum number of concurrent MT track IDs */
+#define CY_NUM_MT_TCH_ID 4
+
+/* maximum number of track IDs */
+#define CY_NUM_TRK_ID 16
+
+#define CY_NTCH 0 /* no touch (lift off) */
+#define CY_TCH 1 /* active touch (touchdown) */
+#define CY_ST_FNGR1_IDX 0
+#define CY_ST_FNGR2_IDX 1
+#define CY_MT_TCH1_IDX 0
+#define CY_MT_TCH2_IDX 1
+#define CY_MT_TCH3_IDX 2
+#define CY_MT_TCH4_IDX 3
+#define CY_XPOS 0
+#define CY_YPOS 1
+#define CY_IGNR_TCH (-1)
+#define CY_SMALL_TOOL_WIDTH 10
+#define CY_LARGE_TOOL_WIDTH 255
+#define CY_REG_BASE 0x00
+#define CY_REG_GEST_SET 0x1E
+#define CY_REG_ACT_INTRVL 0x1D
+#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL+1)
+#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT+1)
+#define CY_SOFT_RESET ((1 << 0))
+#define CY_DEEP_SLEEP ((1 << 1))
+#define CY_LOW_POWER ((1 << 2))
+#define CY_MAXZ 255
+#define CY_OK 0
+#define CY_INIT 1
+#define CY_DLY_DFLT 10 /* ms */
+#define CY_DLY_SYSINFO 20 /* ms */
+#define CY_DLY_BL 300
+#define CY_DLY_DNLOAD 100 /* ms */
+#define CY_NUM_RETRY 4 /* max num touch data read */
+
+/* handshake bit in the hst_mode reg */
+#define CY_HNDSHK_BIT 0x80
+#ifdef CY_USE_HNDSHK
+ #define CY_SEND_HNDSHK 1
+#else
+ #define CY_SEND_HNDSHK 0
+#endif
+
+/* Bootloader File 0 offset */
+#define CY_BL_FILE0 0x00
+
+/* Bootloader command directive */
+#define CY_BL_CMD 0xFF
+
+/* Bootloader Initiate Bootload */
+#define CY_BL_INIT_LOAD 0x38
+
+/* Bootloader Write a Block */
+#define CY_BL_WRITE_BLK 0x39
+
+/* Bootloader Terminate Bootload */
+#define CY_BL_TERMINATE 0x3B
+
+/* Bootloader Exit and Verify Checksum command */
+#define CY_BL_EXIT 0xA5
+
+/* Bootloader default keys */
+#define CY_BL_KEY0 0x00
+#define CY_BL_KEY1 0x01
+#define CY_BL_KEY2 0x02
+#define CY_BL_KEY3 0x03
+#define CY_BL_KEY4 0x04
+#define CY_BL_KEY5 0x05
+#define CY_BL_KEY6 0x06
+#define CY_BL_KEY7 0x07
+
+/* Active Power state scanning/processing refresh interval */
+#define CY_ACT_INTRVL_DFLT 0x00
+
+/* touch timeout for the Active power */
+#define CY_TCH_TMOUT_DFLT 0xFF
+
+/* Low Power state scanning/processing refresh interval */
+#define CY_LP_INTRVL_DFLT 0x0A
+
+#define CY_IDLE_STATE 0
+#define CY_ACTIVE_STATE 1
+#define CY_LOW_PWR_STATE 2
+#define CY_SLEEP_STATE 3
+
+/* device mode bits */
+#define CY_OP_MODE 0x00
+#define CY_SYSINFO_MODE 0x10
+
+/* power mode select bits */
+#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */
+#define CY_DEEP_SLEEP_MODE 0x02
+#define CY_LOW_PWR_MODE 0x04
+
+#define CY_NUM_KEY 8
+
+#ifdef CY_USE_GEST
+ #define CY_USE_GESTURES 1
+#else
+ #define CY_USE_GESTURES 0
+#endif /* CY_USE_GESTURE_SIGNALS */
+
+#ifdef CY_USE_GEST_GRP1
+ #define CY_GEST_GRP1 0x10
+#else
+ #define CY_GEST_GRP1 0x00
+#endif /* CY_USE_GEST_GRP1 */
+#ifdef CY_USE_GEST_GRP2
+ #define CY_GEST_GRP2 0x20
+#else
+ #define CY_GEST_GRP2 0x00
+#endif /* CY_USE_GEST_GRP2 */
+#ifdef CY_USE_GEST_GRP3
+ #define CY_GEST_GRP3 0x40
+#else
+ #define CY_GEST_GRP3 0x00
+#endif /* CY_USE_GEST_GRP3 */
+#ifdef CY_USE_GEST_GRP4
+ #define CY_GEST_GRP4 0x80
+#else
+ #define CY_GEST_GRP4 0x00
+#endif /* CY_USE_GEST_GRP4 */
+
+struct cyttsp_regulator {
+ const char *name;
+ u32 min_uV;
+ u32 max_uV;
+ u32 load_uA;
+};
+
+struct cyttsp_platform_data {
+ u32 panel_maxx;
+ u32 panel_maxy;
+ u32 disp_resx;
+ u32 disp_resy;
+ u32 disp_minx;
+ u32 disp_miny;
+ u32 disp_maxx;
+ u32 disp_maxy;
+ u8 correct_fw_ver;
+ u32 flags;
+ u8 gen;
+ u8 use_st;
+ u8 use_mt;
+ u8 use_hndshk;
+ u8 use_trk_id;
+ u8 use_sleep;
+ u8 use_gestures;
+ u8 gest_set;
+ u8 act_intrvl;
+ u8 tch_tmout;
+ u8 lp_intrvl;
+ u8 power_state;
+ bool wakeup;
+ int sleep_gpio;
+ int resout_gpio;
+ int irq_gpio;
+ struct cyttsp_regulator *regulator_info;
+ u8 num_regulators;
+ const char *fw_fname;
+#ifdef CY_USE_I2C_DRIVER
+ s32 (*init)(struct i2c_client *client);
+ s32 (*resume)(struct i2c_client *client);
+#endif
+#ifdef CY_USE_SPI_DRIVER
+ s32 (*init)(struct spi_device *spi);
+ s32 (*resume)(struct spi_device *spi);
+#endif
+};
+
+/* TrueTouch Standard Product Gen3 (Txx3xx) interface definition */
+struct cyttsp_gen3_xydata_t {
+ u8 hst_mode;
+ u8 tt_mode;
+ u8 tt_stat;
+ u16 x1 __attribute__ ((packed));
+ u16 y1 __attribute__ ((packed));
+ u8 z1;
+ u8 touch12_id;
+ u16 x2 __attribute__ ((packed));
+ u16 y2 __attribute__ ((packed));
+ u8 z2;
+ u8 gest_cnt;
+ u8 gest_id;
+ u16 x3 __attribute__ ((packed));
+ u16 y3 __attribute__ ((packed));
+ u8 z3;
+ u8 touch34_id;
+ u16 x4 __attribute__ ((packed));
+ u16 y4 __attribute__ ((packed));
+ u8 z4;
+ u8 tt_undef[3];
+ u8 gest_set;
+ u8 tt_reserved;
+};
+
+/* TrueTouch Standard Product Gen2 (Txx2xx) interface definition */
+#define CY_GEN2_NOTOUCH 0x03 /* Both touches removed */
+#define CY_GEN2_GHOST 0x02 /* ghost */
+#define CY_GEN2_2TOUCH 0x03 /* 2 touch; no ghost */
+#define CY_GEN2_1TOUCH 0x01 /* 1 touch only */
+#define CY_GEN2_TOUCH2 0x01 /* 1st touch removed;
+ * 2nd touch remains */
+struct cyttsp_gen2_xydata_t {
+ u8 hst_mode;
+ u8 tt_mode;
+ u8 tt_stat;
+ u16 x1 __attribute__ ((packed));
+ u16 y1 __attribute__ ((packed));
+ u8 z1;
+ u8 evnt_idx;
+ u16 x2 __attribute__ ((packed));
+ u16 y2 __attribute__ ((packed));
+ u8 tt_undef1;
+ u8 gest_cnt;
+ u8 gest_id;
+ u8 tt_undef[14];
+ u8 gest_set;
+ u8 tt_reserved;
+};
+
+/* TTSP System Information interface definition */
+struct cyttsp_sysinfo_data_t {
+ u8 hst_mode;
+ u8 mfg_cmd;
+ u8 mfg_stat;
+ u8 cid[3];
+ u8 tt_undef1;
+ u8 uid[8];
+ u8 bl_verh;
+ u8 bl_verl;
+ u8 tts_verh;
+ u8 tts_verl;
+ u8 app_idh;
+ u8 app_idl;
+ u8 app_verh;
+ u8 app_verl;
+ u8 tt_undef[6];
+ u8 act_intrvl;
+ u8 tch_tmout;
+ u8 lp_intrvl;
+};
+
+/* TTSP Bootloader Register Map interface definition */
+#define CY_BL_CHKSUM_OK 0x01
+struct cyttsp_bootloader_data_t {
+ u8 bl_file;
+ u8 bl_status;
+ u8 bl_error;
+ u8 blver_hi;
+ u8 blver_lo;
+ u8 bld_blver_hi;
+ u8 bld_blver_lo;
+ u8 ttspver_hi;
+ u8 ttspver_lo;
+ u8 appid_hi;
+ u8 appid_lo;
+ u8 appver_hi;
+ u8 appver_lo;
+ u8 cid_0;
+ u8 cid_1;
+ u8 cid_2;
+};
+
+#define cyttsp_wake_data_t cyttsp_gen3_xydata_t
+#ifdef CY_DECLARE_GLOBALS
+ #ifdef CY_INCLUDE_LOAD_FILE
+ /* this file declares:
+ * firmware download block array (cyttsp_fw[]),
+ * the number of command block records (cyttsp_fw_records),
+ * and the version variables
+ */
+ #include "cyttsp_fw.h" /* imports cyttsp_fw[] array */
+ #define cyttsp_app_load() 1
+ #ifdef CY_FORCE_FW_UPDATE
+ #define cyttsp_force_fw_load() 1
+ #else
+ #define cyttsp_force_fw_load() 0
+ #endif
+
+ #else
+ /* the following declarations are to allow
+ * some debugging capability
+ */
+ unsigned char cyttsp_fw_tts_verh = 0x00;
+ unsigned char cyttsp_fw_tts_verl = 0x01;
+ unsigned char cyttsp_fw_app_idh = 0x02;
+ unsigned char cyttsp_fw_app_idl = 0x03;
+ unsigned char cyttsp_fw_app_verh = 0x04;
+ unsigned char cyttsp_fw_app_verl = 0x05;
+ unsigned char cyttsp_fw_cid_0 = 0x06;
+ unsigned char cyttsp_fw_cid_1 = 0x07;
+ unsigned char cyttsp_fw_cid_2 = 0x08;
+ #define cyttsp_app_load() 0
+ #define cyttsp_force_fw_load() 0
+ #endif
+ #define cyttsp_tts_verh() cyttsp_fw_tts_verh
+ #define cyttsp_tts_verl() cyttsp_fw_tts_verl
+ #define cyttsp_app_idh() cyttsp_fw_app_idh
+ #define cyttsp_app_idl() cyttsp_fw_app_idl
+ #define cyttsp_app_verh() cyttsp_fw_app_verh
+ #define cyttsp_app_verl() cyttsp_fw_app_verl
+ #define cyttsp_cid_0() cyttsp_fw_cid_0
+ #define cyttsp_cid_1() cyttsp_fw_cid_1
+ #define cyttsp_cid_2() cyttsp_fw_cid_2
+ #ifdef CY_USE_TEST_DATA
+ static struct cyttsp_gen2_xydata_t tt_gen2_testray[] = {
+ {0x00}, {0x00}, {0x04},
+ {0x4000}, {0x8000}, {0x80},
+ {0x03},
+ {0x2000}, {0x1000}, {0x00},
+ {0x00},
+ {0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00},
+ {0x00}
+ };
+
+ static struct cyttsp_gen3_xydata_t tt_gen3_testray[] = {
+ {0x00}, {0x00}, {0x04},
+ {0x4000}, {0x8000}, {0x80},
+ {0x12},
+ {0x2000}, {0x1000}, {0xA0},
+ {0x00}, {0x00},
+ {0x8000}, {0x4000}, {0xB0},
+ {0x34},
+ {0x4000}, {0x1000}, {0xC0},
+ {0x00, 0x00, 0x00},
+ {0x00},
+ {0x00}
+ };
+ #endif /* CY_USE_TEST_DATA */
+
+#else
+ extern u8 g_appload_ray[];
+#endif
+#define FW_FNAME_LEN 40
+#define TP_ID_GPIO 85
+
+
+/* CY TTSP I2C Driver private data */
+struct cyttsp {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct work_struct work;
+ struct timer_list timer;
+ struct mutex mutex;
+ char phys[32];
+ struct cyttsp_platform_data *platform_data;
+ u8 num_prv_st_tch;
+ u16 act_trk[CY_NUM_TRK_ID];
+ u16 prv_st_tch[CY_NUM_ST_TCH_ID];
+ u16 prv_mt_tch[CY_NUM_MT_TCH_ID];
+ u16 prv_mt_pos[CY_NUM_TRK_ID][2];
+ atomic_t irq_enabled;
+ bool cyttsp_update_fw;
+ bool cyttsp_fwloader_mode;
+ bool is_suspended;
+ struct regulator **vdd;
+ char fw_fname[FW_FNAME_LEN];
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+ int tpid;
+
+};
+extern struct cyttsp cust_ts;
+extern void cyttsp_fw_upgrade(void);
+extern void cyttsp_hw_reset(void);
+
+
+#endif /* __CYTTSP_H__ */
diff --git a/drivers/input/touchscreen/cyp140_ts/cyttsp_fw_upgrade.c b/drivers/input/touchscreen/cyp140_ts/cyttsp_fw_upgrade.c
new file mode 100755
index 00000000..ca6e9d10
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/cyttsp_fw_upgrade.c
@@ -0,0 +1,993 @@
+/* Source for:
+ * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver.
+ * drivers/input/touchscreen/cyttsp-i2c.c
+ *
+ * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Cypress reserves the right to make changes without further notice
+ * to the materials described herein. Cypress does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/byteorder/generic.h>
+#include <linux/bitops.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+//#include <mach/vreg.h>
+
+#define CY_DECLARE_GLOBALS
+
+#include "cyttsp.h"
+
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+
+#include <linux/poll.h>
+
+#define LOG_TP() printk("++++%s, Line %d\n", __FUNCTION__, __LINE__);
+
+
+#define CYTTSP_SUPPORT_READ_TP_VERSION
+
+#define CYTTSP_SUPPORT_TP_SENSOR
+// need upgrade,add MACRO
+//#ifdef CONFIG_LCT_AW551_YL
+#define CYTTSP_SUPPORT_SYS_NODE
+//#endif
+
+extern struct tpd_device *tpd;//add 2013-1-7
+
+int cyttsp_vendor_id=20;
+int cyttsp_firmware_version= -1;
+
+int cyttsp_has_bootloader=0;
+
+#ifdef CYTTSP_SUPPORT_TP_SENSOR
+
+#define CYTTSP_DEBUG_TP_SENSOR
+
+#define CYTTSP_SUPPORT_TP_SENSOR_FIRMWARE_VERSION (0xc) //if we change this value we should also change func apds9900_init_dev in file apds9000.c
+static DEFINE_MUTEX(cyttsp_sensor_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(cyttsp_sensor_waitqueue);
+
+//static char cyttsp_sensor_data = 0; // 0 near 1 far
+//static int cyttsp_sensor_data_changed = 0;
+//static struct i2c_client *tp_sensor_I2Cclient = NULL;
+//static int cyttsp_sensor_opened = 0;
+
+
+
+#endif
+
+
+
+#define CYTTSP_AW551_OFILM "cyttspfw_aw551_ofilm.fw"
+#define CYTTSP_AW551_TRULY "cyttspfw_aw551_truly.fw"
+#define CYTTSP_AW550_TRULY "cyttspfw_aw550_truly.fw"
+
+uint32_t cyttsp_tsdebug1 = 0xff;
+
+module_param_named(tsdebug1, cyttsp_tsdebug1, uint, 0664);
+
+#define FW_FNAME_LEN 40
+#define TP_ID_GPIO 85
+
+//static u8 irq_cnt; /* comparison counter with register valuw */
+//static u32 irq_cnt_total; /* total interrupts */
+//static u32 irq_err_cnt; /* count number of touch interrupts with err */
+#define CY_IRQ_CNT_MASK 0x000000FF /* mapped for sizeof count in reg */
+#define CY_IRQ_CNT_REG 0x00 /* tt_undef[0]=reg 0x1B - Gen3 only */
+
+
+
+/* ****************************************************************************
+ * Prototypes for static functions
+ * ************************************************************************** */
+
+static int cyttsp_putbl(struct cyttsp *ts, int show,
+ int show_status, int show_version, int show_cid);
+/*static int __devinit cyttsp_probe(struct i2c_client *client,
+ const struct i2c_device_id *id); */
+//static int __devexit cyttsp_remove(struct i2c_client *client);
+//static int cyttsp_resume(struct device *dev);
+//static int cyttsp_suspend(struct device *dev);
+
+#ifdef CYTTSP_SUPPORT_SYS_NODE
+//static int cyttsp_power_down(void);
+#endif
+
+
+
+/* Static variables */
+//static struct cyttsp_gen3_xydata_t g_xy_data;
+static struct cyttsp_bootloader_data_t g_bl_data;
+static struct cyttsp_sysinfo_data_t g_sysinfo_data;
+static const struct i2c_device_id cyttsp_id[] = {
+ { CY_I2C_NAME, 0 }, { }
+};
+static u8 bl_cmd[] = {
+ CY_BL_FILE0, CY_BL_CMD, CY_BL_EXIT,
+ CY_BL_KEY0, CY_BL_KEY1, CY_BL_KEY2,
+ CY_BL_KEY3, CY_BL_KEY4, CY_BL_KEY5,
+ CY_BL_KEY6, CY_BL_KEY7};
+
+MODULE_DEVICE_TABLE(i2c, cyttsp_id);
+
+
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver");
+MODULE_AUTHOR("Cypress");
+
+
+
+#ifdef CYTTSP_SUPPORT_TP_SENSOR
+#define CYTTSP_SENSOR_IOM 'r'
+
+#define CYTTSP_SENSOR_IOC_SET_PS_ENABLE _IOW(CYTTSP_SENSOR_IOM, 0, char *)
+#define CYTTSP_SENSOR_READ_PS_DATA _IOR(CYTTSP_SENSOR_IOM, 2, char *)
+
+
+
+
+#endif
+
+
+
+
+
+
+
+
+
+static void cyttsp_exit_bl_mode(struct cyttsp *ts);
+
+
+
+
+
+#ifdef CYTTSP_SUPPORT_SYS_NODE
+/* firmware flashing block */
+#define BLK_SIZE 16
+#define DATA_REC_LEN 64
+#define START_ADDR 0x0880//0x0b00
+#define BLK_SEED 0xff
+#define RECAL_REG 0x1b
+
+enum bl_commands {
+ BL_CMD_WRBLK = 0x39,
+ BL_CMD_INIT = 0x38,
+ BL_CMD_TERMINATE = 0x3b,
+};
+/* TODO: Add key as part of platform data */
+#define KEY_CS (0 + 1 + 2 + 3 + 4 + 5 + 6 + 7)
+#define KEY {0, 1, 2, 3, 4, 5, 6, 7}
+
+static const char _key[] = KEY;
+#define KEY_LEN sizeof(_key)
+
+static int rec_cnt;
+struct fw_record {
+ u8 seed;
+ u8 cmd;
+ u8 key[KEY_LEN];
+ u8 blk_hi;
+ u8 blk_lo;
+ u8 data[DATA_REC_LEN];
+ u8 data_cs;
+ u8 rec_cs;
+};
+#define fw_rec_size (sizeof(struct fw_record))
+
+struct cmd_record {
+ u8 reg;
+ u8 seed;
+ u8 cmd;
+ u8 key[KEY_LEN];
+};
+#define cmd_rec_size (sizeof(struct cmd_record))
+
+static struct fw_record data_record = {
+ .seed = BLK_SEED,
+ .cmd = BL_CMD_WRBLK,
+ .key = KEY,
+};
+
+static const struct cmd_record terminate_rec = {
+ .reg = 0,
+ .seed = BLK_SEED,
+ .cmd = BL_CMD_TERMINATE,
+ .key = KEY,
+};
+static const struct cmd_record initiate_rec = {
+ .reg = 0,
+ .seed = BLK_SEED,
+ .cmd = BL_CMD_INIT,
+ .key = KEY,
+};
+
+#define BL_REC1_ADDR 0x0780
+#define BL_REC2_ADDR 0x07c0
+
+#define ID_INFO_REC ":40078000"
+#define ID_INFO_OFFSET_IN_REC 77
+
+#define REC_START_CHR ':'
+#define REC_LEN_OFFSET 1
+#define REC_ADDR_HI_OFFSET 3
+#define REC_ADDR_LO_OFFSET 5
+#define REC_TYPE_OFFSET 7
+#define REC_DATA_OFFSET 9
+#define REC_LINE_SIZE 141
+
+static int cyttsp_soft_reset(struct cyttsp *ts)
+{
+ int retval = 0, tries = 0;
+ u8 host_reg = CY_SOFT_RESET_MODE;
+
+ #if 0
+ gpio_set_value(ts->platform_data->resout_gpio, 1); //reset high valid
+ msleep(100);
+ gpio_set_value(ts->platform_data->resout_gpio, 0);
+ msleep(1000);
+ #endif
+ LOG_TP();
+
+ #if 1// 0 modify 2013-1-7!!!!!!!!!!!
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 10 && (retval < 0));
+
+ if (retval < 0) {
+ pr_err("%s: failed\n", __func__);
+ return retval;
+ }
+ #else
+ cyttsp_hw_reset(); //!!!! 2013-1-7 may not go here !!!
+ #endif
+
+ LOG_TP();
+ tries = 0;
+ do {
+ msleep(20);
+ cyttsp_putbl(ts, 1, true, true, false);
+ } while (g_bl_data.bl_status != 0x10 &&
+ g_bl_data.bl_status != 0x11 &&
+ tries++ < 100);
+ LOG_TP();
+ if (g_bl_data.bl_status != 0x11 && g_bl_data.bl_status != 0x10)
+ return -EINVAL;
+ LOG_TP();
+ return 0;
+}
+
+static void cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+ int retval, tries = 0;
+
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(bl_cmd), bl_cmd);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 10 && (retval < 0));
+}
+
+static void cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+ int retval, tries = 0;
+ u8 host_reg = CY_SYSINFO_MODE;
+
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 10 && (retval < 0));
+
+ /* wait for TTSP Device to complete switch to SysInfo mode */
+ if (!(retval < 0)) {
+ retval = i2c_smbus_read_i2c_block_data(ts->client,
+ CY_REG_BASE,
+ sizeof(struct cyttsp_sysinfo_data_t),
+ (u8 *)&g_sysinfo_data);
+ } else
+ pr_err("%s: failed\n", __func__);
+}
+
+static void cyttsp_set_opmode(struct cyttsp *ts)
+{
+ int retval, tries = 0;
+ u8 host_reg = CY_OP_MODE;
+
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(host_reg), &host_reg);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 10 && (retval < 0));
+}
+
+static int str2uc(char *str, u8 *val)
+{
+ char substr[3];
+ unsigned long ulval;
+ int rc;
+
+ if (!str && strlen(str) < 2)
+ return -EINVAL;
+
+ substr[0] = str[0];
+ substr[1] = str[1];
+ substr[2] = '\0';
+
+ rc = strict_strtoul(substr, 16, &ulval);
+ if (rc != 0)
+ return rc;
+
+ *val = (u8) ulval;
+
+ return 0;
+}
+
+static int flash_block(struct cyttsp *ts, u8 *blk, int len)
+{
+ int retval, i, tries = 0;
+ char buf[(2 * (BLK_SIZE + 1)) + 1];
+ char *p = buf;
+
+ for (i = 0; i < len; i++, p += 2)
+ sprintf(p, "%02x", blk[i]);
+ pr_debug("%s: size %d, pos %ld payload %s\n",
+ __func__, len, (long)0, buf);
+
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, len, blk);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 20 && (retval < 0));
+
+ if (retval < 0) {
+ pr_err("%s: failed\n", __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int flash_command(struct cyttsp *ts, const struct cmd_record *record)
+{
+ return flash_block(ts, (u8 *)record, cmd_rec_size);
+}
+
+static void init_data_record(struct fw_record *rec, unsigned short addr)
+{
+ addr >>= 6;
+ rec->blk_hi = (addr >> 8) & 0xff;
+ rec->blk_lo = addr & 0xff;
+ rec->rec_cs = rec->blk_hi + rec->blk_lo +
+ (unsigned char)(BLK_SEED + BL_CMD_WRBLK + KEY_CS);
+ rec->data_cs = 0;
+}
+
+static int check_record(u8 *rec)
+{
+ int rc;
+ u16 addr;
+ u8 r_len, type, hi_off, lo_off;
+
+ rc = str2uc(rec + REC_LEN_OFFSET, &r_len);
+ if (rc < 0)
+ return rc;
+
+ rc = str2uc(rec + REC_TYPE_OFFSET, &type);
+ if (rc < 0)
+ return rc;
+
+ if (*rec != REC_START_CHR || r_len != DATA_REC_LEN || type != 0)
+ return -EINVAL;
+
+ rc = str2uc(rec + REC_ADDR_HI_OFFSET, &hi_off);
+ if (rc < 0)
+ return rc;
+
+ rc = str2uc(rec + REC_ADDR_LO_OFFSET, &lo_off);
+ if (rc < 0)
+ return rc;
+
+ addr = (hi_off << 8) | lo_off;
+
+ if (addr >= START_ADDR || addr == BL_REC1_ADDR || addr == BL_REC2_ADDR)
+ return 0;
+
+ return -EINVAL;
+}
+
+static struct fw_record *prepare_record(u8 *rec)
+{
+ int i, rc;
+ u16 addr;
+ u8 hi_off, lo_off;
+ u8 *p;
+
+ rc = str2uc(rec + REC_ADDR_HI_OFFSET, &hi_off);
+ if (rc < 0)
+ return ERR_PTR((long) rc);
+
+ rc = str2uc(rec + REC_ADDR_LO_OFFSET, &lo_off);
+ if (rc < 0)
+ return ERR_PTR((long) rc);
+
+ addr = (hi_off << 8) | lo_off;
+
+ init_data_record(&data_record, addr);
+ p = rec + REC_DATA_OFFSET;
+ for (i = 0; i < DATA_REC_LEN; i++) {
+ rc = str2uc(p, &data_record.data[i]);
+ if (rc < 0)
+ return ERR_PTR((long) rc);
+ data_record.data_cs += data_record.data[i];
+ data_record.rec_cs += data_record.data[i];
+ p += 2;
+ }
+ data_record.rec_cs += data_record.data_cs;
+
+ return &data_record;
+}
+
+static int flash_record(struct cyttsp *ts, const struct fw_record *record)
+{
+ int len = fw_rec_size;
+ int blk_len, rc;
+ u8 *rec = (u8 *)record;
+ u8 data[BLK_SIZE + 1];
+ u8 blk_offset;
+
+ for (blk_offset = 0; len; len -= blk_len) {
+ data[0] = blk_offset;
+ blk_len = len > BLK_SIZE ? BLK_SIZE : len;
+ memcpy(data + 1, rec, blk_len);
+ rec += blk_len;
+ rc = flash_block(ts, data, blk_len + 1);
+ if (rc < 0)
+ return rc;
+ blk_offset += blk_len;
+ }
+ return 0;
+}
+
+static int flash_data_rec(struct cyttsp *ts, u8 *buf)
+{
+ struct fw_record *rec;
+ int rc, tries;
+LOG_TP();
+ if (!buf)
+ return -EINVAL;
+
+ rc = check_record(buf);
+
+ if (rc < 0) {
+ pr_debug("%s: record ignored %s", __func__, buf);
+ return 0;
+ }
+
+ rec = prepare_record(buf);
+ if (IS_ERR_OR_NULL(rec))
+ return PTR_ERR(rec);
+
+ rc = flash_record(ts, rec);
+ if (rc < 0)
+ return rc;
+
+ tries = 0;
+ do {
+//printk("++++%s, Line %d, tries=%d\n", __FUNCTION__, __LINE__,tries ); //wujinyou
+
+ //if (rec_cnt%2)
+ //msleep(20);
+ if(tries >50)
+ {
+ printk("++++%s, Line %d, tries=%d\n", __FUNCTION__, __LINE__,tries ); //wujinyou
+ msleep(20);
+ }
+ cyttsp_putbl(ts, 4, true, false, false);
+ } while (g_bl_data.bl_status != 0x10 &&
+ g_bl_data.bl_status != 0x11 &&
+ tries++ < 100);
+ rec_cnt++;
+ return rc;
+}
+
+static int cyttspfw_flash_firmware(struct cyttsp *ts, const u8 *data,
+ int data_len)
+{
+ u8 *buf;
+ int i, j;
+ int rc, tries = 0;
+ LOG_TP();
+
+ /* initiate bootload: this will erase all the existing data */
+ rc = flash_command(ts, &initiate_rec);
+ if (rc < 0)
+ return rc;
+
+ do {
+// LOG_TP();
+printk("++++%s, Line %d, tries=%d\n", __FUNCTION__, __LINE__,tries );
+ msleep(60);
+ cyttsp_putbl(ts, 4, true, false, false);
+ } while (g_bl_data.bl_status != 0x10 &&
+ g_bl_data.bl_status != 0x11 &&
+ tries++ < 100);
+
+ buf = kzalloc(REC_LINE_SIZE + 1, GFP_KERNEL);
+ if (!buf) {
+ pr_err("%s: no memory\n", __func__);
+ return -ENOMEM;
+ }
+ LOG_TP();
+ rec_cnt = 0;
+ /* flash data records */
+ for (i = 0, j = 0; i < data_len; i++, j++) {
+ if ((data[i] == REC_START_CHR) && j) {
+ buf[j] = 0;
+ rc = flash_data_rec(ts, buf);
+ if (rc < 0)
+ return rc;
+ j = 0;
+ }
+ buf[j] = data[i];
+ }
+ LOG_TP();
+ /* flash last data record */
+ if (j) {
+ buf[j] = 0;
+ rc = flash_data_rec(ts, buf);
+ if (rc < 0)
+ return rc;
+ }
+ LOG_TP();
+ kfree(buf);
+
+ /* termiate bootload */
+ tries = 0;
+ rc = flash_command(ts, &terminate_rec);
+ do {
+ msleep(100);
+printk("++++%s, Line %d, tries=%d\n", __FUNCTION__, __LINE__,tries );
+
+ cyttsp_putbl(ts, 4, true, false, false);
+ } while (g_bl_data.bl_status != 0x10 &&
+ g_bl_data.bl_status != 0x11 &&
+ tries++ < 100);
+ LOG_TP();
+ return rc;
+}
+
+static int get_hex_fw_ver(u8 *p, u8 *ttspver_hi, u8 *ttspver_lo,
+ u8 *appid_hi, u8 *appid_lo, u8 *appver_hi,
+ u8 *appver_lo, u8 *cid_0, u8 *cid_1, u8 *cid_2)
+{
+ int rc;
+
+ p = p + ID_INFO_OFFSET_IN_REC;
+ rc = str2uc(p, ttspver_hi);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, ttspver_lo);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, appid_hi);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, appid_lo);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, appver_hi);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, appver_lo);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, cid_0);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, cid_1);
+ if (rc < 0)
+ return rc;
+ p += 2;
+ rc = str2uc(p, cid_2);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static void cyttspfw_flash_start(struct cyttsp *ts, const u8 *data,
+ int data_len, u8 *buf, bool force)
+{
+ int rc;
+ u8 ttspver_hi = 0, ttspver_lo = 0, fw_upgrade = 0;
+ u8 appid_hi = 0, appid_lo = 0;
+ u8 appver_hi = 0, appver_lo = 0;
+ u8 cid_0 = 0, cid_1 = 0, cid_2 = 0;
+ char *p = buf;
+
+ /* get hex firmware version */
+ rc = get_hex_fw_ver(p, &ttspver_hi, &ttspver_lo,
+ &appid_hi, &appid_lo, &appver_hi,
+ &appver_lo, &cid_0, &cid_1, &cid_2);
+printk("++++tpd-fw-ver: %x %x %x %x %x %x %x %x %x\n", ttspver_hi, ttspver_lo, appid_hi, appid_lo, appver_hi,appver_lo, cid_0, cid_1,cid_2);
+ if (rc < 0) {
+ pr_err("%s: unable to get hex firmware version\n", __func__);
+ return;
+ }
+#if 0 //wujinyou
+ /* disable interrupts before flashing */
+ if (ts->client->irq == 0)
+ del_timer(&ts->timer);
+ else
+ disable_irq(ts->client->irq);
+
+ rc = cancel_work_sync(&ts->work);
+
+ if (rc && ts->client->irq)
+ enable_irq(ts->client->irq);
+#endif
+
+ /* enter bootloader idle mode */
+ rc = cyttsp_soft_reset(ts);
+ //LOG_TP();
+ printk("++++%s, Line %d, rc=%d\n", __FUNCTION__, __LINE__,rc);
+
+ if (rc < 0) {
+ LOG_TP();
+ pr_err("%s: cyttsp_soft_reset try entering into idle mode"
+ " second time\n", __func__);
+ msleep(1000);
+
+ rc = cyttsp_soft_reset(ts);
+ }
+
+ if (rc < 0) {
+ LOG_TP();
+ pr_err("%s:cyttsp_soft_reset try again later\n", __func__);
+ return;
+ }
+
+ LOG_TP();
+
+ pr_info("Current firmware:lusongbai %d.%d.%d", g_bl_data.appid_lo,
+ g_bl_data.appver_hi, g_bl_data.appver_lo);
+ pr_info("New firmware: %d.%d.%d", appid_lo, appver_hi, appver_lo);
+ LOG_TP();
+
+ if (force)
+ fw_upgrade = 1;
+ else
+ if ((appid_hi == g_bl_data.appid_hi) &&
+ (appid_lo == g_bl_data.appid_lo)) {
+ if (appver_hi > g_bl_data.appver_hi) {
+ fw_upgrade = 1;
+ } else if ((appver_hi == g_bl_data.appver_hi) &&
+ (appver_lo > g_bl_data.appver_lo)) {
+ fw_upgrade = 1;
+ } else {
+ fw_upgrade = 0;
+ pr_info("%s: Firmware version "
+ "lesser/equal to existing firmware, "
+ "upgrade not needed\n", __func__);
+ }
+ } else {
+ fw_upgrade = 0;
+ pr_info("%s: Firware versions do not match, "
+ "cannot upgrade\n", __func__);
+ }
+
+ printk("++++%s, Line %d, fw_upgrade=%d\n", __FUNCTION__, __LINE__,fw_upgrade);
+
+ if (fw_upgrade) {
+ pr_info("%s: Starting firmware upgrade\n", __func__);
+ rc = cyttspfw_flash_firmware(ts, data, data_len);
+ if (rc < 0)
+ pr_err("%s: firmware upgrade failed\n", __func__);
+ else
+ pr_info("%s: lusongbai firmware upgrade success\n", __func__);
+ }
+ LOG_TP();
+
+ /* enter bootloader idle mode */
+ cyttsp_soft_reset(ts);
+ LOG_TP();
+
+ /* exit bootloader mode */
+ cyttsp_exit_bl_mode(ts);
+ LOG_TP();
+
+ msleep(100);
+ /* set sysinfo details */
+ cyttsp_set_sysinfo_mode(ts);
+ LOG_TP();
+
+ /* enter application mode */
+ cyttsp_set_opmode(ts);
+ LOG_TP();
+
+ if((fw_upgrade == 1) && (rc >= 0))
+ {
+ u8 tmpData[18] = {0};
+ rc = i2c_smbus_read_i2c_block_data(ts->client,CY_REG_BASE,18, tmpData);
+ if(rc < 0)
+ {
+ printk(KERN_ERR"cyttspfw_flash_start read version and module error\n");
+ }
+ else
+ {
+ cyttsp_vendor_id=tmpData[16];
+ cyttsp_firmware_version = tmpData[17];
+ printk(KERN_ERR"cyttspfw_flash_start tp module is:%x firmware version is %x:\n",tmpData[16],tmpData[17]);
+ }
+ }
+ LOG_TP();
+
+ /* enable interrupts */
+#if 0
+ if (ts->client->irq == 0)
+ mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+ else
+ enable_irq(ts->client->irq);
+#endif
+ LOG_TP();
+
+}
+
+static void cyttspfw_upgrade_start(struct cyttsp *ts, const u8 *data,
+ int data_len, bool force)
+{
+ int i, j;
+ u8 *buf;
+
+ buf = kzalloc(REC_LINE_SIZE + 1, GFP_KERNEL);
+ if (!buf) {
+ pr_err("%s: no memory\n", __func__);
+ return;
+ }
+
+ for (i = 0, j = 0; i < data_len; i++, j++) {
+ if ((data[i] == REC_START_CHR) && j) {
+ buf[j] = 0;
+ j = 0;
+ if (!strncmp(buf, ID_INFO_REC, strlen(ID_INFO_REC))) {
+ cyttspfw_flash_start(ts, data, data_len,
+ buf, force);
+ break;
+ }
+ }
+ buf[j] = data[i];
+ }
+
+ /* check in the last record of firmware */
+ if (j) {
+ buf[j] = 0;
+ if (!strncmp(buf, ID_INFO_REC, strlen(ID_INFO_REC))) {
+ cyttspfw_flash_start(ts, data, data_len,
+ buf, force);
+ }
+ }
+
+ kfree(buf);
+}
+static bool cyttsp_IsTpInBootloader(struct cyttsp *ts)
+{
+ int retval = -1;
+ u8 tmpData[18] = {0};
+ retval = i2c_smbus_read_i2c_block_data(ts->client,CY_REG_BASE,18, tmpData);
+ if(retval < 0)
+ {
+ printk(KERN_ERR"cyttsp_IsTpInBootloader read version and module error\n");
+ return false;
+ }
+ else
+ {
+ retval = 0;
+ retval = ((tmpData[1] & 0x10) >> 4);
+
+ printk(KERN_ERR"cyttsp_IsTpInBootloader tmpData[1]:%x retval:%x\n",tmpData[1],retval);
+ }
+ if(retval == 0)
+ {
+ return false;
+ }
+ return true;
+
+}
+
+const u8 fw_hex_of[] = {
+// #include "BOOT_AG500_OF_DW_2802_V2_20120702.i"
+// #include "BOOT_AG500_OF_DW_2803_V3_20120711.i"
+};
+const u8 fw_hex_hhx[] = {
+// #include "BOOT_AG500_F5_HHX_2503_V3_20120711.i"
+};
+struct cyttsp cust_ts;
+const u8* fw_hex = fw_hex_hhx;
+extern void cyttsp_print_reg(struct i2c_client *client);
+
+int read_vender_id(void)
+{
+ char buffer[32];
+ int ret =0;
+
+ ret = i2c_smbus_read_i2c_block_data(cust_ts.client, 0x00, 24, &(buffer[0]));
+ if(ret<0)
+ {
+ return -1;
+ }
+ cyttsp_vendor_id = buffer[3];
+ printk("++++cyttp read_vender_id=0x%x\n", cyttsp_vendor_id);
+ cyttsp_print_reg(cust_ts.client);
+ return 0;
+}
+
+
+void cyttsp_fw_upgrade(void)
+{
+ struct cyttsp *ts = &cust_ts;
+ bool force = 1;
+ /* check and start upgrade */
+ //if upgrade failed we should force upgrage when next power up
+
+ if(read_vender_id() != 0)
+ {
+ printk("++++cyttspfw_upgrade read vender id failed!\n");
+ return;
+ }
+ switch(cyttsp_vendor_id)
+ {
+ case 0x28:
+ fw_hex = fw_hex_of;
+ break;
+ case 0x25:
+ fw_hex = fw_hex_hhx;
+ break;
+ case 0x0:
+ break;
+ default:
+ break;
+ }
+
+ if(cyttsp_IsTpInBootloader(ts) == true)
+ {
+ LOG_TP();
+ printk(KERN_ERR"cyttspfw_upgrade we should force upgrade tp fw\n");
+ cyttspfw_upgrade_start(ts, fw_hex,
+ sizeof(fw_hex_of), true);
+ }
+ else
+ {
+ LOG_TP();
+ cyttspfw_upgrade_start(ts, fw_hex,
+ sizeof(fw_hex_of), force);
+ }
+}
+
+#endif
+
+
+
+static int cyttsp_putbl(struct cyttsp *ts, int show,
+ int show_status, int show_version, int show_cid)
+{
+ int retval = CY_OK;
+
+ int num_bytes = (show_status * 3) + (show_version * 6) + (show_cid * 3);
+
+ if (show_cid)
+ num_bytes = sizeof(struct cyttsp_bootloader_data_t);
+ else if (show_version)
+ num_bytes = sizeof(struct cyttsp_bootloader_data_t) - 3;
+ else
+ num_bytes = sizeof(struct cyttsp_bootloader_data_t) - 9;
+ LOG_TP();
+
+ if (show) {
+ retval = i2c_smbus_read_i2c_block_data(ts->client,
+ CY_REG_BASE, num_bytes, (u8 *)&g_bl_data);
+
+ {
+ int i = 0;
+ printk("cyttsp_putbl:");
+ for(i=0; i<num_bytes; i++)
+ printk(" 0x%x",*((u8 *)&g_bl_data+i));
+ printk("\n");
+ }
+ if (show_status) {
+ cyttsp_debug("BL%d: f=%02X s=%02X err=%02X bl=%02X%02X bld=%02X%02X\n", \
+ show, \
+ g_bl_data.bl_file, \
+ g_bl_data.bl_status, \
+ g_bl_data.bl_error, \
+ g_bl_data.blver_hi, g_bl_data.blver_lo, \
+ g_bl_data.bld_blver_hi, g_bl_data.bld_blver_lo);
+ }
+ if (show_version) {
+ cyttsp_debug("BL%d: ttspver=0x%02X%02X appid=0x%02X%02X appver=0x%02X%02X\n", \
+ show, \
+ g_bl_data.ttspver_hi, g_bl_data.ttspver_lo, \
+ g_bl_data.appid_hi, g_bl_data.appid_lo, \
+ g_bl_data.appver_hi, g_bl_data.appver_lo);
+ }
+ if (show_cid) {
+ cyttsp_debug("BL%d: cid=0x%02X%02X%02X\n", \
+ show, \
+ g_bl_data.cid_0, \
+ g_bl_data.cid_1, \
+ g_bl_data.cid_2);
+ }
+ }
+ //LOG_TP();
+
+ return retval;
+}
+
+
+#ifndef CYTTSP_SUPPORT_SYS_NODE
+static void cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+ int retval, tries = 0;
+
+ do {
+ retval = i2c_smbus_write_i2c_block_data(ts->client,
+ CY_REG_BASE, sizeof(bl_cmd), bl_cmd);
+ if (retval < 0)
+ msleep(20);
+ } while (tries++ < 10 && (retval < 0));
+}
+#endif
+
+
+
diff --git a/drivers/input/touchscreen/cyp140_ts/debug.txt b/drivers/input/touchscreen/cyp140_ts/debug.txt
new file mode 100755
index 00000000..a424c186
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/debug.txt
@@ -0,0 +1,3 @@
+/ # wmtenv get wmt.io.touch
+1:cyp140:7:600:1024:4:0:1:-1:5 //it seems ok
+
diff --git a/drivers/input/touchscreen/cyp140_ts/wmt_ts.c b/drivers/input/touchscreen/cyp140_ts/wmt_ts.c
new file mode 100755
index 00000000..d7234ac3
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/wmt_ts.c
@@ -0,0 +1,1094 @@
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+//#include <asm/semaphore.h>
+#include <linux/proc_fs.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+
+#include "wmt_ts.h"
+//#include "zet6221_ts.h"
+
+/////////////////////////////////////////////////////////////////
+
+// commands for ui
+#define TS_IOC_MAGIC 't'
+
+#define TS_IOCTL_CAL_START _IO(TS_IOC_MAGIC, 1)
+#define TS_IOCTL_CAL_DONE _IOW(TS_IOC_MAGIC, 2, int*)
+#define TS_IOCTL_GET_RAWDATA _IOR(TS_IOC_MAGIC, 3, int*)
+#define TS_IOCTL_CAL_QUIT _IOW(TS_IOC_MAGIC, 4, int*)
+#define TS_IOCTL_AUTO_CALIBRATION _IOW(TS_IOC_MAGIC, 5, int*)
+#define TS_IOC_MAXNR 5
+
+#define TP_INFOR_ARRAY_SIZE (sizeof(l_tpinfor)/sizeof(l_tpinfor[1]))
+//
+#define TS_MAJOR 11
+#define TS_DRIVER_NAME "wmtts_touch"
+#define TS_NAME "wmtts"
+#define WMTTS_PROC_NAME "wmtts_config"
+
+#define EXT_GPIO0 0
+#define EXT_GPIO1 1
+#define EXT_GPIO2 2
+#define EXT_GPIO3 3
+#define EXT_GPIO4 4
+#define EXT_GPIO5 5
+#define EXT_GPIO6 6
+#define EXT_GPIO7 7
+
+typedef struct {
+ int a1;
+ int b1;
+ int c1;
+ int a2;
+ int b2;
+ int c2;
+ int delta;
+}CALIBRATION_PARAMETER, *PCALIBRATION_PARAMETER;
+
+
+static int irq_gpio = 7;//!!!2012-12-28
+static int rst_gpio = 4;//
+static int panelres_x;
+static int panelres_y;
+static DECLARE_WAIT_QUEUE_HEAD(queue);
+static CALIBRATION_PARAMETER g_CalcParam;
+static TS_EVENT g_evLast;
+static struct mutex cal_mutex;
+static DECLARE_WAIT_QUEUE_HEAD(ts_penup_wait_queue);
+static int lcd_exchg = 0;
+
+static struct class* l_dev_class = NULL;
+static struct device *l_clsdevice = NULL;
+extern struct wmtts_device cyp140_tsdev;
+static struct wmtts_device* l_tsdev = &cyp140_tsdev;
+struct proc_dir_entry* l_tsproc = NULL;
+static struct i2c_client *l_client=NULL;
+static int l_penup = 1; // 1-pen up,0-pen down
+
+struct tp_infor
+{
+ //enum tp_type type;
+ char name[64];
+ //unsigned int i2caddr;
+ unsigned int xaxis; //0: x,1: x swap with y
+ unsigned int xdir; // 1: positive,-1: revert
+ unsigned int ydir; // 1: positive,-1: revert
+ unsigned int max_finger_num;
+};
+
+static int l_tpindex = -1;
+static struct tp_infor l_tpinfor[1];
+
+/////////////////////////////////////////////////////
+// function declare
+/////////////////////////////////////////////////////
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data );
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+///////////////////////////////////////////////////////////////////////
+void TouchPanelCalibrateAPoint(
+ int UncalX, //@PARM The uncalibrated X coordinate
+ int UncalY, //@PARM The uncalibrated Y coordinate
+ int *pCalX, //@PARM The calibrated X coordinate
+ int *pCalY //@PARM The calibrated Y coordinate
+ )
+{
+ int x, y;
+ mutex_lock(&cal_mutex);
+ x = (g_CalcParam.a1 * UncalX + g_CalcParam.b1 * UncalY +
+ g_CalcParam.c1) / g_CalcParam.delta;
+ y = (g_CalcParam.a2 * UncalX + g_CalcParam.b2 * UncalY +
+ g_CalcParam.c2) / g_CalcParam.delta;
+
+//klog("afer(%d,%d)(%d,%d)\n", x,y,panelres_x,panelres_y);
+ if ( x < 0 )
+ x = 0;
+
+ if ( y < 0 )
+ y = 0;
+ if (x >= panelres_x)
+ x = panelres_x-1;
+ if (y >= panelres_y)
+ y = panelres_y-1;
+
+ *pCalX = x;
+ *pCalY = y;
+ mutex_unlock(&cal_mutex);
+ return;
+}
+
+int wmt_ts_if_updatefw(void)
+{
+ /*if ((!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7dgntpc0350")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7zcc1950")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_8dgntpc0406")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7adc700148")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_8xdc806")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7tp070005q8")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7yiheng7002")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7atc7031"))||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7xclg7027a")) ||
+ (!strcmp(l_tpinfor[l_tpindex].name,"ZET6221_7est07000416")))
+ {
+ return 1;
+ }
+
+ return 0;
+ */
+
+ struct file *fp;
+ char filepath[128];
+
+ sprintf(filepath,"/lib/firmware/%s_fw.ts",l_tpinfor[l_tpindex].name);
+
+ fp = filp_open(filepath, O_RDONLY, 0);
+ if (IS_ERR(fp))
+ {
+ //printk("create file error\n");
+ return 0;
+ }
+ filp_close(fp, NULL);
+ return 1;
+}
+
+unsigned int wmt_ts_get_xaxis(void)
+{
+ return l_tpinfor[l_tpindex].xaxis;
+}
+
+unsigned int wmt_ts_get_xdir(void)
+{
+ return l_tpinfor[l_tpindex].xdir;
+}
+
+unsigned int wmt_ts_get_ydir(void)
+{
+ return l_tpinfor[l_tpindex].ydir;
+}
+
+unsigned int wmt_ts_get_maxfingernum(void)
+{
+ return l_tpinfor[l_tpindex].max_finger_num;
+}
+
+#if 0
+static int parse_firmwarefile(char* filedata, unsigned char** firmarr, int maxlen)
+{
+ char endflag[]="/* End flag */";
+ char* p = filedata;
+ int i = 0;
+ int j = 0;
+ char* s = NULL;
+
+ s = p;
+ // calculate the number of array
+ while (strncmp(p,endflag,strlen(endflag)))
+ {
+ if (!strncmp(p,"0x",strlen("0x")))
+ {
+ i++;
+ }
+ p++;
+ };
+ dbg("the number of arry:0x%x\n", i);
+ // alloc the memory for array
+ j = i + i%4;
+ *firmarr = kzalloc(sizeof(unsigned char)*j, GFP_KERNEL);
+ // parse the value of array
+ p = s;
+ j = 0;
+ while (strncmp(p,endflag,strlen(endflag)))
+ {
+ if (!strncmp(p,"0x",strlen("0x")))
+ {
+ //dbg("find 0x!\n");
+ sscanf(p,"0x%x", &((*firmarr)[j]));
+ //dbg("arry[0x%x]:%x\n",j,(*firmarr)[j]);
+ j++;
+ p+=4;
+ } else {
+ p++;
+ }
+ //p = strchr(p,'}');
+ if (j>=i-2)
+ {
+ dbg("%s",p);
+ }
+
+ };
+ if (i != j)
+ {
+ errlog("Error parsing file(the number of arry not match)!\n");
+ return -1;
+ };
+ dbg("paring firmware file end.\n");
+ return i;
+}
+
+//filepath:the path of firmware file;
+//firmdata:store the data from firmware file;
+//return:0-successful,negative-parsing error.
+int read_firmwarefile(char* filepath, unsigned char** firmdata)
+{
+ struct file *fp;
+ mm_segment_t fs;
+ loff_t pos;
+ char* data = NULL;
+ long fsize;
+ int alloclen = 2052*(8*5+2)+200;
+ int i = 0;
+
+ klog("ts firmware file:%s\n",filepath);
+ data = kzalloc(alloclen, GFP_KERNEL);
+ if (data == NULL)
+ {
+ errlog("Error when alloc memory for firmware file!\n");
+ return -1;
+ }
+ fp = filp_open(filepath, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ printk("create file error\n");
+ goto error_flip_open;
+ }
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ pos = 0;
+ fsize = vfs_read(fp, data, alloclen, &pos);
+ dbg("filesize:0x%x,alloclen:0x%x\n",fsize,alloclen);
+ if (fsize <= 0)
+ {
+ errlog("alloc size is too small.\n");
+ goto error_vfs_read;
+ }
+ i = parse_firmwarefile(data,firmdata,fsize);
+ // Check the parsing and ori file
+ /* for (i=0;i < maxlen; i++)
+ {
+ if (firmdata[i]!=nvctp_BinaryFile_default[i])
+ {
+ errlog("Don't match:i=%x,parse:0x%x,ori:0x%x\n",i,firmdata[i], nvctp_BinaryFile_default[i]);
+ break;
+ }
+ };
+ dbg("parsing match with ori.\n");
+ */
+ filp_close(fp, NULL);
+ set_fs(fs);
+ kfree(data);
+ dbg("success to read firmware file!\n");;
+
+ //sscanf(data,"%hd %hd %hd",&offset.u.x,&offset.u.y,&offset.u.z);
+ return i;
+error_vfs_read:
+ filp_close(fp, NULL);
+ set_fs(fs);
+error_flip_open:
+ kfree(data);
+ return -1;
+}
+#endif
+
+void wmt_ts_get_firmwname(char* firmname)
+{
+ sprintf(firmname,"/lib/firmware/%s_fw.ts",l_tpinfor[l_tpindex].name);
+}
+
+ int wmt_ts_get_irqgpnum(void)
+{
+ return irq_gpio;
+}
+
+int wmt_ts_get_resetgpnum(void)
+{
+ return rst_gpio;
+}
+
+ int wmt_ts_get_lcdexchg(void)
+{
+ return lcd_exchg;
+}
+
+int wmt_ts_get_resolvX(void)
+{
+ return panelres_x;
+}
+
+int wmt_ts_get_resolvY(void)
+{
+ return panelres_y;
+}
+
+//up:1-pen up,0-pen down
+void wmt_ts_set_penup(int up)
+{
+ l_penup = up;
+}
+
+//
+int wmt_ts_wait_penup(void)
+{
+ int ret = wait_event_interruptible(
+ ts_penup_wait_queue,
+ (1==l_penup));
+ return ret;
+}
+
+// return:1-pen up,0-pen dwon
+int wmt_ts_ispenup(void)
+{
+ return l_penup;
+}
+
+
+void wmt_ts_wakeup_penup(void)
+{
+ wake_up(&ts_penup_wait_queue);
+}
+
+int wmt_is_tsirq_enable(void)
+{
+ int val = 0;
+ int num = irq_gpio;
+
+ if(num > 11)
+ return 0;
+
+ if(num<4)
+ val = REG32_VAL(__GPIO_BASE+0x0300) & (1<<(num*8+7));
+ else if(num >= 4 && num < 8)
+ val = REG32_VAL(__GPIO_BASE+0x0304) & (1<<((num-4)*8+7));
+ else
+ val = REG32_VAL(__GPIO_BASE+0x0308) & (1<<((num-8)*8+7));
+
+ return val?1:0;
+
+}
+
+int wmt_is_tsint(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return 0;
+ }
+ return (REG32_VAL(__GPIO_BASE+0x0360) & (1<<num)) ? 1: 0;
+}
+
+void wmt_clr_int(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return;
+ }
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num;
+}
+
+void wmt_tsreset_init(void)
+{
+ int num = rst_gpio;
+/*
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num);//&= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<num); // out low
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<num); //output enable
+ msleep(10);
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<num); // out high
+*/
+ gpio_direction_output(num, 0);
+ msleep(10);
+ gpio_set_value(num, 1);
+}
+/*
+// enable:0-disable,1-enable
+void wmt_enable_rst_pull(int enable)
+{
+ if (enable)
+ {
+ REG32_VAL(__GPIO_BASE+0x0480) |= (1<<rst_gpio); //enable pull up/down
+ } else {
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<rst_gpio); //disable pull up/down
+ }
+}
+*/
+
+// enable:0-disable,1-enable
+void wmt_disable_rst_pull(void)
+{
+ wmt_gpio_setpull(rst_gpio, WMT_GPIO_PULL_NONE);
+
+}
+
+// up:0-pull down,1-pull up
+void wmt_set_rst_pull(int up)
+{
+ wmt_gpio_setpull(rst_gpio, up ? WMT_GPIO_PULL_UP : WMT_GPIO_PULL_DOWN);
+}
+
+// high:0-low level,1-high level
+void wmt_rst_output(int high)
+{
+ if (high)
+ gpio_direction_output(rst_gpio, 1);
+ else
+ gpio_direction_output(rst_gpio, 0);
+}
+
+void wmt_rst_input(void)
+{
+ gpio_direction_input(rst_gpio);
+}
+
+void wmt_set_intasgp(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<irq_gpio); //enable gpio
+}
+
+// val:1--high,0-low
+void wmt_intgp_out(int val)
+{
+ if (val)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<irq_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<irq_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<irq_gpio); //set output
+}
+
+void wmt_ts_set_irqinput(void)
+{
+ int num = irq_gpio;
+
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+}
+
+unsigned int wmt_ts_irqinval(void)
+{
+ return REG32_VAL(__GPIO_BASE+0x0000)&(1<<irq_gpio);
+}
+
+int wmt_set_gpirq(int type)
+{
+ int shift;
+ int offset;
+ unsigned long reg;
+ int num = irq_gpio;
+
+ if(num >11)
+ return -1;
+ //if (num > 9)
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; // gpio10,11 as gpio
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num);//|=(1<<num);// //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull up
+ //REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down .debug pulldown 2013-5-8
+ REG32_VAL(__GPIO_BASE+0x0480) |= (1<<num); //enable pull up/down 2013-1-4 replace 2013-5-7 default enable!!
+ //REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down
+ //set gpio irq triger type
+ if(num < 4){//[0,3]
+ shift = num;
+ offset = 0x0300;
+ }else if(num >= 4 && num < 8){//[4,7]
+ shift = num-4;
+ offset = 0x0304;
+ }else{// [8,11]
+ shift = num-8;
+ offset = 0x0308;
+ }
+
+ reg = REG32_VAL(__GPIO_BASE + offset);
+
+ switch(type){
+ case IRQ_TYPE_LEVEL_LOW:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ default://both edge
+ reg |= (1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+
+ }
+ //reg |= 1<<(shift*8+7);//enable interrupt
+ reg &= ~(1<<(shift*8+7)); //disable int
+
+ REG32_VAL(__GPIO_BASE + offset) = reg;
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num; //clear interrupt status
+ msleep(5);
+ return 0;
+}
+
+int wmt_enable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) |= 1<<(num*8+7); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) |= 1<<((num-4)*8+7); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) |= 1<<((num-8)*8+7); //enable interrupt
+
+ return 0;
+}
+
+int wmt_disable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) &= ~(1<<(num*8+7)); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) &= ~(1<<((num-4)*8+7)); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) &= ~(1<<((num-8)*8+7)); //enable interrupt
+
+ return 0;
+}
+
+
+int wmt_get_tsirqnum(void)
+{
+ return IRQ_GPIO;
+}
+
+
+int wmt_ts_set_rawcoord(unsigned short x, unsigned short y)
+{
+ g_evLast.x = x;
+ g_evLast.y = y;
+ //dbg("raw(%d,%d)*\n", x, y);
+ return 0;
+}
+
+static void wmt_ts_platform_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device wmt_ts_plt_device = {
+ .name = TS_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = wmt_ts_platform_release,
+ },
+// .num_resources = ARRAY_SIZE(wm9715_ts_resources),
+// .resource = wm9715_ts_resources,
+};
+
+static int wmt_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dbg("ts suspend....\n");
+ return l_tsdev->suspend(pdev, state);
+}
+static int wmt_ts_resume(struct platform_device *pdev)
+{
+ dbg("ts resume....\n");
+ return l_tsdev->resume(pdev);
+}
+
+static int wmt_ts_probe(struct platform_device *pdev)
+{
+ l_tsproc= create_proc_entry(WMTTS_PROC_NAME, 0666, NULL/*&proc_root*/);
+ if (l_tsproc != NULL)
+ {
+ l_tsproc->read_proc = ts_readproc;
+ l_tsproc->write_proc = ts_writeproc;
+ }
+
+ if (l_tsdev->probe != NULL)
+ return l_tsdev->probe(pdev);
+ else
+ return 0;
+}
+
+static int wmt_ts_remove(struct platform_device *pdev)
+{
+ if (l_tsproc != NULL)
+ {
+ remove_proc_entry(WMTTS_PROC_NAME, NULL);
+ l_tsproc = NULL;
+ }
+
+ if (l_tsdev->remove != NULL)
+ return l_tsdev->remove(pdev);
+ else
+ return 0;
+}
+
+static struct platform_driver wmt_ts_plt_driver = {
+ .driver = {
+ .name = TS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = wmt_ts_probe,
+ .remove = wmt_ts_remove,
+ .suspend = wmt_ts_suspend,
+ .resume = wmt_ts_resume,
+};
+
+static int wmt_ts_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+
+ klog("wmt ts driver opening...\n");
+
+ //ts_clear();
+ //try_module_get(THIS_MODULE);
+
+ return ret;
+}
+
+static int wmt_ts_close(struct inode *inode, struct file *filp)
+{
+ klog("wmt ts driver closing...\n");
+ //ts_clear();
+ //module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static unsigned int wmt_ts_poll(struct file *filp, struct poll_table_struct *wait)
+{
+#if 0
+ poll_wait(filp, &queue, wait);
+ if ( head != tail )
+ return (POLLIN | POLLRDNORM);
+#endif
+ return 0;
+}
+
+static long wmt_ts_ioctl(/*struct inode * node,*/ struct file *dev, unsigned int cmd, unsigned long arg)
+{
+ int nBuff[7];
+ char env_val[96]={0};
+ //dbg("wmt_ts_ioctl(node=0x%p, dev=0x%p, cmd=0x%08x, arg=0x%08lx)\n", node, dev, cmd, arg);
+
+ if (_IOC_TYPE(cmd) != TS_IOC_MAGIC){
+ dbg("CMD ERROR!");
+ return -ENOTTY;
+ }
+
+ if (_IOC_NR(cmd) > TS_IOC_MAXNR){
+ dbg("NO SUCH IO CMD!\n");
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+
+ case TS_IOCTL_CAL_DONE:
+ klog("wmt_ts_ioctl: TS_IOCTL_CAL_DONE\n");
+ copy_from_user(nBuff, (unsigned int*)arg, 7*sizeof(int));
+
+ mutex_lock(&cal_mutex);
+ g_CalcParam.a1 = nBuff[0];
+ g_CalcParam.b1 = nBuff[1];
+ g_CalcParam.c1 = nBuff[2];
+ g_CalcParam.a2 = nBuff[3];
+ g_CalcParam.b2 = nBuff[4];
+ g_CalcParam.c2 = nBuff[5];
+ g_CalcParam.delta = nBuff[6];
+
+ if(g_CalcParam.delta == 0)
+ g_CalcParam.delta =1;//avoid divide by zero
+
+ mutex_unlock(&cal_mutex);
+
+ sprintf(env_val,"%d %d %d %d %d %d %d",nBuff[0],nBuff[1],nBuff[2],nBuff[3],nBuff[4],nBuff[5],nBuff[6]);
+ wmt_setsyspara("wmt.io.ts.2dcal", env_val);
+ klog("Tsc calibrate done data: [%s]\n",env_val);
+
+ return 0;
+
+
+ case TS_IOCTL_GET_RAWDATA:
+ // wait for point up
+ dbg("test wait_penup\n");
+ //l_tsdev->wait_penup(&gt811_tsdev);
+ klog("wmt_ts_ioctl: TS_IOCTL_GET_RAWDATA\n");
+ wmt_ts_wait_penup();
+
+ nBuff[0] = g_evLast.x;
+ nBuff[1] = g_evLast.y;
+ copy_to_user((unsigned int*)arg, nBuff, 2*sizeof(int));
+ klog("raw data: x=%d, y=%d\n", nBuff[0], nBuff[1]);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t wmt_ts_read(struct file *filp, char *buf, size_t count, loff_t *l)
+{
+ // read firmware file
+
+ return 0;
+}
+
+
+static struct file_operations wmt_ts_fops = {
+ .read = wmt_ts_read,
+ .poll = wmt_ts_poll,
+ .unlocked_ioctl = wmt_ts_ioctl,
+ .open = wmt_ts_open,
+ .release = wmt_ts_close,
+};
+
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data )
+{
+ int calibrate = 0;
+ int val = 0;
+
+ if (sscanf(buffer, "calibrate=%d\n", &calibrate))
+ {
+ if (1 == calibrate)
+ {
+ if((l_tsdev->capacitance_calibrate != NULL) &&
+ (0 == l_tsdev->capacitance_calibrate()))
+ {
+ printk(KERN_ALERT "%s calibration successfully!\n", l_tsdev->ts_id);
+ } else {
+ printk(KERN_ALERT "%s calibration failed!\n", l_tsdev->ts_id);
+ }
+ }
+ } else if (sscanf(buffer, "reset=%d\n", &val))
+ {
+
+ }
+ return count;
+}
+
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len = sprintf(page,
+ "echo calibrate=1 > /proc/wmtts_config to calibrate ts.\n");
+ return len;
+}
+
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 96, i = 0;
+ char retval[200] = {0},*p=NULL,*s=NULL;
+ //int nBuff[7] = {0};
+ int Enable=0; //Gpio=0,PX=0,PY=0;
+ int val,val1;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ errlog("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ memset(l_tpinfor,0,sizeof(l_tpinfor[0]));
+ p = retval;
+ sscanf(p,"%d:", &Enable);
+ p = strchr(p,':');p++;
+ s = strchr(p,':');
+ strncpy(l_tpinfor[0].name,p, (s-p));
+ p = s+1;
+ dbg("ts_name=%s\n", l_tpinfor[0].name);
+
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d",
+ &irq_gpio,&panelres_x,&panelres_y,&rst_gpio,
+ &(l_tpinfor[0].xaxis),&(l_tpinfor[0].xdir),&(l_tpinfor[0].ydir),
+ &(l_tpinfor[0].max_finger_num));
+ if (ret != 8)
+ {
+ dbg("Wrong format ts u-boot param(%d)!\n",ret);
+ return -ENODEV;
+ }
+ //check touch enable
+ if(Enable == 0){
+ errlog("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ /*p = strchr(retval,':');
+ p++;
+ if(strncmp(p, l_tsdev->ts_id,strlen(l_tsdev->ts_id))){//check touch ID
+ //errlog(" %s!=====\n", l_tsdev->ts_id);
+ return -ENODEV;
+ }*/
+
+ //sscanf(p,"%s:", );
+#if 1
+ if (strstr(l_tpinfor[0].name, l_tsdev->ts_id) == NULL)// cyp140
+ {
+ dbg("Can't find %s!\n", l_tsdev->ts_id);
+ return -ENODEV;
+ }
+#endif //comment 2012-12-31
+ l_tpindex = 0;
+
+/*
+ p = strchr(p,':');
+ p++;
+ sscanf(p,"%d:%d:%d:%d",&irq_gpio,&panelres_x,&panelres_y,&rst_gpio);
+
+ irq_gpio = Gpio;
+ panelres_x = PX;
+ panelres_y = PY;
+ */
+ klog("p.x = %d, p.y = %d, gpio=%d, resetgpio=%d,xaxis=%d,xdir=%d,ydri=%d,maxfingernum=%d\n",
+ panelres_x, panelres_y, irq_gpio, rst_gpio,
+ l_tpinfor[0].xaxis,l_tpinfor[0].xdir,l_tpinfor[0].ydir,
+ l_tpinfor[0].max_finger_num);
+
+ // parse touch key param
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.tskeyindex", retval, &len);
+ if(ret){
+ dbg("no touch key!\n");
+ //return -EIO;
+ } else {
+ p = retval;
+ // the number of touch key
+ sscanf(retval,"%d:", &val);
+ dbg("tskey num:%d\n",val);
+ p = strchr(p,':');
+ p++;
+ // touch key range
+ for (i=0;i<val;i++)
+ {
+ sscanf(p,"%d:",&val1);
+ p = strchr(p,':');
+ p++;
+ //zet6221_set_tskey(i, val1); //own tp ic 2012-12-31??!!!
+ dbg("key%d:(%d)\n",i,val1);
+ };
+ }
+
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ lcd_exchg = 1;
+ }
+
+/* memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.ts.2dcal", retval, &len);
+ if(ret){
+ errlog("Read env wmt.io.ts.2dcal Failed.\n ");
+ //return -EIO;
+ }
+ i = 0;
+ while(i < sizeof(retval)){
+ if(retval[i]==' ' || retval[i]==',' || retval[i]==':')
+ retval[i] = '\0';
+ i++;
+ }
+
+ i = 0;
+ p = retval;
+ while(i<7 && p < (retval + sizeof(retval))){
+ if(*p == '\0')
+ p++;
+ else{
+ sscanf(p,"%d",&nBuff[i]);
+ //printk("%d\n",nBuff[i]);
+ p=p+strlen(p);
+ i++;
+ }
+ }
+ //sscanf(retval,"%d %d %d %d %d %d %d %d",&nBuff[0],&nBuff[1],&nBuff[2],&nBuff[3],&nBuff[4],&nBuff[5],&nBuff[6]);
+ dbg("Tsc calibrate init data: [%d %d %d %d %d %d %d]\n",nBuff[0],nBuff[1],nBuff[2],nBuff[3],nBuff[4],nBuff[5],nBuff[6]);
+
+ g_CalcParam.a1 = nBuff[0];
+ g_CalcParam.b1 = nBuff[1];
+ g_CalcParam.c1 = nBuff[2];
+ g_CalcParam.a2 = nBuff[3];
+ g_CalcParam.b2 = nBuff[4];
+ g_CalcParam.c2 = nBuff[5];
+ g_CalcParam.delta = nBuff[6];
+
+ if(g_CalcParam.delta == 0)
+ g_CalcParam.delta =1;//avoid divide by zero
+*/
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = WMT_TS_I2C_NAME,
+ .flags = 0x00,
+ .addr = WMT_TS_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(1);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+struct i2c_client* ts_get_i2c_client(void)
+{
+ return l_client;
+}
+
+static int __init wmt_ts_init(void)
+{
+ int ret = 0;
+
+
+
+ if(wmt_check_touch_env())
+ return -ENODEV;
+
+ //ts_i2c_board_info.addr = l_tpinfor[l_tpindex].i2caddr;
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+ mutex_init(&cal_mutex);
+
+ if (l_tsdev->init() < 0){
+ dbg("Errors to init %s ts IC!!!\n", l_tsdev->ts_id);
+ ret = -1;
+ goto err_init;
+ }
+ // Create device node
+ if (register_chrdev (TS_MAJOR, TS_NAME, &wmt_ts_fops)) {
+ printk (KERN_ERR "wmt touch: unable to get major %d\n", TS_MAJOR);
+ return -EIO;
+ }
+
+ l_dev_class = class_create(THIS_MODULE, TS_NAME);
+ if (IS_ERR(l_dev_class)){
+ ret = PTR_ERR(l_dev_class);
+ printk(KERN_ERR "Can't class_create touch device !!\n");
+ return ret;
+ }
+ l_clsdevice = device_create(l_dev_class, NULL, MKDEV(TS_MAJOR, 0), NULL, TS_NAME);
+ if (IS_ERR(l_clsdevice)){
+ ret = PTR_ERR(l_clsdevice);
+ printk(KERN_ERR "Failed to create device %s !!!",TS_NAME);
+ return ret;
+ }
+
+ // register device and driver of platform
+ ret = platform_device_register(&wmt_ts_plt_device);
+ if(ret){
+ errlog("wmt ts plat device register failed!\n");
+ return ret;
+ }
+ ret = platform_driver_register(&wmt_ts_plt_driver);
+ if(ret){
+ errlog("can not register platform_driver_register\n");
+ platform_device_unregister(&wmt_ts_plt_device);
+ return ret;
+ }
+
+ klog("%s driver init ok!\n",l_tsdev->ts_id);
+ return 0;
+err_init:
+ ts_i2c_unregister_device();
+ return ret;
+}
+
+static void __exit wmt_ts_exit(void)
+{
+ dbg("%s\n",__FUNCTION__);
+
+ l_tsdev->exit();
+ platform_driver_unregister(&wmt_ts_plt_driver);
+ platform_device_unregister(&wmt_ts_plt_device);
+ device_destroy(l_dev_class, MKDEV(TS_MAJOR, 0));
+ unregister_chrdev(TS_MAJOR, TS_NAME);
+ class_destroy(l_dev_class);
+ mutex_destroy(&cal_mutex);
+ ts_i2c_unregister_device();
+}
+
+
+module_init(wmt_ts_init);
+module_exit(wmt_ts_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/cyp140_ts/wmt_ts.h b/drivers/input/touchscreen/cyp140_ts/wmt_ts.h
new file mode 100755
index 00000000..11261348
--- /dev/null
+++ b/drivers/input/touchscreen/cyp140_ts/wmt_ts.h
@@ -0,0 +1,120 @@
+
+#ifndef WMT_TSH_201010191758
+#define WMT_TSH_201010191758
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <linux/i2c.h>
+
+
+//#define DEBUG_WMT_TS
+#ifdef DEBUG_WMT_TS
+#undef dbg
+#define dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args)
+
+//#define dbg(fmt, args...) if (kpadall_isrundbg()) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args)
+
+#else
+#define dbg(fmt, args...)
+#endif
+
+#undef errlog
+#undef klog
+#define errlog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+#define klog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+
+#define WMT_TS_I2C_NAME "cyp140-ts"
+#define WMT_TS_I2C_ADDR 0x24 //0x76
+
+
+
+
+
+//////////////////////////////data type///////////////////////////
+typedef struct {
+ short pressure;
+ short x;
+ short y;
+ //short millisecs;
+} TS_EVENT;
+
+struct wmtts_device
+{
+ //data
+ char* driver_name;
+ char* ts_id;
+ //function
+ int (*init)(void);
+ int (*probe)(struct platform_device *platdev);
+ int (*remove)(struct platform_device *pdev);
+ void (*exit)(void);
+ int (*suspend)(struct platform_device *pdev, pm_message_t state);
+ int (*resume)(struct platform_device *pdev);
+ int (*capacitance_calibrate)(void);
+ int (*wait_penup)(struct wmtts_device*tsdev); // waiting untill penup
+ int penup; // 0--pendown;1--penup
+
+};
+
+//////////////////////////function interface/////////////////////////
+extern int wmt_ts_get_irqgpnum(void);
+extern int wmt_ts_iscalibrating(void);
+extern int wmt_ts_get_resolvX(void);
+extern int wmt_ts_get_resolvY(void);
+extern int wmt_ts_set_rawcoord(unsigned short x, unsigned short y);
+extern int wmt_set_gpirq(int type);
+extern int wmt_get_tsirqnum(void);
+extern int wmt_disable_gpirq(void);
+extern int wmt_enable_gpirq(void);
+extern int wmt_is_tsirq_enable(void);
+extern int wmt_is_tsint(void);
+extern void wmt_clr_int(void);
+extern void wmt_tsreset_init(void);
+extern int wmt_ts_get_resetgpnum(void);
+extern int wmt_ts_get_lcdexchg(void);
+extern void wmt_disable_rst_pull(void);
+extern void wmt_set_rst_pull(int up);
+extern void wmt_rst_output(int high);
+extern void wmt_rst_input(void);
+extern void wmt_set_intasgp(void);
+extern void wmt_intgp_out(int val);
+extern void wmt_ts_set_irqinput(void);
+extern unsigned int wmt_ts_irqinval(void);
+extern void wmt_ts_set_penup(int up);
+extern int wmt_ts_wait_penup(void);
+extern void wmt_ts_wakeup_penup(void);
+extern struct i2c_client* ts_get_i2c_client(void);
+extern int wmt_ts_ispenup(void);
+extern void wmt_ts_get_firmwname(char* firmname);
+extern unsigned int wmt_ts_get_maxfingernum(void);
+extern unsigned int wmt_ts_get_ictype(void);
+extern unsigned int wmt_ts_get_xaxis(void);
+extern unsigned int wmt_ts_get_xdir(void);
+extern unsigned int wmt_ts_get_ydir(void);
+// short
+extern unsigned int wmt_ts_get_touchheight(void);
+// long
+extern unsigned int wmt_ts_get_touchwidth(void);
+extern int wmt_ts_if_updatefw(void);
+
+
+
+extern void TouchPanelCalibrateAPoint(
+ int UncalX, //@PARM The uncalibrated X coordinate
+ int UncalY, //@PARM The uncalibrated Y coordinate
+ int *pCalX, //@PARM The calibrated X coordinate
+ int *pCalY //@PARM The calibrated Y coordinate
+ );
+
+//filepath:the path of firmware file;
+//firmdata:store the data from firmware file;
+//maxlen: the max len of firmdata;
+//return:the length of firmware data,negative-parsing error.
+//extern int read_firmwarefile(char* filepath, unsigned char** firmdata);
+
+#endif
+
+
+
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
new file mode 100644
index 00000000..f030d9ec
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -0,0 +1,625 @@
+/*
+ * Core Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "cyttsp_core.h"
+
+/* Bootloader number of command keys */
+#define CY_NUM_BL_KEYS 8
+
+/* helpers */
+#define GET_NUM_TOUCHES(x) ((x) & 0x0F)
+#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4)
+#define IS_BAD_PKT(x) ((x) & 0x20)
+#define IS_VALID_APP(x) ((x) & 0x01)
+#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F)
+#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4)
+#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4)
+
+#define CY_REG_BASE 0x00
+#define CY_REG_ACT_DIST 0x1E
+#define CY_REG_ACT_INTRVL 0x1D
+#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1)
+#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1)
+#define CY_MAXZ 255
+#define CY_DELAY_DFLT 20 /* ms */
+#define CY_DELAY_MAX 500
+#define CY_ACT_DIST_DFLT 0xF8
+#define CY_HNDSHK_BIT 0x80
+/* device mode bits */
+#define CY_OPERATE_MODE 0x00
+#define CY_SYSINFO_MODE 0x10
+/* power mode select bits */
+#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */
+#define CY_DEEP_SLEEP_MODE 0x02
+#define CY_LOW_POWER_MODE 0x04
+
+/* Slots management */
+#define CY_MAX_FINGER 4
+#define CY_MAX_ID 16
+
+static const u8 bl_command[] = {
+ 0x00, /* file offset */
+ 0xFF, /* command */
+ 0xA5, /* exit bootloader command */
+ 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */
+};
+
+static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int error;
+ int tries;
+
+ for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+ error = ts->bus_ops->read(ts, command, length, buf);
+ if (!error)
+ return 0;
+
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return -EIO;
+}
+
+static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int error;
+ int tries;
+
+ for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+ error = ts->bus_ops->write(ts, command, length, buf);
+ if (!error)
+ return 0;
+
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return -EIO;
+}
+
+static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
+{
+ return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+}
+
+static int cyttsp_load_bl_regs(struct cyttsp *ts)
+{
+ memset(&ts->bl_data, 0, sizeof(ts->bl_data));
+ ts->bl_data.bl_status = 0x10;
+
+ return ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->bl_data), &ts->bl_data);
+}
+
+static int cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+ int error;
+ u8 bl_cmd[sizeof(bl_command)];
+
+ memcpy(bl_cmd, bl_command, sizeof(bl_command));
+ if (ts->pdata->bl_keys)
+ memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
+ ts->pdata->bl_keys, sizeof(bl_command));
+
+ error = ttsp_write_block_data(ts, CY_REG_BASE,
+ sizeof(bl_cmd), bl_cmd);
+ if (error)
+ return error;
+
+ /* wait for TTSP Device to complete the operation */
+ msleep(CY_DELAY_DFLT);
+
+ error = cyttsp_load_bl_regs(ts);
+ if (error)
+ return error;
+
+ if (GET_BOOTLOADERMODE(ts->bl_data.bl_status))
+ return -EIO;
+
+ return 0;
+}
+
+static int cyttsp_set_operational_mode(struct cyttsp *ts)
+{
+ int error;
+
+ error = ttsp_send_command(ts, CY_OPERATE_MODE);
+ if (error)
+ return error;
+
+ /* wait for TTSP Device to complete switch to Operational mode */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->xy_data), &ts->xy_data);
+ if (error)
+ return error;
+
+ return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0;
+}
+
+static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+ int error;
+
+ memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data));
+
+ /* switch to sysinfo mode */
+ error = ttsp_send_command(ts, CY_SYSINFO_MODE);
+ if (error)
+ return error;
+
+ /* read sysinfo registers */
+ msleep(CY_DELAY_DFLT);
+ error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data),
+ &ts->sysinfo_data);
+ if (error)
+ return error;
+
+ if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl)
+ return -EIO;
+
+ return 0;
+}
+
+static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
+{
+ int retval = 0;
+
+ if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT ||
+ ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT ||
+ ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) {
+
+ u8 intrvl_ray[] = {
+ ts->pdata->act_intrvl,
+ ts->pdata->tch_tmout,
+ ts->pdata->lp_intrvl
+ };
+
+ /* set intrvl registers */
+ retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL,
+ sizeof(intrvl_ray), intrvl_ray);
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return retval;
+}
+
+static int cyttsp_soft_reset(struct cyttsp *ts)
+{
+ unsigned long timeout;
+ int retval;
+
+ /* wait for interrupt to set ready completion */
+ INIT_COMPLETION(ts->bl_ready);
+ ts->state = CY_BL_STATE;
+
+ enable_irq(ts->irq);
+
+ retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
+ if (retval)
+ goto out;
+
+ timeout = wait_for_completion_timeout(&ts->bl_ready,
+ msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
+ retval = timeout ? 0 : -EIO;
+
+out:
+ ts->state = CY_IDLE_STATE;
+ disable_irq(ts->irq);
+ return retval;
+}
+
+static int cyttsp_act_dist_setup(struct cyttsp *ts)
+{
+ u8 act_dist_setup = ts->pdata->act_dist;
+
+ /* Init gesture; active distance setup */
+ return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
+ sizeof(act_dist_setup), &act_dist_setup);
+}
+
+static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids)
+{
+ ids[0] = xy_data->touch12_id >> 4;
+ ids[1] = xy_data->touch12_id & 0xF;
+ ids[2] = xy_data->touch34_id >> 4;
+ ids[3] = xy_data->touch34_id & 0xF;
+}
+
+static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data,
+ int idx)
+{
+ switch (idx) {
+ case 0:
+ return &xy_data->tch1;
+ case 1:
+ return &xy_data->tch2;
+ case 2:
+ return &xy_data->tch3;
+ case 3:
+ return &xy_data->tch4;
+ default:
+ return NULL;
+ }
+}
+
+static void cyttsp_report_tchdata(struct cyttsp *ts)
+{
+ struct cyttsp_xydata *xy_data = &ts->xy_data;
+ struct input_dev *input = ts->input;
+ int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat);
+ const struct cyttsp_tch *tch;
+ int ids[CY_MAX_ID];
+ int i;
+ DECLARE_BITMAP(used, CY_MAX_ID);
+
+ if (IS_LARGE_AREA(xy_data->tt_stat) == 1) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
+ } else if (num_tch > CY_MAX_FINGER) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__);
+ } else if (IS_BAD_PKT(xy_data->tt_mode)) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__);
+ }
+
+ cyttsp_extract_track_ids(xy_data, ids);
+
+ bitmap_zero(used, CY_MAX_ID);
+
+ for (i = 0; i < num_tch; i++) {
+ tch = cyttsp_get_tch(xy_data, i);
+
+ input_mt_slot(input, ids[i]);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+ input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x));
+ input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y));
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z);
+
+ __set_bit(ids[i], used);
+ }
+
+ for (i = 0; i < CY_MAX_ID; i++) {
+ if (test_bit(i, used))
+ continue;
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ }
+
+ input_sync(input);
+}
+
+static irqreturn_t cyttsp_irq(int irq, void *handle)
+{
+ struct cyttsp *ts = handle;
+ int error;
+
+ if (unlikely(ts->state == CY_BL_STATE)) {
+ complete(&ts->bl_ready);
+ goto out;
+ }
+
+ /* Get touch data from CYTTSP device */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(struct cyttsp_xydata), &ts->xy_data);
+ if (error)
+ goto out;
+
+ /* provide flow control handshake */
+ if (ts->pdata->use_hndshk) {
+ error = ttsp_send_command(ts,
+ ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
+ if (error)
+ goto out;
+ }
+
+ if (unlikely(ts->state == CY_IDLE_STATE))
+ goto out;
+
+ if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) {
+ /*
+ * TTSP device has reset back to bootloader mode.
+ * Restore to operational mode.
+ */
+ error = cyttsp_exit_bl_mode(ts);
+ if (error) {
+ dev_err(ts->dev,
+ "Could not return to operational mode, err: %d\n",
+ error);
+ ts->state = CY_IDLE_STATE;
+ }
+ } else {
+ cyttsp_report_tchdata(ts);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int cyttsp_power_on(struct cyttsp *ts)
+{
+ int error;
+
+ error = cyttsp_soft_reset(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_load_bl_regs(ts);
+ if (error)
+ return error;
+
+ if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
+ IS_VALID_APP(ts->bl_data.bl_status)) {
+ error = cyttsp_exit_bl_mode(ts);
+ if (error)
+ return error;
+ }
+
+ if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
+ IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) {
+ return -ENODEV;
+ }
+
+ error = cyttsp_set_sysinfo_mode(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_set_sysinfo_regs(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_set_operational_mode(ts);
+ if (error)
+ return error;
+
+ /* init active distance */
+ error = cyttsp_act_dist_setup(ts);
+ if (error)
+ return error;
+
+ ts->state = CY_ACTIVE_STATE;
+
+ return 0;
+}
+
+static int cyttsp_enable(struct cyttsp *ts)
+{
+ int error;
+
+ /*
+ * The device firmware can wake on an I2C or SPI memory slave
+ * address match. So just reading a register is sufficient to
+ * wake up the device. The first read attempt will fail but it
+ * will wake it up making the second read attempt successful.
+ */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->xy_data), &ts->xy_data);
+ if (error)
+ return error;
+
+ if (GET_HSTMODE(ts->xy_data.hst_mode))
+ return -EIO;
+
+ enable_irq(ts->irq);
+
+ return 0;
+}
+
+static int cyttsp_disable(struct cyttsp *ts)
+{
+ int error;
+
+ error = ttsp_send_command(ts, CY_LOW_POWER_MODE);
+ if (error)
+ return error;
+
+ disable_irq(ts->irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cyttsp_suspend(struct device *dev)
+{
+ struct cyttsp *ts = dev_get_drvdata(dev);
+ int retval = 0;
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->input->users) {
+ retval = cyttsp_disable(ts);
+ if (retval == 0)
+ ts->suspended = true;
+ }
+
+ mutex_unlock(&ts->input->mutex);
+
+ return retval;
+}
+
+static int cyttsp_resume(struct device *dev)
+{
+ struct cyttsp *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->input->users)
+ cyttsp_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->input->mutex);
+
+ return 0;
+}
+
+#endif
+
+SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume);
+EXPORT_SYMBOL_GPL(cyttsp_pm_ops);
+
+static int cyttsp_open(struct input_dev *dev)
+{
+ struct cyttsp *ts = input_get_drvdata(dev);
+ int retval = 0;
+
+ if (!ts->suspended)
+ retval = cyttsp_enable(ts);
+
+ return retval;
+}
+
+static void cyttsp_close(struct input_dev *dev)
+{
+ struct cyttsp *ts = input_get_drvdata(dev);
+
+ if (!ts->suspended)
+ cyttsp_disable(ts);
+}
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, int irq, size_t xfer_buf_size)
+{
+ const struct cyttsp_platform_data *pdata = dev->platform_data;
+ struct cyttsp *ts;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!pdata || !pdata->name || irq <= 0) {
+ error = -EINVAL;
+ goto err_out;
+ }
+
+ ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->dev = dev;
+ ts->input = input_dev;
+ ts->pdata = dev->platform_data;
+ ts->bus_ops = bus_ops;
+ ts->irq = irq;
+
+ init_completion(&ts->bl_ready);
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+ if (pdata->init) {
+ error = pdata->init();
+ if (error) {
+ dev_err(ts->dev, "platform init failed, err: %d\n",
+ error);
+ goto err_free_mem;
+ }
+ }
+
+ input_dev->name = pdata->name;
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = bus_ops->bustype;
+ input_dev->dev.parent = ts->dev;
+
+ input_dev->open = cyttsp_open;
+ input_dev->close = cyttsp_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, pdata->maxx, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, pdata->maxy, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, CY_MAXZ, 0, 0);
+
+ input_mt_init_slots(input_dev, CY_MAX_ID);
+
+ error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ pdata->name, ts);
+ if (error) {
+ dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
+ ts->irq, error);
+ goto err_platform_exit;
+ }
+
+ disable_irq(ts->irq);
+
+ error = cyttsp_power_on(ts);
+ if (error)
+ goto err_free_irq;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(ts->dev, "failed to register input device: %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ return ts;
+
+err_free_irq:
+ free_irq(ts->irq, ts);
+err_platform_exit:
+ if (pdata->exit)
+ pdata->exit();
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+err_out:
+ return ERR_PTR(error);
+}
+EXPORT_SYMBOL_GPL(cyttsp_probe);
+
+void cyttsp_remove(struct cyttsp *ts)
+{
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ if (ts->pdata->exit)
+ ts->pdata->exit();
+ kfree(ts);
+}
+EXPORT_SYMBOL_GPL(cyttsp_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h
new file mode 100644
index 00000000..1aa3c696
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.h
@@ -0,0 +1,149 @@
+/*
+ * Header file for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+
+#ifndef __CYTTSP_CORE_H__
+#define __CYTTSP_CORE_H__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/input/cyttsp.h>
+
+#define CY_NUM_RETRY 16 /* max number of retries for read ops */
+
+struct cyttsp_tch {
+ __be16 x, y;
+ u8 z;
+} __packed;
+
+/* TrueTouch Standard Product Gen3 interface definition */
+struct cyttsp_xydata {
+ u8 hst_mode;
+ u8 tt_mode;
+ u8 tt_stat;
+ struct cyttsp_tch tch1;
+ u8 touch12_id;
+ struct cyttsp_tch tch2;
+ u8 gest_cnt;
+ u8 gest_id;
+ struct cyttsp_tch tch3;
+ u8 touch34_id;
+ struct cyttsp_tch tch4;
+ u8 tt_undef[3];
+ u8 act_dist;
+ u8 tt_reserved;
+} __packed;
+
+
+/* TTSP System Information interface definition */
+struct cyttsp_sysinfo_data {
+ u8 hst_mode;
+ u8 mfg_cmd;
+ u8 mfg_stat;
+ u8 cid[3];
+ u8 tt_undef1;
+ u8 uid[8];
+ u8 bl_verh;
+ u8 bl_verl;
+ u8 tts_verh;
+ u8 tts_verl;
+ u8 app_idh;
+ u8 app_idl;
+ u8 app_verh;
+ u8 app_verl;
+ u8 tt_undef[5];
+ u8 scn_typ;
+ u8 act_intrvl;
+ u8 tch_tmout;
+ u8 lp_intrvl;
+};
+
+/* TTSP Bootloader Register Map interface definition */
+#define CY_BL_CHKSUM_OK 0x01
+struct cyttsp_bootloader_data {
+ u8 bl_file;
+ u8 bl_status;
+ u8 bl_error;
+ u8 blver_hi;
+ u8 blver_lo;
+ u8 bld_blver_hi;
+ u8 bld_blver_lo;
+ u8 ttspver_hi;
+ u8 ttspver_lo;
+ u8 appid_hi;
+ u8 appid_lo;
+ u8 appver_hi;
+ u8 appver_lo;
+ u8 cid_0;
+ u8 cid_1;
+ u8 cid_2;
+};
+
+struct cyttsp;
+
+struct cyttsp_bus_ops {
+ u16 bustype;
+ int (*write)(struct cyttsp *ts,
+ u8 addr, u8 length, const void *values);
+ int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values);
+};
+
+enum cyttsp_state {
+ CY_IDLE_STATE,
+ CY_ACTIVE_STATE,
+ CY_BL_STATE,
+};
+
+struct cyttsp {
+ struct device *dev;
+ int irq;
+ struct input_dev *input;
+ char phys[32];
+ const struct cyttsp_platform_data *pdata;
+ const struct cyttsp_bus_ops *bus_ops;
+ struct cyttsp_bootloader_data bl_data;
+ struct cyttsp_sysinfo_data sysinfo_data;
+ struct cyttsp_xydata xy_data;
+ struct completion bl_ready;
+ enum cyttsp_state state;
+ bool suspended;
+
+ u8 xfer_buf[] ____cacheline_aligned;
+};
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, int irq, size_t xfer_buf_size);
+void cyttsp_remove(struct cyttsp *ts);
+
+extern const struct dev_pm_ops cyttsp_pm_ops;
+
+#endif /* __CYTTSP_CORE_H__ */
diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c
new file mode 100644
index 00000000..2af1d0c5
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_i2c.c
@@ -0,0 +1,136 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+
+#define CY_I2C_DATA_SIZE 128
+
+static int cyttsp_i2c_read_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, void *values)
+{
+ struct i2c_client *client = to_i2c_client(ts->dev);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = values,
+ },
+ };
+ int retval;
+
+ retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (retval < 0)
+ return retval;
+
+ return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+
+static int cyttsp_i2c_write_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, const void *values)
+{
+ struct i2c_client *client = to_i2c_client(ts->dev);
+ int retval;
+
+ ts->xfer_buf[0] = addr;
+ memcpy(&ts->xfer_buf[1], values, length);
+
+ retval = i2c_master_send(client, ts->xfer_buf, length + 1);
+
+ return retval < 0 ? retval : 0;
+}
+
+static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = {
+ .bustype = BUS_I2C,
+ .write = cyttsp_i2c_write_block_data,
+ .read = cyttsp_i2c_read_block_data,
+};
+
+static int __devinit cyttsp_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cyttsp *ts;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C functionality not Supported\n");
+ return -EIO;
+ }
+
+ ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq,
+ CY_I2C_DATA_SIZE);
+
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ i2c_set_clientdata(client, ts);
+
+ return 0;
+}
+
+static int __devexit cyttsp_i2c_remove(struct i2c_client *client)
+{
+ struct cyttsp *ts = i2c_get_clientdata(client);
+
+ cyttsp_remove(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id cyttsp_i2c_id[] = {
+ { CY_I2C_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id);
+
+static struct i2c_driver cyttsp_i2c_driver = {
+ .driver = {
+ .name = CY_I2C_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp_pm_ops,
+ },
+ .probe = cyttsp_i2c_probe,
+ .remove = __devexit_p(cyttsp_i2c_remove),
+ .id_table = cyttsp_i2c_id,
+};
+
+module_i2c_driver(cyttsp_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("i2c:cyttsp");
diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c
new file mode 100644
index 00000000..9f263410
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_spi.c
@@ -0,0 +1,200 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+
+#define CY_SPI_WR_OP 0x00 /* r/~w */
+#define CY_SPI_RD_OP 0x01
+#define CY_SPI_CMD_BYTES 4
+#define CY_SPI_SYNC_BYTE 2
+#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */
+#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */
+#define CY_SPI_DATA_SIZE 128
+#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
+#define CY_SPI_BITS_PER_WORD 8
+
+static int cyttsp_spi_xfer(struct cyttsp *ts,
+ u8 op, u8 reg, u8 *buf, int length)
+{
+ struct spi_device *spi = to_spi_device(ts->dev);
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ u8 *wr_buf = &ts->xfer_buf[0];
+ u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE];
+ int retval;
+ int i;
+
+ if (length > CY_SPI_DATA_SIZE) {
+ dev_err(ts->dev, "%s: length %d is too big.\n",
+ __func__, length);
+ return -EINVAL;
+ }
+
+ memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
+ memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE);
+
+ wr_buf[0] = 0x00; /* header byte 0 */
+ wr_buf[1] = 0xFF; /* header byte 1 */
+ wr_buf[2] = reg; /* reg index */
+ wr_buf[3] = op; /* r/~w */
+ if (op == CY_SPI_WR_OP)
+ memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
+
+ memset(xfer, 0, sizeof(xfer));
+ spi_message_init(&msg);
+
+ /*
+ We set both TX and RX buffers because Cypress TTSP
+ requires full duplex operation.
+ */
+ xfer[0].tx_buf = wr_buf;
+ xfer[0].rx_buf = rd_buf;
+ switch (op) {
+ case CY_SPI_WR_OP:
+ xfer[0].len = length + CY_SPI_CMD_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+ break;
+
+ case CY_SPI_RD_OP:
+ xfer[0].len = CY_SPI_CMD_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+
+ xfer[1].rx_buf = buf;
+ xfer[1].len = length;
+ spi_message_add_tail(&xfer[1], &msg);
+ break;
+
+ default:
+ dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op);
+ return -EINVAL;
+ }
+
+ retval = spi_sync(spi, &msg);
+ if (retval < 0) {
+ dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
+ __func__, retval, xfer[1].len, op);
+
+ /*
+ * do not return here since was a bad ACK sequence
+ * let the following ACK check handle any errors and
+ * allow silent retries
+ */
+ }
+
+ if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 ||
+ rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) {
+
+ dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op);
+
+ for (i = 0; i < CY_SPI_CMD_BYTES; i++)
+ dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n",
+ __func__, i, rd_buf[i]);
+ for (i = 0; i < length; i++)
+ dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n",
+ __func__, i, buf[i]);
+
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cyttsp_spi_read_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, void *data)
+{
+ return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length);
+}
+
+static int cyttsp_spi_write_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, const void *data)
+{
+ return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length);
+}
+
+static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = {
+ .bustype = BUS_SPI,
+ .write = cyttsp_spi_write_block_data,
+ .read = cyttsp_spi_read_block_data,
+};
+
+static int __devinit cyttsp_spi_probe(struct spi_device *spi)
+{
+ struct cyttsp *ts;
+ int error;
+
+ /* Set up SPI*/
+ spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+ spi->mode = SPI_MODE_0;
+ error = spi_setup(spi);
+ if (error < 0) {
+ dev_err(&spi->dev, "%s: SPI setup error %d\n",
+ __func__, error);
+ return error;
+ }
+
+ ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
+ CY_SPI_DATA_BUF_SIZE * 2);
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ spi_set_drvdata(spi, ts);
+
+ return 0;
+}
+
+static int __devexit cyttsp_spi_remove(struct spi_device *spi)
+{
+ struct cyttsp *ts = spi_get_drvdata(spi);
+
+ cyttsp_remove(ts);
+
+ return 0;
+}
+
+static struct spi_driver cyttsp_spi_driver = {
+ .driver = {
+ .name = CY_SPI_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp_pm_ops,
+ },
+ .probe = cyttsp_spi_probe,
+ .remove = __devexit_p(cyttsp_spi_remove),
+};
+
+module_spi_driver(cyttsp_spi_driver);
+
+MODULE_ALIAS("spi:cyttsp");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("spi:cyttsp");
diff --git a/drivers/input/touchscreen/da9034-ts.c b/drivers/input/touchscreen/da9034-ts.c
new file mode 100644
index 00000000..36b65cf1
--- /dev/null
+++ b/drivers/input/touchscreen/da9034-ts.c
@@ -0,0 +1,387 @@
+/*
+ * Touchscreen driver for Dialog Semiconductor DA9034
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Fengwei Yin <fengwei.yin@marvell.com>
+ * Bin Yang <bin.yang@marvell.com>
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/da903x.h>
+#include <linux/slab.h>
+
+#define DA9034_MANUAL_CTRL 0x50
+#define DA9034_LDO_ADC_EN (1 << 4)
+
+#define DA9034_AUTO_CTRL1 0x51
+
+#define DA9034_AUTO_CTRL2 0x52
+#define DA9034_AUTO_TSI_EN (1 << 3)
+#define DA9034_PEN_DETECT (1 << 4)
+
+#define DA9034_TSI_CTRL1 0x53
+#define DA9034_TSI_CTRL2 0x54
+#define DA9034_TSI_X_MSB 0x6c
+#define DA9034_TSI_Y_MSB 0x6d
+#define DA9034_TSI_XY_LSB 0x6e
+
+enum {
+ STATE_IDLE, /* wait for pendown */
+ STATE_BUSY, /* TSI busy sampling */
+ STATE_STOP, /* sample available */
+ STATE_WAIT, /* Wait to start next sample */
+};
+
+enum {
+ EVENT_PEN_DOWN,
+ EVENT_PEN_UP,
+ EVENT_TSI_READY,
+ EVENT_TIMEDOUT,
+};
+
+struct da9034_touch {
+ struct device *da9034_dev;
+ struct input_dev *input_dev;
+
+ struct delayed_work tsi_work;
+ struct notifier_block notifier;
+
+ int state;
+
+ int interval_ms;
+ int x_inverted;
+ int y_inverted;
+
+ int last_x;
+ int last_y;
+};
+
+static inline int is_pen_down(struct da9034_touch *touch)
+{
+ return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);
+}
+
+static inline int detect_pen_down(struct da9034_touch *touch, int on)
+{
+ if (on)
+ return da903x_set_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
+ else
+ return da903x_clr_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
+}
+
+static int read_tsi(struct da9034_touch *touch)
+{
+ uint8_t _x, _y, _v;
+ int ret;
+
+ ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);
+ if (ret)
+ return ret;
+
+ ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);
+ if (ret)
+ return ret;
+
+ ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);
+ if (ret)
+ return ret;
+
+ touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);
+ touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);
+
+ return 0;
+}
+
+static inline int start_tsi(struct da9034_touch *touch)
+{
+ return da903x_set_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
+}
+
+static inline int stop_tsi(struct da9034_touch *touch)
+{
+ return da903x_clr_bits(touch->da9034_dev,
+ DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
+}
+
+static inline void report_pen_down(struct da9034_touch *touch)
+{
+ int x = touch->last_x;
+ int y = touch->last_y;
+
+ x &= 0xfff;
+ if (touch->x_inverted)
+ x = 1024 - x;
+ y &= 0xfff;
+ if (touch->y_inverted)
+ y = 1024 - y;
+
+ input_report_abs(touch->input_dev, ABS_X, x);
+ input_report_abs(touch->input_dev, ABS_Y, y);
+ input_report_key(touch->input_dev, BTN_TOUCH, 1);
+
+ input_sync(touch->input_dev);
+}
+
+static inline void report_pen_up(struct da9034_touch *touch)
+{
+ input_report_key(touch->input_dev, BTN_TOUCH, 0);
+ input_sync(touch->input_dev);
+}
+
+static void da9034_event_handler(struct da9034_touch *touch, int event)
+{
+ int err;
+
+ switch (touch->state) {
+ case STATE_IDLE:
+ if (event != EVENT_PEN_DOWN)
+ break;
+
+ /* Enable auto measurement of the TSI, this will
+ * automatically disable pen down detection
+ */
+ err = start_tsi(touch);
+ if (err)
+ goto err_reset;
+
+ touch->state = STATE_BUSY;
+ break;
+
+ case STATE_BUSY:
+ if (event != EVENT_TSI_READY)
+ break;
+
+ err = read_tsi(touch);
+ if (err)
+ goto err_reset;
+
+ /* Disable auto measurement of the TSI, so that
+ * pen down status will be available
+ */
+ err = stop_tsi(touch);
+ if (err)
+ goto err_reset;
+
+ touch->state = STATE_STOP;
+
+ /* FIXME: PEN_{UP/DOWN} events are expected to be
+ * available by stopping TSI, but this is found not
+ * always true, delay and simulate such an event
+ * here is more reliable
+ */
+ mdelay(1);
+ da9034_event_handler(touch,
+ is_pen_down(touch) ? EVENT_PEN_DOWN :
+ EVENT_PEN_UP);
+ break;
+
+ case STATE_STOP:
+ if (event == EVENT_PEN_DOWN) {
+ report_pen_down(touch);
+ schedule_delayed_work(&touch->tsi_work,
+ msecs_to_jiffies(touch->interval_ms));
+ touch->state = STATE_WAIT;
+ }
+
+ if (event == EVENT_PEN_UP) {
+ report_pen_up(touch);
+ touch->state = STATE_IDLE;
+ }
+ break;
+
+ case STATE_WAIT:
+ if (event != EVENT_TIMEDOUT)
+ break;
+
+ if (is_pen_down(touch)) {
+ start_tsi(touch);
+ touch->state = STATE_BUSY;
+ } else {
+ report_pen_up(touch);
+ touch->state = STATE_IDLE;
+ }
+ break;
+ }
+ return;
+
+err_reset:
+ touch->state = STATE_IDLE;
+ stop_tsi(touch);
+ detect_pen_down(touch, 1);
+}
+
+static void da9034_tsi_work(struct work_struct *work)
+{
+ struct da9034_touch *touch =
+ container_of(work, struct da9034_touch, tsi_work.work);
+
+ da9034_event_handler(touch, EVENT_TIMEDOUT);
+}
+
+static int da9034_touch_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct da9034_touch *touch =
+ container_of(nb, struct da9034_touch, notifier);
+
+ if (event & DA9034_EVENT_TSI_READY)
+ da9034_event_handler(touch, EVENT_TSI_READY);
+
+ if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE)
+ da9034_event_handler(touch, EVENT_PEN_DOWN);
+
+ return 0;
+}
+
+static int da9034_touch_open(struct input_dev *dev)
+{
+ struct da9034_touch *touch = input_get_drvdata(dev);
+ int ret;
+
+ ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,
+ DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
+ if (ret)
+ return -EBUSY;
+
+ /* Enable ADC LDO */
+ ret = da903x_set_bits(touch->da9034_dev,
+ DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
+ if (ret)
+ return ret;
+
+ /* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
+ ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);
+ if (ret)
+ return ret;
+
+ ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);
+ if (ret)
+ return ret;
+
+ touch->state = STATE_IDLE;
+ detect_pen_down(touch, 1);
+
+ return 0;
+}
+
+static void da9034_touch_close(struct input_dev *dev)
+{
+ struct da9034_touch *touch = input_get_drvdata(dev);
+
+ da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,
+ DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
+
+ cancel_delayed_work_sync(&touch->tsi_work);
+
+ touch->state = STATE_IDLE;
+ stop_tsi(touch);
+ detect_pen_down(touch, 0);
+
+ /* Disable ADC LDO */
+ da903x_clr_bits(touch->da9034_dev,
+ DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
+}
+
+
+static int __devinit da9034_touch_probe(struct platform_device *pdev)
+{
+ struct da9034_touch_pdata *pdata = pdev->dev.platform_data;
+ struct da9034_touch *touch;
+ struct input_dev *input_dev;
+ int ret;
+
+ touch = kzalloc(sizeof(struct da9034_touch), GFP_KERNEL);
+ if (touch == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ touch->da9034_dev = pdev->dev.parent;
+
+ if (pdata) {
+ touch->interval_ms = pdata->interval_ms;
+ touch->x_inverted = pdata->x_inverted;
+ touch->y_inverted = pdata->y_inverted;
+ } else
+ /* fallback into default */
+ touch->interval_ms = 10;
+
+ INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);
+ touch->notifier.notifier_call = da9034_touch_notifier;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ ret = -ENOMEM;
+ goto err_free_touch;
+ }
+
+ input_dev->name = pdev->name;
+ input_dev->open = da9034_touch_open;
+ input_dev->close = da9034_touch_close;
+ input_dev->dev.parent = &pdev->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ touch->input_dev = input_dev;
+ input_set_drvdata(input_dev, touch);
+
+ ret = input_register_device(input_dev);
+ if (ret)
+ goto err_free_input;
+
+ platform_set_drvdata(pdev, touch);
+ return 0;
+
+err_free_input:
+ input_free_device(input_dev);
+err_free_touch:
+ kfree(touch);
+ return ret;
+}
+
+static int __devexit da9034_touch_remove(struct platform_device *pdev)
+{
+ struct da9034_touch *touch = platform_get_drvdata(pdev);
+
+ input_unregister_device(touch->input_dev);
+ kfree(touch);
+
+ return 0;
+}
+
+static struct platform_driver da9034_touch_driver = {
+ .driver = {
+ .name = "da9034-touch",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9034_touch_probe,
+ .remove = __devexit_p(da9034_touch_remove),
+};
+module_platform_driver(da9034_touch_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9034-touch");
diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c
new file mode 100644
index 00000000..45535390
--- /dev/null
+++ b/drivers/input/touchscreen/dynapro.c
@@ -0,0 +1,206 @@
+/*
+ * Dynapro serial touchscreen driver
+ *
+ * Copyright (c) 2009 Tias Guns
+ * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and
+ * Richard Lemon
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2009/09/19 Tias Guns <tias@ulyssis.org>
+ * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module)
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Dynapro serial touchscreen driver"
+
+MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define DYNAPRO_FORMAT_TOUCH_BIT 0x40
+#define DYNAPRO_FORMAT_LENGTH 3
+#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80
+
+#define DYNAPRO_MIN_XC 0
+#define DYNAPRO_MAX_XC 0x3ff
+#define DYNAPRO_MIN_YC 0
+#define DYNAPRO_MAX_YC 0x3ff
+
+#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4))
+#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7))
+#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct dynapro {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[DYNAPRO_FORMAT_LENGTH];
+ char phys[32];
+};
+
+static void dynapro_process_data(struct dynapro *pdynapro)
+{
+ struct input_dev *dev = pdynapro->dev;
+
+ if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) {
+ input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data));
+ input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data));
+ input_report_key(dev, BTN_TOUCH,
+ DYNAPRO_GET_TOUCHED(pdynapro->data));
+ input_sync(dev);
+
+ pdynapro->idx = 0;
+ }
+}
+
+static irqreturn_t dynapro_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+ pdynapro->data[pdynapro->idx] = data;
+
+ if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0])
+ dynapro_process_data(pdynapro);
+ else
+ dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+ pdynapro->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+static void dynapro_disconnect(struct serio *serio)
+{
+ struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+ input_get_device(pdynapro->dev);
+ input_unregister_device(pdynapro->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(pdynapro->dev);
+ kfree(pdynapro);
+}
+
+/*
+ * dynapro_connect() is the routine that is called when someone adds a
+ * new serio device that supports dynapro protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int dynapro_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct dynapro *pdynapro;
+ struct input_dev *input_dev;
+ int err;
+
+ pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pdynapro || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pdynapro->serio = serio;
+ pdynapro->dev = input_dev;
+ snprintf(pdynapro->phys, sizeof(pdynapro->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Dynapro Serial TouchScreen";
+ input_dev->phys = pdynapro->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_DYNAPRO;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(pdynapro->dev, ABS_X,
+ DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0);
+ input_set_abs_params(pdynapro->dev, ABS_Y,
+ DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, pdynapro);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pdynapro->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pdynapro);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id dynapro_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_DYNAPRO,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, dynapro_serio_ids);
+
+static struct serio_driver dynapro_drv = {
+ .driver = {
+ .name = "dynapro",
+ },
+ .description = DRIVER_DESC,
+ .id_table = dynapro_serio_ids,
+ .interrupt = dynapro_interrupt,
+ .connect = dynapro_connect,
+ .disconnect = dynapro_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init dynapro_init(void)
+{
+ return serio_register_driver(&dynapro_drv);
+}
+
+static void __exit dynapro_exit(void)
+{
+ serio_unregister_driver(&dynapro_drv);
+}
+
+module_init(dynapro_init);
+module_exit(dynapro_exit);
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
new file mode 100644
index 00000000..503c7096
--- /dev/null
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -0,0 +1,327 @@
+/*
+ * Touch Screen driver for EETI's I2C connected touch screen panels
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * See EETI's software guide for the protocol specification:
+ * http://home.eeti.com.tw/web20/eg/guide.htm
+ *
+ * Based on migor_ts.c
+ * Copyright (c) 2008 Magnus Damm
+ * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>
+ *
+ * This file 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 file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+#include <linux/input/eeti_ts.h>
+#include <linux/slab.h>
+
+static bool flip_x;
+module_param(flip_x, bool, 0644);
+MODULE_PARM_DESC(flip_x, "flip x coordinate");
+
+static bool flip_y;
+module_param(flip_y, bool, 0644);
+MODULE_PARM_DESC(flip_y, "flip y coordinate");
+
+struct eeti_ts_priv {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct work_struct work;
+ struct mutex mutex;
+ int irq, irq_active_high;
+};
+
+#define EETI_TS_BITDEPTH (11)
+#define EETI_MAXVAL ((1 << (EETI_TS_BITDEPTH + 1)) - 1)
+
+#define REPORT_BIT_PRESSED (1 << 0)
+#define REPORT_BIT_AD0 (1 << 1)
+#define REPORT_BIT_AD1 (1 << 2)
+#define REPORT_BIT_HAS_PRESSURE (1 << 6)
+#define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH)
+
+static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)
+{
+ return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high;
+}
+
+static void eeti_ts_read(struct work_struct *work)
+{
+ char buf[6];
+ unsigned int x, y, res, pressed, to = 100;
+ struct eeti_ts_priv *priv =
+ container_of(work, struct eeti_ts_priv, work);
+
+ mutex_lock(&priv->mutex);
+
+ while (eeti_ts_irq_active(priv) && --to)
+ i2c_master_recv(priv->client, buf, sizeof(buf));
+
+ if (!to) {
+ dev_err(&priv->client->dev,
+ "unable to clear IRQ - line stuck?\n");
+ goto out;
+ }
+
+ /* drop non-report packets */
+ if (!(buf[0] & 0x80))
+ goto out;
+
+ pressed = buf[0] & REPORT_BIT_PRESSED;
+ res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1));
+ x = buf[2] | (buf[1] << 8);
+ y = buf[4] | (buf[3] << 8);
+
+ /* fix the range to 11 bits */
+ x >>= res - EETI_TS_BITDEPTH;
+ y >>= res - EETI_TS_BITDEPTH;
+
+ if (flip_x)
+ x = EETI_MAXVAL - x;
+
+ if (flip_y)
+ y = EETI_MAXVAL - y;
+
+ if (buf[0] & REPORT_BIT_HAS_PRESSURE)
+ input_report_abs(priv->input, ABS_PRESSURE, buf[5]);
+
+ input_report_abs(priv->input, ABS_X, x);
+ input_report_abs(priv->input, ABS_Y, y);
+ input_report_key(priv->input, BTN_TOUCH, !!pressed);
+ input_sync(priv->input);
+
+out:
+ mutex_unlock(&priv->mutex);
+}
+
+static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
+{
+ struct eeti_ts_priv *priv = dev_id;
+
+ /* postpone I2C transactions as we are atomic */
+ schedule_work(&priv->work);
+
+ return IRQ_HANDLED;
+}
+
+static void eeti_ts_start(struct eeti_ts_priv *priv)
+{
+ enable_irq(priv->irq);
+
+ /* Read the events once to arm the IRQ */
+ eeti_ts_read(&priv->work);
+}
+
+static void eeti_ts_stop(struct eeti_ts_priv *priv)
+{
+ disable_irq(priv->irq);
+ cancel_work_sync(&priv->work);
+}
+
+static int eeti_ts_open(struct input_dev *dev)
+{
+ struct eeti_ts_priv *priv = input_get_drvdata(dev);
+
+ eeti_ts_start(priv);
+
+ return 0;
+}
+
+static void eeti_ts_close(struct input_dev *dev)
+{
+ struct eeti_ts_priv *priv = input_get_drvdata(dev);
+
+ eeti_ts_stop(priv);
+}
+
+static int __devinit eeti_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *idp)
+{
+ struct eeti_ts_platform_data *pdata;
+ struct eeti_ts_priv *priv;
+ struct input_dev *input;
+ unsigned int irq_flags;
+ int err = -ENOMEM;
+
+ /*
+ * In contrast to what's described in the datasheet, there seems
+ * to be no way of probing the presence of that device using I2C
+ * commands. So we need to blindly believe it is there, and wait
+ * for interrupts to occur.
+ */
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ goto err0;
+ }
+
+ mutex_init(&priv->mutex);
+ input = input_allocate_device();
+
+ if (!input) {
+ dev_err(&client->dev, "Failed to allocate input device.\n");
+ goto err1;
+ }
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0);
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+ input->open = eeti_ts_open;
+ input->close = eeti_ts_close;
+
+ priv->client = client;
+ priv->input = input;
+ priv->irq = client->irq;
+
+ pdata = client->dev.platform_data;
+
+ if (pdata)
+ priv->irq_active_high = pdata->irq_active_high;
+
+ irq_flags = priv->irq_active_high ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+ INIT_WORK(&priv->work, eeti_ts_read);
+ i2c_set_clientdata(client, priv);
+ input_set_drvdata(input, priv);
+
+ err = input_register_device(input);
+ if (err)
+ goto err1;
+
+ err = request_irq(priv->irq, eeti_ts_isr, irq_flags,
+ client->name, priv);
+ if (err) {
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ goto err2;
+ }
+
+ /*
+ * Disable the device for now. It will be enabled once the
+ * input device is opened.
+ */
+ eeti_ts_stop(priv);
+
+ device_init_wakeup(&client->dev, 0);
+ return 0;
+
+err2:
+ input_unregister_device(input);
+ input = NULL; /* so we dont try to free it below */
+err1:
+ input_free_device(input);
+ kfree(priv);
+err0:
+ return err;
+}
+
+static int __devexit eeti_ts_remove(struct i2c_client *client)
+{
+ struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+
+ free_irq(priv->irq, priv);
+ /*
+ * eeti_ts_stop() leaves IRQ disabled. We need to re-enable it
+ * so that device still works if we reload the driver.
+ */
+ enable_irq(priv->irq);
+
+ input_unregister_device(priv->input);
+ kfree(priv);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int eeti_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+ struct input_dev *input_dev = priv->input;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ eeti_ts_stop(priv);
+
+ mutex_unlock(&input_dev->mutex);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+static int eeti_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+ struct input_dev *input_dev = priv->input;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(priv->irq);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ eeti_ts_start(priv);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume);
+#endif
+
+static const struct i2c_device_id eeti_ts_id[] = {
+ { "eeti_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, eeti_ts_id);
+
+static struct i2c_driver eeti_ts_driver = {
+ .driver = {
+ .name = "eeti_ts",
+#ifdef CONFIG_PM
+ .pm = &eeti_ts_pm,
+#endif
+ },
+ .probe = eeti_ts_probe,
+ .remove = __devexit_p(eeti_ts_remove),
+ .id_table = eeti_ts_id,
+};
+
+module_i2c_driver(eeti_ts_driver);
+
+MODULE_DESCRIPTION("EETI Touchscreen driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
new file mode 100644
index 00000000..70524dd3
--- /dev/null
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -0,0 +1,292 @@
+/*
+ * Driver for EETI eGalax Multiple Touch Controller
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * based on max11801_ts.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* EETI eGalax serial touch screen controller is a I2C based multiple
+ * touch screen controller, it supports 5 point multiple touch. */
+
+/* TODO:
+ - auto idle mode support
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/input/mt.h>
+
+/*
+ * Mouse Mode: some panel may configure the controller to mouse mode,
+ * which can only report one point at a given time.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_MOUSE 0x1
+/*
+ * Vendor Mode: this mode is used to transfer some vendor specific
+ * messages.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_VENDOR 0x3
+/* Multiple Touch Mode */
+#define REPORT_MODE_MTTOUCH 0x4
+
+#define MAX_SUPPORT_POINTS 5
+
+#define EVENT_VALID_OFFSET 7
+#define EVENT_VALID_MASK (0x1 << EVENT_VALID_OFFSET)
+#define EVENT_ID_OFFSET 2
+#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET)
+#define EVENT_IN_RANGE (0x1 << 1)
+#define EVENT_DOWN_UP (0X1 << 0)
+
+#define MAX_I2C_DATA_LEN 10
+
+#define EGALAX_MAX_X 32760
+#define EGALAX_MAX_Y 32760
+#define EGALAX_MAX_TRIES 100
+
+struct egalax_ts {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+};
+
+static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
+{
+ struct egalax_ts *ts = dev_id;
+ struct input_dev *input_dev = ts->input_dev;
+ struct i2c_client *client = ts->client;
+ u8 buf[MAX_I2C_DATA_LEN];
+ int id, ret, x, y, z;
+ int tries = 0;
+ bool down, valid;
+ u8 state;
+
+ do {
+ ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
+ } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
+
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (buf[0] != REPORT_MODE_MTTOUCH) {
+ /* ignore mouse events and vendor events */
+ return IRQ_HANDLED;
+ }
+
+ state = buf[1];
+ x = (buf[3] << 8) | buf[2];
+ y = (buf[5] << 8) | buf[4];
+ z = (buf[7] << 8) | buf[6];
+
+ valid = state & EVENT_VALID_MASK;
+ id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
+ down = state & EVENT_DOWN_UP;
+
+ if (!valid || id > MAX_SUPPORT_POINTS) {
+ dev_dbg(&client->dev, "point invalid\n");
+ return IRQ_HANDLED;
+ }
+
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
+
+ dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d",
+ down ? "down" : "up", id, x, y, z);
+
+ if (down) {
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, z);
+ }
+
+ input_mt_report_pointer_emulation(input_dev, true);
+ input_sync(input_dev);
+
+ return IRQ_HANDLED;
+}
+
+/* wake up controller by an falling edge of interrupt gpio. */
+static int egalax_wake_up_device(struct i2c_client *client)
+{
+ int gpio = irq_to_gpio(client->irq);
+ int ret;
+
+ ret = gpio_request(gpio, "egalax_irq");
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "request gpio failed, cannot wake up controller: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* wake up controller via an falling edge on IRQ gpio. */
+ gpio_direction_output(gpio, 0);
+ gpio_set_value(gpio, 1);
+
+ /* controller should be waken up, return irq. */
+ gpio_direction_input(gpio);
+ gpio_free(gpio);
+
+ return 0;
+}
+
+static int __devinit egalax_firmware_version(struct i2c_client *client)
+{
+ static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
+ int ret;
+
+ ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int __devinit egalax_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct egalax_ts *ts;
+ struct input_dev *input_dev;
+ int ret;
+ int error;
+
+ ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL);
+ if (!ts) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_ts;
+ }
+
+ ts->client = client;
+ ts->input_dev = input_dev;
+
+ /* controller may be in sleep, wake it up. */
+ egalax_wake_up_device(client);
+
+ ret = egalax_firmware_version(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read firmware version\n");
+ error = -EIO;
+ goto err_free_dev;
+ }
+
+ input_dev->name = "EETI eGalax Touch Screen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
+ input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
+
+ input_set_drvdata(input_dev, ts);
+
+ error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "egalax_ts", ts);
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_dev;
+ }
+
+ error = input_register_device(ts->input_dev);
+ if (error)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, ts);
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, ts);
+err_free_dev:
+ input_free_device(input_dev);
+err_free_ts:
+ kfree(ts);
+
+ return error;
+}
+
+static __devexit int egalax_ts_remove(struct i2c_client *client)
+{
+ struct egalax_ts *ts = i2c_get_clientdata(client);
+
+ free_irq(client->irq, ts);
+
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id egalax_ts_id[] = {
+ { "egalax_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, egalax_ts_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int egalax_ts_suspend(struct device *dev)
+{
+ static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = {
+ 0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
+ };
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
+ return ret > 0 ? 0 : ret;
+}
+
+static int egalax_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return egalax_wake_up_device(client);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
+
+static struct i2c_driver egalax_ts_driver = {
+ .driver = {
+ .name = "egalax_ts",
+ .owner = THIS_MODULE,
+ .pm = &egalax_ts_pm_ops,
+ },
+ .id_table = egalax_ts_id,
+ .probe = egalax_ts_probe,
+ .remove = __devexit_p(egalax_ts_remove),
+};
+
+module_i2c_driver(egalax_ts_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
new file mode 100644
index 00000000..486d31ba
--- /dev/null
+++ b/drivers/input/touchscreen/elo.c
@@ -0,0 +1,423 @@
+/*
+ * Elo serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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 driver can handle serial Elo touchscreens using either the Elo standard
+ * 'E271-2210' 10-byte protocol, Elo legacy 'E281A-4002' 6-byte protocol, Elo
+ * legacy 'E271-140' 4-byte protocol and Elo legacy 'E261-280' 3-byte protocol.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+
+#define DRIVER_DESC "Elo serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define ELO_MAX_LENGTH 10
+
+#define ELO10_PACKET_LEN 8
+#define ELO10_TOUCH 0x03
+#define ELO10_PRESSURE 0x80
+
+#define ELO10_LEAD_BYTE 'U'
+
+#define ELO10_ID_CMD 'i'
+
+#define ELO10_TOUCH_PACKET 'T'
+#define ELO10_ACK_PACKET 'A'
+#define ELI10_ID_PACKET 'I'
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct elo {
+ struct input_dev *dev;
+ struct serio *serio;
+ struct mutex cmd_mutex;
+ struct completion cmd_done;
+ int id;
+ int idx;
+ unsigned char expected_packet;
+ unsigned char csum;
+ unsigned char data[ELO_MAX_LENGTH];
+ unsigned char response[ELO10_PACKET_LEN];
+ char phys[32];
+};
+
+static void elo_process_data_10(struct elo *elo, unsigned char data)
+{
+ struct input_dev *dev = elo->dev;
+
+ elo->data[elo->idx] = data;
+
+ switch (elo->idx++) {
+ case 0:
+ elo->csum = 0xaa;
+ if (data != ELO10_LEAD_BYTE) {
+ dev_dbg(&elo->serio->dev,
+ "unsynchronized data: 0x%02x\n", data);
+ elo->idx = 0;
+ }
+ break;
+
+ case 9:
+ elo->idx = 0;
+ if (data != elo->csum) {
+ dev_dbg(&elo->serio->dev,
+ "bad checksum: 0x%02x, expected 0x%02x\n",
+ data, elo->csum);
+ break;
+ }
+ if (elo->data[1] != elo->expected_packet) {
+ if (elo->data[1] != ELO10_TOUCH_PACKET)
+ dev_dbg(&elo->serio->dev,
+ "unexpected packet: 0x%02x\n",
+ elo->data[1]);
+ break;
+ }
+ if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) {
+ input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
+ input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
+ if (elo->data[2] & ELO10_PRESSURE)
+ input_report_abs(dev, ABS_PRESSURE,
+ (elo->data[8] << 8) | elo->data[7]);
+ input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH);
+ input_sync(dev);
+ } else if (elo->data[1] == ELO10_ACK_PACKET) {
+ if (elo->data[2] == '0')
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ complete(&elo->cmd_done);
+ } else {
+ memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN);
+ elo->expected_packet = ELO10_ACK_PACKET;
+ }
+ break;
+ }
+ elo->csum += data;
+}
+
+static void elo_process_data_6(struct elo *elo, unsigned char data)
+{
+ struct input_dev *dev = elo->dev;
+
+ elo->data[elo->idx] = data;
+
+ switch (elo->idx++) {
+
+ case 0:
+ if ((data & 0xc0) != 0xc0)
+ elo->idx = 0;
+ break;
+
+ case 1:
+ if ((data & 0xc0) != 0x80)
+ elo->idx = 0;
+ break;
+
+ case 2:
+ if ((data & 0xc0) != 0x40)
+ elo->idx = 0;
+ break;
+
+ case 3:
+ if (data & 0xc0) {
+ elo->idx = 0;
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, ((elo->data[0] & 0x3f) << 6) | (elo->data[1] & 0x3f));
+ input_report_abs(dev, ABS_Y, ((elo->data[2] & 0x3f) << 6) | (elo->data[3] & 0x3f));
+
+ if (elo->id == 2) {
+ input_report_key(dev, BTN_TOUCH, 1);
+ input_sync(dev);
+ elo->idx = 0;
+ }
+
+ break;
+
+ case 4:
+ if (data) {
+ input_sync(dev);
+ elo->idx = 0;
+ }
+ break;
+
+ case 5:
+ if ((data & 0xf0) == 0) {
+ input_report_abs(dev, ABS_PRESSURE, elo->data[5]);
+ input_report_key(dev, BTN_TOUCH, !!elo->data[5]);
+ }
+ input_sync(dev);
+ elo->idx = 0;
+ break;
+ }
+}
+
+static void elo_process_data_3(struct elo *elo, unsigned char data)
+{
+ struct input_dev *dev = elo->dev;
+
+ elo->data[elo->idx] = data;
+
+ switch (elo->idx++) {
+
+ case 0:
+ if ((data & 0x7f) != 0x01)
+ elo->idx = 0;
+ break;
+ case 2:
+ input_report_key(dev, BTN_TOUCH, !(elo->data[1] & 0x80));
+ input_report_abs(dev, ABS_X, elo->data[1]);
+ input_report_abs(dev, ABS_Y, elo->data[2]);
+ input_sync(dev);
+ elo->idx = 0;
+ break;
+ }
+}
+
+static irqreturn_t elo_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct elo *elo = serio_get_drvdata(serio);
+
+ switch (elo->id) {
+ case 0:
+ elo_process_data_10(elo, data);
+ break;
+
+ case 1:
+ case 2:
+ elo_process_data_6(elo, data);
+ break;
+
+ case 3:
+ elo_process_data_3(elo, data);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int elo_command_10(struct elo *elo, unsigned char *packet)
+{
+ int rc = -1;
+ int i;
+ unsigned char csum = 0xaa + ELO10_LEAD_BYTE;
+
+ mutex_lock(&elo->cmd_mutex);
+
+ serio_pause_rx(elo->serio);
+ elo->expected_packet = toupper(packet[0]);
+ init_completion(&elo->cmd_done);
+ serio_continue_rx(elo->serio);
+
+ if (serio_write(elo->serio, ELO10_LEAD_BYTE))
+ goto out;
+
+ for (i = 0; i < ELO10_PACKET_LEN; i++) {
+ csum += packet[i];
+ if (serio_write(elo->serio, packet[i]))
+ goto out;
+ }
+
+ if (serio_write(elo->serio, csum))
+ goto out;
+
+ wait_for_completion_timeout(&elo->cmd_done, HZ);
+
+ if (elo->expected_packet == ELO10_TOUCH_PACKET) {
+ /* We are back in reporting mode, the command was ACKed */
+ memcpy(packet, elo->response, ELO10_PACKET_LEN);
+ rc = 0;
+ }
+
+ out:
+ mutex_unlock(&elo->cmd_mutex);
+ return rc;
+}
+
+static int elo_setup_10(struct elo *elo)
+{
+ static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
+ struct input_dev *dev = elo->dev;
+ unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
+
+ if (elo_command_10(elo, packet))
+ return -1;
+
+ dev->id.version = (packet[5] << 8) | packet[4];
+
+ input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0);
+ if (packet[3] & ELO10_PRESSURE)
+ input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+ dev_info(&elo->serio->dev,
+ "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n",
+ elo_types[(packet[1] -'0') & 0x03],
+ packet[5], packet[4], packet[3], packet[7]);
+
+ return 0;
+}
+
+/*
+ * elo_disconnect() is the opposite of elo_connect()
+ */
+
+static void elo_disconnect(struct serio *serio)
+{
+ struct elo *elo = serio_get_drvdata(serio);
+
+ input_get_device(elo->dev);
+ input_unregister_device(elo->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(elo->dev);
+ kfree(elo);
+}
+
+/*
+ * elo_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int elo_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct elo *elo;
+ struct input_dev *input_dev;
+ int err;
+
+ elo = kzalloc(sizeof(struct elo), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!elo || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ elo->serio = serio;
+ elo->id = serio->id.id;
+ elo->dev = input_dev;
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ mutex_init(&elo->cmd_mutex);
+ init_completion(&elo->cmd_done);
+ snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Elo Serial TouchScreen";
+ input_dev->phys = elo->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_ELO;
+ input_dev->id.product = elo->id;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ serio_set_drvdata(serio, elo);
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ switch (elo->id) {
+
+ case 0: /* 10-byte protocol */
+ if (elo_setup_10(elo))
+ goto fail3;
+
+ break;
+
+ case 1: /* 6-byte protocol */
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
+
+ case 2: /* 4-byte protocol */
+ input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0);
+ break;
+
+ case 3: /* 3-byte protocol */
+ input_set_abs_params(input_dev, ABS_X, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 255, 0, 0);
+ break;
+ }
+
+ err = input_register_device(elo->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(elo);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id elo_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_ELO,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, elo_serio_ids);
+
+static struct serio_driver elo_drv = {
+ .driver = {
+ .name = "elo",
+ },
+ .description = DRIVER_DESC,
+ .id_table = elo_serio_ids,
+ .interrupt = elo_interrupt,
+ .connect = elo_connect,
+ .disconnect = elo_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init elo_init(void)
+{
+ return serio_register_driver(&elo_drv);
+}
+
+static void __exit elo_exit(void)
+{
+ serio_unregister_driver(&elo_drv);
+}
+
+module_init(elo_init);
+module_exit(elo_exit);
diff --git a/drivers/input/touchscreen/ft5x0x/Kconfig b/drivers/input/touchscreen/ft5x0x/Kconfig
new file mode 100755
index 00000000..7dadb37f
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/Kconfig
@@ -0,0 +1,11 @@
+config TOUCHSCREEN_FT5X0X
+ tristate "FT5X0X Capacity Touchscreen Device Support"
+ default y
+ depends on ARCH_WMT
+ ---help---
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+ If unsure, say N.
+ To compile this driver as a module, choose M here: the
+ module will be called ft5x0x.
+
diff --git a/drivers/input/touchscreen/ft5x0x/Makefile b/drivers/input/touchscreen/ft5x0x/Makefile
new file mode 100755
index 00000000..0283c3ec
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/Makefile
@@ -0,0 +1,34 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_ft5x0x
+
+#obj-$(CONFIG_TOUCHSCREEN_FT5X0X) := $(MY_MODULE_NAME).o
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := ft5x0x.o ft5x0x_upg.o ft5402_config.o ini.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ @rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ @rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
+
diff --git a/drivers/input/touchscreen/ft5x0x/ft5402_config.c b/drivers/input/touchscreen/ft5x0x/ft5402_config.c
new file mode 100755
index 00000000..58683ebd
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ft5402_config.c
@@ -0,0 +1,2295 @@
+#include "ft5402_config.h"
+//#include <linux/i2c/ft5402_ts.h>
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/semaphore.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+
+#include <linux/syscalls.h>
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+#include "ini.h"
+#include "ft5402_ini_config.h"
+#include "ft5x0x.h"
+
+
+extern int ft5x0x_i2c_txdata(char *txdata, int length);
+
+int ft5402_write_reg(struct i2c_client * client, u8 regaddr, u8 regvalue)
+{
+ unsigned char buf[2] = {0};
+ buf[0] = regaddr;
+ buf[1] = regvalue;
+
+ return ft5x0x_i2c_txdata(buf, 2);
+}
+
+int ft5402_read_reg(struct i2c_client * client, u8 regaddr, u8 * regvalue)
+{
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &regaddr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = regvalue,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ pr_err("function:%s. i2c read error: %d\n", __func__, ret);
+ return ret;
+}
+
+/*set tx order
+*@txNO: offset from tx order start
+*@txNO1: tx NO.
+*/
+static int ft5402_set_tx_order(struct i2c_client * client, u8 txNO, u8 txNO1)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_write_reg(client, FT5402_REG_TX_ORDER_START + txNO,
+ txNO1);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_ORDER_START + txNO - FT5402_TX_TEST_MODE_1,
+ txNO1);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*set tx order
+*@txNO: offset from tx order start
+*@pTxNo: return value of tx NO.
+*/
+static int ft5402_get_tx_order(struct i2c_client * client, u8 txNO, u8 *pTxNo)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client, FT5402_REG_TX_ORDER_START + txNO,
+ pTxNo);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if(ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_ORDER_START + txNO - FT5402_TX_TEST_MODE_1,
+ pTxNo);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*set tx cap
+*@txNO: tx NO.
+*@cap_value: value of cap
+*/
+static int ft5402_set_tx_cap(struct i2c_client * client, u8 txNO, u8 cap_value)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_write_reg(client, FT5402_REG_TX_CAP_START + txNO,
+ cap_value);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_CAP_START + txNO - FT5402_TX_TEST_MODE_1,
+ cap_value);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*get tx cap*/
+static int ft5402_get_tx_cap(struct i2c_client * client, u8 txNO, u8 *pCap)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client, FT5402_REG_TX_CAP_START + txNO,
+ pCap);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_CAP_START + txNO - FT5402_TX_TEST_MODE_1,
+ pCap);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*set tx offset*/
+static int ft5402_set_tx_offset(struct i2c_client * client, u8 txNO, u8 offset_value)
+{
+ unsigned char temp=0;
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1), &temp);
+ if (ReCode >= 0) {
+ if (txNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1),
+ (temp&0xf0) + (offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1),
+ (temp&0x0f) + (offset_value<<4));
+ }
+ } else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_DEVICE_MODE+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ &temp); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ if(txNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value<<4));
+ }
+ }
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*get tx offset*/
+static int ft5402_get_tx_offset(struct i2c_client * client, u8 txNO, u8 *pOffset)
+{
+ unsigned char temp=0;
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1), &temp);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_OFFSET_START+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ &temp);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ if (ReCode >= 0)
+ (txNO%2 == 0) ? (*pOffset = (temp&0x0f)) : (*pOffset = (temp>>4));
+ return ReCode;
+}
+
+/*set rx order*/
+static int ft5402_set_rx_order(struct i2c_client * client, u8 rxNO, u8 rxNO1)
+{
+ unsigned char ReCode = 0;
+ ReCode = ft5402_write_reg(client, FT5402_REG_RX_ORDER_START + rxNO,
+ rxNO1);
+ return ReCode;
+}
+
+/*get rx order*/
+static int ft5402_get_rx_order(struct i2c_client * client, u8 rxNO, u8 *prxNO1)
+{
+ unsigned char ReCode = 0;
+ ReCode = ft5402_read_reg(client, FT5402_REG_RX_ORDER_START + rxNO,
+ prxNO1);
+ return ReCode;
+}
+
+/*set rx cap*/
+static int ft5402_set_rx_cap(struct i2c_client * client, u8 rxNO, u8 cap_value)
+{
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1)
+ ReCode = ft5402_write_reg(client, FT5402_REG_RX_CAP_START + rxNO,
+ cap_value);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if(ReCode >= 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_CAP_START + rxNO - FT5402_RX_TEST_MODE_1,
+ cap_value);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*get rx cap*/
+static int ft5402_get_rx_cap(struct i2c_client * client, u8 rxNO, u8 *pCap)
+{
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client, FT5402_REG_RX_CAP_START + rxNO,
+ pCap);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if(ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_CAP_START + rxNO - FT5402_RX_TEST_MODE_1,
+ pCap);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*set rx offset*/
+static int ft5402_set_rx_offset(struct i2c_client * client, u8 rxNO, u8 offset_value)
+{
+ unsigned char temp=0;
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1), &temp);
+ if (ReCode >= 0) {
+ if (rxNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1),
+ (temp&0xf0) + (offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1),
+ (temp&0x0f) + (offset_value<<4));
+ }
+ }
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_DEVICE_MODE+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ &temp); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ if (rxNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value<<4));
+ }
+ }
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*get rx offset*/
+static int ft5402_get_rx_offset(struct i2c_client * client, u8 rxNO, u8 *pOffset)
+{
+ unsigned char temp = 0;
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1), &temp);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_OFFSET_START+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ &temp);
+
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ if (ReCode >= 0) {
+ if (0 == (rxNO%2))
+ *pOffset = (temp&0x0f);
+ else
+ *pOffset = (temp>>4);
+ }
+
+ return ReCode;
+}
+
+/*set tx num*/
+static int ft5402_set_tx_num(struct i2c_client *client, u8 txnum)
+{
+ return ft5402_write_reg(client, FT5402_REG_TX_NUM, txnum);
+}
+
+/*get tx num*/
+static int ft5402_get_tx_num(struct i2c_client *client, u8 *ptxnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_TX_NUM, ptxnum);
+}
+
+/*set rx num*/
+static int ft5402_set_rx_num(struct i2c_client *client, u8 rxnum)
+{
+ return ft5402_write_reg(client, FT5402_REG_RX_NUM, rxnum);
+}
+
+/*get rx num*/
+static int ft5402_get_rx_num(struct i2c_client *client, u8 *prxnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_RX_NUM, prxnum);
+}
+
+/*set resolution*/
+static int ft5402_set_Resolution(struct i2c_client *client, u16 x, u16 y)
+{
+ unsigned char cRet = 0;
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_X_H, ((unsigned char)(x>>8)));
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_X_L, ((unsigned char)(x&0x00ff)));
+
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_Y_H, ((unsigned char)(y>>8)));
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_Y_L, ((unsigned char)(y&0x00ff)));
+
+ return cRet;
+}
+
+/*get resolution*/
+static int ft5402_get_Resolution(struct i2c_client *client,
+ u16 *px, u16 *py)
+{
+ unsigned char cRet = 0, temp1 = 0, temp2 = 0;
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_X_H, &temp1);
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_X_L, &temp2);
+ (*px) = (((u16)temp1) << 8) | ((u16)temp2);
+
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_Y_H, &temp1);
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_Y_L, &temp2);
+ (*py) = (((u16)temp1) << 8) | ((u16)temp2);
+
+ return cRet;
+}
+
+
+/*set voltage*/
+static int ft5402_set_vol(struct i2c_client *client, u8 Vol)
+{
+ return ft5402_write_reg(client, FT5402_REG_VOLTAGE, Vol);
+}
+
+/*get voltage*/
+static int ft5402_get_vol(struct i2c_client *client, u8 *pVol)
+{
+ return ft5402_read_reg(client, FT5402_REG_VOLTAGE, pVol);
+}
+
+/*set gain*/
+static int ft5402_set_gain(struct i2c_client *client, u8 Gain)
+{
+ return ft5402_write_reg(client, FT5402_REG_GAIN, Gain);
+}
+
+/*get gain*/
+static int ft5402_get_gain(struct i2c_client *client, u8 *pGain)
+{
+ return ft5402_read_reg(client, FT5402_REG_GAIN, pGain);
+}
+
+/*get start rx*/
+static int ft5402_get_start_rx(struct i2c_client *client, u8 *pRx)
+{
+ return ft5402_read_reg(client, FT5402_REG_START_RX, pRx);
+}
+
+
+/*get adc target*/
+static int ft5402_get_adc_target(struct i2c_client *client, u16 *pvalue)
+{
+ int err = 0;
+ u8 tmp1, tmp2;
+ err = ft5402_read_reg(client, FT5402_REG_ADC_TARGET_HIGH,
+ &tmp1);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get adc target high failed\n",
+ __func__);
+ err = ft5402_read_reg(client, FT5402_REG_ADC_TARGET_LOW,
+ &tmp2);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get adc target low failed\n",
+ __func__);
+
+ *pvalue = ((u16)tmp1<<8) + (u16)tmp2;
+ return err;
+}
+
+static int ft5402_set_face_detect_statistics_tx_num(struct i2c_client *client, u8 prevalue)
+{
+ return ft5402_write_reg(client, FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM,
+ prevalue);
+}
+
+static int ft5402_get_face_detect_statistics_tx_num(struct i2c_client *client, u8 *pprevalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM,
+ pprevalue);
+}
+
+static int ft5402_set_face_detect_pre_value(struct i2c_client *client, u8 prevalue)
+{
+ return ft5402_write_reg(client, FT5402_REG_FACE_DETECT_PRE_VALUE,
+ prevalue);
+}
+
+static int ft5402_get_face_detect_pre_value(struct i2c_client *client, u8 *pprevalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_FACE_DETECT_PRE_VALUE,
+ pprevalue);
+}
+
+static int ft5402_set_face_detect_num(struct i2c_client *client, u8 num)
+{
+ return ft5402_write_reg(client, FT5402_REG_FACE_DETECT_NUM,
+ num);
+}
+
+static int ft5402_get_face_detect_num(struct i2c_client *client, u8 *pnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_FACE_DETECT_NUM,
+ pnum);
+}
+
+
+static int ft5402_set_peak_value_min(struct i2c_client *client, u8 min)
+{
+ return ft5402_write_reg(client, FT5402_REG_BIGAREA_PEAK_VALUE_MIN,
+ min);
+}
+
+static int ft5402_get_peak_value_min(struct i2c_client *client, u8 *pmin)
+{
+ return ft5402_read_reg(client, FT5402_REG_BIGAREA_PEAK_VALUE_MIN,
+ pmin);
+}
+
+static int ft5402_set_diff_value_over_num(struct i2c_client *client, u8 num)
+{
+ return ft5402_write_reg(client, FT5402_REG_BIGAREA_DIFF_VALUE_OVER_NUM,
+ num);
+}
+static int ft5402_get_diff_value_over_num(struct i2c_client *client, u8 *pnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_BIGAREA_DIFF_VALUE_OVER_NUM,
+ pnum);
+}
+
+
+static int ft5402_set_customer_id(struct i2c_client *client, u8 num)
+{
+ return ft5402_write_reg(client, FT5402_REG_CUSTOMER_ID,
+ num);
+}
+static int ft5402_get_customer_id(struct i2c_client *client, u8 *pnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_CUSTOMER_ID,
+ pnum);
+}
+
+static int ft5402_set_kx(struct i2c_client *client, u16 value)
+{
+ int err = 0;
+ err = ft5402_write_reg(client, FT5402_REG_KX_H,
+ value >> 8);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set kx high failed\n",
+ __func__);
+ err = ft5402_write_reg(client, FT5402_REG_KX_L,
+ value);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set kx low failed\n",
+ __func__);
+
+ return err;
+}
+
+static int ft5402_get_kx(struct i2c_client *client, u16 *pvalue)
+{
+ int err = 0;
+ u8 tmp1, tmp2;
+ err = ft5402_read_reg(client, FT5402_REG_KX_H,
+ &tmp1);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get kx high failed\n",
+ __func__);
+ err = ft5402_read_reg(client, FT5402_REG_KX_L,
+ &tmp2);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get kx low failed\n",
+ __func__);
+
+ *pvalue = ((u16)tmp1<<8) + (u16)tmp2;
+ return err;
+}
+static int ft5402_set_ky(struct i2c_client *client, u16 value)
+{
+ int err = 0;
+ err = ft5402_write_reg(client, FT5402_REG_KY_H,
+ value >> 8);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set ky high failed\n",
+ __func__);
+ err = ft5402_write_reg(client, FT5402_REG_KY_L,
+ value);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set ky low failed\n",
+ __func__);
+
+ return err;
+}
+
+static int ft5402_get_ky(struct i2c_client *client, u16 *pvalue)
+{
+ int err = 0;
+ u8 tmp1, tmp2;
+ err = ft5402_read_reg(client, FT5402_REG_KY_H,
+ &tmp1);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get ky high failed\n",
+ __func__);
+ err = ft5402_read_reg(client, FT5402_REG_KY_L,
+ &tmp2);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get ky low failed\n",
+ __func__);
+
+ *pvalue = ((u16)tmp1<<8) + (u16)tmp2;
+ return err;
+}
+static int ft5402_set_lemda_x(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_LEMDA_X,
+ value);
+}
+
+static int ft5402_get_lemda_x(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_LEMDA_X,
+ pvalue);
+}
+static int ft5402_set_lemda_y(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_LEMDA_Y,
+ value);
+}
+
+static int ft5402_get_lemda_y(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_LEMDA_Y,
+ pvalue);
+}
+static int ft5402_set_pos_x(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_DIRECTION,
+ value);
+}
+
+static int ft5402_get_pos_x(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_DIRECTION,
+ pvalue);
+}
+
+static int ft5402_set_scan_select(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_SCAN_SELECT,
+ value);
+}
+
+static int ft5402_get_scan_select(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_SCAN_SELECT,
+ pvalue);
+}
+
+static int ft5402_set_other_param(struct i2c_client *client)
+{
+ int err = 0;
+ err = ft5402_write_reg(client, FT5402_REG_THGROUP, (u8)(g_param_ft5402.ft5402_THGROUP));
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THGROUP failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_THPEAK, g_param_ft5402.ft5402_THPEAK);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THPEAK failed.\n",
+ __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_PWMODE_CTRL,
+ g_param_ft5402.ft5402_PWMODE_CTRL);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PERIOD_CTRL failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_PERIOD_ACTIVE,
+ g_param_ft5402.ft5402_PERIOD_ACTIVE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PERIOD_ACTIVE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM,
+ g_param_ft5402.ft5402_FACE_DETECT_STATISTICS_TX_NUM);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FACE_DETECT_STATISTICS_TX_NUM failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_MAX_TOUCH_VALUE_HIGH,
+ g_param_ft5402.ft5402_MAX_TOUCH_VALUE>>8);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_HIGH failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_MAX_TOUCH_VALUE_LOW,
+ g_param_ft5402.ft5402_MAX_TOUCH_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_LOW failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FACE_DETECT_MODE,
+ g_param_ft5402.ft5402_FACE_DETECT_MODE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FACE_DETECT_MODE failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_DRAW_LINE_TH,
+ g_param_ft5402.ft5402_DRAW_LINE_TH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write DRAW_LINE_TH failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_POINTS_SUPPORTED,
+ g_param_ft5402.ft5402_POINTS_SUPPORTED);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write POINTS_SUPPORTED failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_ESD_FILTER_FRAME,
+ g_param_ft5402.ft5402_ESD_FILTER_FRAME);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_FILTER_FRAME failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_POINTS_STABLE_MACRO,
+ g_param_ft5402.ft5402_POINTS_STABLE_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_POINTS_STABLE_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_DELTA_X,
+ g_param_ft5402.ft5402_MIN_DELTA_X);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_DELTA_X failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_DELTA_Y,
+ g_param_ft5402.ft5402_MIN_DELTA_Y);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_DELTA_Y failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_DELTA_STEP,
+ g_param_ft5402.ft5402_MIN_DELTA_STEP);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_DELTA_STEP failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_NOISE_MACRO,
+ g_param_ft5402.ft5402_ESD_NOISE_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_NOISE_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_DIFF_VAL,
+ g_param_ft5402.ft5402_ESD_DIFF_VAL);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_DIFF_VAL failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_NEGTIVE,
+ g_param_ft5402.ft5402_ESD_NEGTIVE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_NEGTIVE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_FILTER_FRAMES,
+ g_param_ft5402.ft5402_ESD_FILTER_FRAMES);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_FILTER_FRAMES failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_IO_LEVEL_SELECT,
+ g_param_ft5402.ft5402_IO_LEVEL_SELECT);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_IO_LEVEL_SELECT failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_POINTID_DELAY_COUNT,
+ g_param_ft5402.ft5402_POINTID_DELAY_COUNT);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_POINTID_DELAY_COUNT failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_LIFTUP_FILTER_MACRO,
+ g_param_ft5402.ft5402_LIFTUP_FILTER_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_LIFTUP_FILTER_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_DIFF_HANDLE_MACRO,
+ g_param_ft5402.ft5402_DIFF_HANDLE_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_DIFF_HANDLE_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_WATER,
+ g_param_ft5402.ft5402_MIN_WATER);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_WATER failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MAX_NOISE,
+ g_param_ft5402.ft5402_MAX_NOISE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MAX_NOISE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_WATER_START_RX,
+ g_param_ft5402.ft5402_WATER_START_RX);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_WATER_START_RX failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_WATER_START_TX,
+ g_param_ft5402.ft5402_WATER_START_TX);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_WATER_START_TX failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO,
+ g_param_ft5402.ft5402_HOST_NUMBER_SUPPORTED_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_HOST_NUMBER_SUPPORTED_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_RAISE_THGROUP,
+ g_param_ft5402.ft5402_RAISE_THGROUP);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_RAISE_THGROUP failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_CHARGER_STATE,
+ g_param_ft5402.ft5402_CHARGER_STATE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_CHARGER_STATE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FILTERID_START,
+ g_param_ft5402.ft5402_FILTERID_START);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FILTERID_START failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO,
+ g_param_ft5402.ft5402_FRAME_FILTER_EN_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_EN_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH,
+ g_param_ft5402.ft5402_FRAME_FILTER_SUB_MAX_TH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_SUB_MAX_TH failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH,
+ g_param_ft5402.ft5402_FRAME_FILTER_ADD_MAX_TH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_ADD_MAX_TH failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ g_param_ft5402.ft5402_FRAME_FILTER_SKIP_START_FRAME);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_SKIP_START_FRAME failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_EN,
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_EN);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_BAND_EN failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH,
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_WIDTH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_BAND_WIDTH failed.\n", __func__);
+ return err;
+ }
+
+ return err;
+}
+
+static int ft5402_get_other_param(struct i2c_client *client)
+{
+ int err = 0;
+ u8 value = 0x00;
+ err = ft5402_read_reg(client, FT5402_REG_THGROUP, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THGROUP failed.\n", __func__);
+ return err;
+ } else
+ DBG("THGROUP=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_THPEAK, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THPEAK failed.\n",
+ __func__);
+ return err;
+ } else
+ DBG("THPEAK=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_PWMODE_CTRL, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PWMODE_CTRL failed.\n", __func__);
+ return err;
+ } else
+ DBG("CTRL=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_PERIOD_ACTIVE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PERIOD_ACTIVE failed.\n", __func__);
+ return err;
+ } else
+ DBG("PERIOD_ACTIVE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_MAX_TOUCH_VALUE_HIGH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_HIGH failed.\n", __func__);
+ return err;
+ } else
+ DBG("MAX_TOUCH_VALUE_HIGH=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_MAX_TOUCH_VALUE_LOW,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_LOW failed.\n", __func__);
+ return err;
+ } else
+ DBG("MAX_TOUCH_VALUE_LOW=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_FACE_DETECT_MODE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FACE_DETECT_MODE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FACE_DEC_MODE=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_DRAW_LINE_TH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write DRAW_LINE_TH failed.\n", __func__);
+ return err;
+ } else
+ DBG("DRAW_LINE_TH=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_POINTS_SUPPORTED,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_POINTS_SUPPORTED failed.\n", __func__);
+ return err;
+ } else
+ DBG("ft5402_POINTS_SUPPORTED=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_ESD_FILTER_FRAME,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_ESD_FILTER_FRAME failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_ESD_FILTER_FRAME=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_POINTS_STABLE_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_POINTS_STABLE_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_POINTS_STABLE_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_DELTA_X,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_DELTA_X failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_DELTA_X=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_DELTA_Y,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_DELTA_Y failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_DELTA_Y=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_DELTA_STEP,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_DELTA_STEP failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_DELTA_STEP=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_NOISE_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_NOISE_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_NOISE_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_DIFF_VAL,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_DIFF_VAL failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_DIFF_VAL=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_NEGTIVE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_NEGTIVE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_NEGTIVE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_FILTER_FRAMES,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_FILTER_FRAMES failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_FILTER_FRAMES=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_IO_LEVEL_SELECT,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_IO_LEVEL_SELECT failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_IO_LEVEL_SELECT=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_POINTID_DELAY_COUNT,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_POINTID_DELAY_COUNT failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_POINTID_DELAY_COUNT=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_LIFTUP_FILTER_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_LIFTUP_FILTER_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_LIFTUP_FILTER_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_DIFF_HANDLE_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_DIFF_HANDLE_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_DIFF_HANDLE_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_WATER,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_WATER failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_WATER=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MAX_NOISE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MAX_NOISE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MAX_NOISE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_WATER_START_RX,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_WATER_START_RX failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_WATER_START_RX=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_WATER_START_TX,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_WATER_START_TX failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_WATER_START_TX=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_RAISE_THGROUP,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_RAISE_THGROUP failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_RAISE_THGROUP=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_CHARGER_STATE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_CHARGER_STATE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_CHARGER_STATE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FILTERID_START,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FILTERID_START failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FILTERID_START=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_EN,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_BAND_EN failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_BAND_EN=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH=%02x\n", value);
+
+ return err;
+}
+int ft5402_get_ic_param(struct i2c_client *client)
+{
+ int err = 0;
+ int i = 0;
+ u8 value = 0x00;
+ u16 xvalue = 0x0000, yvalue = 0x0000;
+
+ /*enter factory mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_FACTORYMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter factory mode failed.\n", __func__);
+ goto RETURN_WORK;
+ }
+
+ for (i = 0; i < g_ft5402_tx_num; i++) {
+ DBG("tx%d:", i);
+ /*get tx order*/
+ err = ft5402_get_tx_order(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("order=%d ", value);
+ /*get tx cap*/
+ err = ft5402_get_tx_cap(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("cap=%02x\n", value);
+ }
+ /*get tx offset*/
+ err = ft5402_get_tx_offset(client, 0, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx 0 offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("tx offset = %02x\n", value);
+
+ /*get rx offset and cap*/
+ for (i = 0; i < g_ft5402_rx_num; i++) {
+ /*get rx order*/
+ DBG("rx%d:", i);
+ err = ft5402_get_rx_order(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("order=%d ", value);
+ /*get rx cap*/
+ err = ft5402_get_rx_cap(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("cap=%02x ", value);
+ err = ft5402_get_rx_offset(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ DBG("offset=%02x\n", value);
+ }
+
+ /*get scan select*/
+ err = ft5402_get_scan_select(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get scan select.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("scan select = %02x\n", value);
+
+ /*get tx number*/
+ err = ft5402_get_tx_num(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("tx num = %02x\n", value);
+ /*get rx number*/
+ err = ft5402_get_rx_num(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("rx num = %02x\n", value);
+
+ /*get gain*/
+ err = ft5402_get_gain(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get gain.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("gain = %02x\n", value);
+ /*get voltage*/
+ err = ft5402_get_vol(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get voltage.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("voltage = %02x\n", value);
+ /*get start rx*/
+ err = ft5402_get_start_rx(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get start rx.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("start rx = %02x\n", value);
+ /*get adc target*/
+ err = ft5402_get_adc_target(client, &xvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get adc target.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("ADC target = %02x\n", xvalue);
+
+
+RETURN_WORK:
+ /*enter work mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_WORKMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter work mode failed.\n", __func__);
+ goto ERR_EXIT;
+ }
+
+ /*get resolution*/
+ err = ft5402_get_Resolution(client, &xvalue, &yvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get resolution.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("resolution X = %d Y = %d\n", xvalue, yvalue);
+
+
+ /*get face detect statistics tx num*/
+ err = ft5402_get_face_detect_statistics_tx_num(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not get face detect statistics tx num.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_FACE_DETECT_STATISTICS_TX_NUM = %02x\n", value);
+ /*get face detect pre value*/
+ err = ft5402_get_face_detect_pre_value(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not get face detect pre value.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_FACE_DETECT_PRE_VALUE = %02x\n", value);
+ /*get face detect num*/
+ err = ft5402_get_face_detect_num(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get face detect num.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("face detect num = %02x\n", value);
+
+ /*get min peak value*/
+ err = ft5402_get_peak_value_min(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get min peak value.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_BIGAREA_PEAK_VALUE_MIN = %02x\n", value);
+ /*get diff value over num*/
+ err = ft5402_get_diff_value_over_num(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get diff value over num.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_BIGAREA_DIFF_VALUE_OVER_NUM = %02x\n", value);
+ /*get customer id*/
+ err = ft5402_get_customer_id(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get customer id.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_CUSTOMER_ID = %02x\n", value);
+ /*get kx*/
+ err = ft5402_get_kx(client, &xvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get kx.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("kx = %02x\n", xvalue);
+ /*get ky*/
+ err = ft5402_get_ky(client, &xvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get ky.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("ky = %02x\n", xvalue);
+ /*get lemda x*/
+ err = ft5402_get_lemda_x(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get lemda x.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("lemda x = %02x\n", value);
+ /*get lemda y*/
+ err = ft5402_get_lemda_y(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get lemda y.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("lemda y = %02x\n", value);
+ /*get pos x*/
+ err = ft5402_get_pos_x(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get pos x.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("pos x = %02x\n", value);
+
+ err = ft5402_get_other_param(client);
+
+ERR_EXIT:
+ return err;
+}
+
+int ft5402_Init_IC_Param(struct i2c_client *client)
+{
+ int err = 0;
+ int i = 0;
+
+ /*enter factory mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_FACTORYMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter factory mode failed.\n", __func__);
+ goto RETURN_WORK;
+ }
+
+ for (i = 0; i < g_ft5402_tx_num; i++) {
+ if (g_ft5402_tx_order[i] != 0xFF) {
+ /*set tx order*/
+ err = ft5402_set_tx_order(client, i, g_ft5402_tx_order[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ }
+ /*set tx cap*/
+ err = ft5402_set_tx_cap(client, i, g_ft5402_tx_cap[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ }
+ /*set tx offset*/
+ err = ft5402_set_tx_offset(client, 0, g_ft5402_tx_offset);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx 0 offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ /*set rx offset and cap*/
+ for (i = 0; i < g_ft5402_rx_num; i++) {
+ /*set rx order*/
+ err = ft5402_set_rx_order(client, i, g_ft5402_rx_order[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ /*set rx cap*/
+ err = ft5402_set_rx_cap(client, i, g_ft5402_rx_cap[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ }
+ for (i = 0; i < g_ft5402_rx_num/2; i++) {
+ err = ft5402_set_rx_offset(client, i*2, g_ft5402_rx_offset[i]>>4);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ err = ft5402_set_rx_offset(client, i*2+1, g_ft5402_rx_offset[i]&0x0F);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ }
+
+ /*set scan select*/
+ err = ft5402_set_scan_select(client, g_ft5402_scanselect);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set scan select.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ /*set tx number*/
+ err = ft5402_set_tx_num(client, g_ft5402_tx_num);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ /*set rx number*/
+ err = ft5402_set_rx_num(client, g_ft5402_rx_num);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ /*set gain*/
+ err = ft5402_set_gain(client, g_ft5402_gain);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set gain.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ /*set voltage*/
+ err = ft5402_set_vol(client, g_ft5402_voltage);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set voltage.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_ADC_TARGET_HIGH,
+ g_param_ft5402.ft5402_ADC_TARGET>>8);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ADC_TARGET_HIGH failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_ADC_TARGET_LOW,
+ g_param_ft5402.ft5402_ADC_TARGET);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ADC_TARGET_LOW failed.\n", __func__);
+ return err;
+ }
+
+RETURN_WORK:
+ /*enter work mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_WORKMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter work mode failed.\n", __func__);
+ goto ERR_EXIT;
+ }
+
+ /*set resolution*/
+ err = ft5402_set_Resolution(client, g_param_ft5402.ft5402_RESOLUTION_X,
+ g_param_ft5402.ft5402_RESOLUTION_Y);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set resolution.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+
+ /*set face detect statistics tx num*/
+ err = ft5402_set_face_detect_statistics_tx_num(client,
+ g_param_ft5402.ft5402_FACE_DETECT_STATISTICS_TX_NUM);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not set face detect statistics tx num.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set face detect pre value*/
+ err = ft5402_set_face_detect_pre_value(client,
+ g_param_ft5402.ft5402_FACE_DETECT_PRE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not set face detect pre value.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set face detect num*/
+ err = ft5402_set_face_detect_num(client,
+ g_param_ft5402.ft5402_FACE_DETECT_NUM);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set face detect num.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+
+ /*set min peak value*/
+ err = ft5402_set_peak_value_min(client,
+ g_param_ft5402.ft5402_BIGAREA_PEAK_VALUE_MIN);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set min peak value.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set diff value over num*/
+ err = ft5402_set_diff_value_over_num(client,
+ g_param_ft5402.ft5402_BIGAREA_DIFF_VALUE_OVER_NUM);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set diff value over num.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set customer id*/
+ err = ft5402_set_customer_id(client,
+ g_param_ft5402.ft5402_CUSTOMER_ID);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set customer id.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set kx*/
+ err = ft5402_set_kx(client, g_param_ft5402.ft5402_KX);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set kx.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set ky*/
+ err = ft5402_set_ky(client, g_param_ft5402.ft5402_KY);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set ky.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set lemda x*/
+ err = ft5402_set_lemda_x(client,
+ g_param_ft5402.ft5402_LEMDA_X);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set lemda x.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set lemda y*/
+ err = ft5402_set_lemda_y(client,
+ g_param_ft5402.ft5402_LEMDA_Y);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set lemda y.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set pos x*/
+ err = ft5402_set_pos_x(client, g_param_ft5402.ft5402_DIRECTION);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set pos x.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+
+ err = ft5402_set_other_param(client);
+
+ERR_EXIT:
+ return err;
+}
+
+
+char dst[512];
+static char * ft5402_sub_str(char * src, int n)
+{
+ char *p = src;
+ int i;
+ int m = 0;
+ int len = strlen(src);
+
+ while (n >= 1 && m <= len) {
+ i = 0;
+ dst[10] = ' ';
+ n--;
+ while ( *p != ',' && *p != ' ') {
+ dst[i++] = *(p++);
+ m++;
+ if (i >= len)
+ break;
+ }
+ dst[i++] = '\0';
+ p++;
+ }
+ return dst;
+}
+static int ft5402_GetInISize(char *config_name)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize = 0;
+ char filepath[128];
+ memset(filepath, 0, sizeof(filepath));
+
+ sprintf(filepath, "%s%s", FT5402_INI_FILEPATH, config_name);
+
+ if (NULL == pfile)
+ pfile = filp_open(filepath, O_RDONLY, 0);
+
+ if (IS_ERR(pfile)) {
+ pr_err("error occured while opening file %s.\n", filepath);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ filp_close(pfile, NULL);
+ return fsize;
+}
+
+static int ft5x0x_ReadInIData(char *config_name,
+ char *config_buf)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize;
+ char filepath[128];
+ loff_t pos;
+ mm_segment_t old_fs;
+
+ memset(filepath, 0, sizeof(filepath));
+ sprintf(filepath, "%s%s", FT5402_INI_FILEPATH, config_name);
+ if (NULL == pfile)
+ pfile = filp_open(filepath, O_RDONLY, 0);
+ if (IS_ERR(pfile)) {
+ pr_err("error occured while opening file %s.\n", filepath);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ pos = 0;
+ vfs_read(pfile, config_buf, fsize, &pos);
+ filp_close(pfile, NULL);
+ set_fs(old_fs);
+
+ return 0;
+}
+
+int ft5402_Get_Param_From_Ini(char *config_name)
+{
+ char key[64];
+ char value[512];
+ char section[64];
+ int i = 0;//,ret=0;
+ int j = 0;
+ char *filedata = NULL;
+ unsigned char legal_byte1 = 0x00;
+ unsigned char legal_byte2 = 0x00;
+
+ int inisize = ft5402_GetInISize(config_name);
+
+ if (inisize <= 0) {
+ pr_err("%s ERROR:Get firmware size failed\n",
+ __func__);
+ return -EIO;
+ }
+
+ filedata = kmalloc(inisize + 1, GFP_ATOMIC);
+
+ if (ft5x0x_ReadInIData(config_name, filedata)) {
+ pr_err("%s() - ERROR: request_firmware failed\n",
+ __func__);
+ kfree(filedata);
+ return -EIO;
+ }
+
+ /*check ini if it is illegal*/
+ sprintf(section, "%s", FT5402_APP_LEGAL);
+ sprintf(key, "%s", FT5402_APP_LEGAL_BYTE_1_STR);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ legal_byte1 = atoi(value);
+ DBG("legal_byte1=%s\n", value);
+ sprintf(key, "%s", FT5402_APP_LEGAL_BYTE_2_STR);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ legal_byte2 = atoi(value);
+ DBG("lega2_byte1=%s\n", value);
+ if(FT5402_APP_LEGAL_BYTE_1_VALUE == legal_byte1 &&
+ FT5402_APP_LEGAL_BYTE_2_VALUE == legal_byte2)
+ DBG("the ini file is valid\n");
+ else {
+ pr_err("[FTS]-----the ini file is invalid!please check it.\n");
+ goto ERROR_RETURN;
+ }
+
+ /*get ini param*/
+ sprintf(section, "%s", FT5402_APP_NAME);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_KX = atoi(value);
+ DBG("ft5402_KX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_KY = atoi(value);
+ DBG("ft5402_KY=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_LEMDA_X = atoi(value);
+ DBG("ft5402_LEMDA_X=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_LEMDA_Y = atoi(value);
+ DBG("ft5402_LEMDA_Y=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_RESOLUTION_X = atoi(value);
+ DBG("ft5402_RESOLUTION_X=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_RESOLUTION_Y = atoi(value);
+ DBG("ft5402_RESOLUTION_Y=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_DIRECTION= atoi(value);
+ DBG("ft5402_DIRECTION=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_PRE_VALUE = atoi(value);
+ DBG("ft5402_FACE_DETECT_PRE_VALUE=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_NUM = atoi(value);
+ DBG("ft5402_FACE_DETECT_NUM=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_BIGAREA_PEAK_VALUE_MIN = atoi(value);/*The min value to be decided as the big point*/
+ DBG("ft5402_BIGAREA_PEAK_VALUE_MIN=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_BIGAREA_DIFF_VALUE_OVER_NUM = atoi(value);/*The min big points of the big area*/
+ DBG("ft5402_BIGAREA_DIFF_VALUE_OVER_NUM=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_CUSTOMER_ID = atoi(value);
+ DBG("ft5402_CUSTOM_ID=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_PERIOD_ACTIVE = atoi(value);
+ DBG("ft5402_PERIOD_ACTIVE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_STATISTICS_TX_NUM = atoi(value);
+ DBG("ft5402_FACE_DETECT_STATISTICS_TX_NUM=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_THGROUP = atoi(value);
+ DBG("ft5402_THGROUP=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_THPEAK = atoi(value);
+ DBG("ft5402_THPEAK=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_MODE = atoi(value);
+ DBG("ft5402_FACE_DETECT_MODE=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MAX_TOUCH_VALUE = atoi(value);
+ DBG("ft5402_MAX_TOUCH_VALUE=%s\n", value);
+
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_PWMODE_CTRL= atoi(value);
+ DBG("ft5402_PWMODE_CTRL=%s\n", value);
+
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+
+ i++;
+ g_param_ft5402.ft5402_DRAW_LINE_TH = atoi(value);
+ DBG("ft5402_DRAW_LINE_TH=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_POINTS_SUPPORTED= atoi(value);
+ DBG("ft5402_POINTS_SUPPORTED=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_START_RX = atoi(value);
+ DBG("ft5402_START_RX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+
+ g_param_ft5402.ft5402_ADC_TARGET = atoi(value);
+ DBG("ft5402_ADC_TARGET=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+
+ g_param_ft5402.ft5402_ESD_FILTER_FRAME = atoi(value);
+ DBG("ft5402_ESD_FILTER_FRAME=%s\n", value);
+
+/*********************************************************************/
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_tx_num = atoi(value);
+ DBG("ft5402_tx_num=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_rx_num = atoi(value);
+ DBG("ft5402_rx_num=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_gain = atoi(value);
+ DBG("ft5402_gain=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_voltage = atoi(value);
+ DBG("ft5402_voltage=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_scanselect = atoi(value);
+ DBG("ft5402_scanselect=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_tx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_tx_order[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_tx_order=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_tx_offset = atoi(value);
+ DBG("ft5402_tx_offset=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_tx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_tx_cap[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_tx_cap=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_rx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_rx_order[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_rx_order=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_rx_num/2; j++)
+ {
+ char * psrc = value;
+ g_ft5402_rx_offset[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_rx_offset=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_rx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_rx_cap[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_rx_cap=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_POINTS_STABLE_MACRO = atoi(value);
+ DBG("ft5402_POINTS_STABLE_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_DELTA_X = atoi(value);
+ DBG("ft5402_MIN_DELTA_X=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_DELTA_Y = atoi(value);
+ DBG("ft5402_MIN_DELTA_Y=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_DELTA_STEP = atoi(value);
+ DBG("ft5402_MIN_DELTA_STEP=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_NOISE_MACRO = atoi(value);
+ DBG("ft5402_ESD_NOISE_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_DIFF_VAL = atoi(value);
+ DBG("ft5402_ESD_DIFF_VAL=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_NEGTIVE = atoi(value);
+ DBG("ft5402_ESD_NEGTIVE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_FILTER_FRAMES = atoi(value);
+ DBG("ft5402_ESD_FILTER_FRAMES=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_IO_LEVEL_SELECT = atoi(value);
+ DBG("ft5402_IO_LEVEL_SELECT=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_POINTID_DELAY_COUNT = atoi(value);
+ DBG("ft5402_POINTID_DELAY_COUNT=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_LIFTUP_FILTER_MACRO = atoi(value);
+ DBG("ft5402_LIFTUP_FILTER_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_DIFF_HANDLE_MACRO = atoi(value);
+ DBG("ft5402_DIFF_HANDLE_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_WATER = atoi(value);
+ DBG("ft5402_MIN_WATER=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MAX_NOISE = atoi(value);
+ DBG("ft5402_MAX_NOISE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_WATER_START_RX = atoi(value);
+ DBG("ft5402_WATER_START_RX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_WATER_START_TX = atoi(value);
+ DBG("ft5402_WATER_START_TX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_HOST_NUMBER_SUPPORTED_MACRO = atoi(value);
+ DBG("ft5402_HOST_NUMBER_SUPPORTED_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_RAISE_THGROUP = atoi(value);
+ DBG("ft5402_RAISE_THGROUP=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_CHARGER_STATE = atoi(value);
+ DBG("ft5402_CHARGER_STATE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FILTERID_START = atoi(value);
+ DBG("ft5402_FILTERID_START=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_EN_MACRO = atoi(value);
+ DBG("ft5402_FRAME_FILTER_EN_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_SUB_MAX_TH = atoi(value);
+ DBG("ft5402_FRAME_FILTER_SUB_MAX_TH=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_ADD_MAX_TH = atoi(value);
+ DBG("ft5402_FRAME_FILTER_ADD_MAX_TH=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_SKIP_START_FRAME = atoi(value);
+ DBG("ft5402_FRAME_FILTER_SKIP_START_FRAME=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_EN = atoi(value);
+ DBG("ft5402_FRAME_FILTER_BAND_EN=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_WIDTH = atoi(value);
+ DBG("ft5402_FRAME_FILTER_BAND_WIDTH=%s\n", value);
+
+
+ if (filedata)
+ kfree(filedata);
+ return 0;
+ERROR_RETURN:
+ if (filedata)
+ kfree(filedata);
+ return -1;
+}
+
diff --git a/drivers/input/touchscreen/ft5x0x/ft5402_config.h b/drivers/input/touchscreen/ft5x0x/ft5402_config.h
new file mode 100755
index 00000000..b0c63889
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ft5402_config.h
@@ -0,0 +1,71 @@
+#ifndef __FT5402_CONFIG_H__
+#define __FT5402_CONFIG_H__
+/*FT5402 config*/
+
+
+#define FT5402_START_RX 0
+#define FT5402_ADC_TARGET 8500
+#define FT5402_KX 120
+#define FT5402_KY 120
+#define FT5402_RESOLUTION_X 480
+#define FT5402_RESOLUTION_Y 800
+#define FT5402_LEMDA_X 0
+#define FT5402_LEMDA_Y 0
+#define FT5402_PWMODE_CTRL 1
+#define FT5402_POINTS_SUPPORTED 5
+#define FT5402_DRAW_LINE_TH 150
+#define FT5402_FACE_DETECT_MODE 0
+#define FT5402_FACE_DETECT_STATISTICS_TX_NUM 3
+#define FT5402_FACE_DETECT_PRE_VALUE 20
+#define FT5402_FACE_DETECT_NUM 10
+#define FT5402_THGROUP 25
+#define FT5402_THPEAK 60
+#define FT5402_BIGAREA_PEAK_VALUE_MIN 100
+#define FT5402_BIGAREA_DIFF_VALUE_OVER_NUM 50
+#define FT5402_MIN_DELTA_X 2
+#define FT5402_MIN_DELTA_Y 2
+#define FT5402_MIN_DELTA_STEP 2
+#define FT5402_ESD_DIFF_VAL 20
+#define FT5402_ESD_NEGTIVE -50
+#define FT5402_ESD_FILTER_FRAME 10
+#define FT5402_MAX_TOUCH_VALUE 600
+#define FT5402_CUSTOMER_ID 121
+#define FT5402_IO_LEVEL_SELECT 0
+#define FT5402_DIRECTION 1
+#define FT5402_POINTID_DELAY_COUNT 3
+#define FT5402_LIFTUP_FILTER_MACRO 1
+#define FT5402_POINTS_STABLE_MACRO 1
+#define FT5402_ESD_NOISE_MACRO 1
+#define FT5402_RV_G_PERIOD_ACTIVE 16
+#define FT5402_DIFFDATA_HANDLE 1
+#define FT5402_MIN_WATER_VAL -50
+#define FT5402_MAX_NOISE_VAL 10
+#define FT5402_WATER_HANDLE_START_RX 0
+#define FT5402_WATER_HANDLE_START_TX 0
+#define FT5402_HOST_NUMBER_SUPPORTED 1
+#define FT5402_RV_G_RAISE_THGROUP 30
+#define FT5402_RV_G_CHARGER_STATE 0
+#define FT5402_RV_G_FILTERID_START 2
+#define FT5402_FRAME_FILTER_EN 1
+#define FT5402_FRAME_FILTER_SUB_MAX_TH 2
+#define FT5402_FRAME_FILTER_ADD_MAX_TH 2
+#define FT5402_FRAME_FILTER_SKIP_START_FRAME 6
+#define FT5402_FRAME_FILTER_BAND_EN 1
+#define FT5402_FRAME_FILTER_BAND_WIDTH 128
+#define FT5402_OTP_PARAM_ID 0
+
+
+unsigned char g_ft5402_tx_num = 27;
+unsigned char g_ft5402_rx_num = 16;
+unsigned char g_ft5402_gain = 10;
+unsigned char g_ft5402_voltage = 3;
+unsigned char g_ft5402_scanselect = 12;
+unsigned char g_ft5402_tx_order[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26};
+unsigned char g_ft5402_tx_offset = 2;
+unsigned char g_ft5402_tx_cap[] = {42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42};
+unsigned char g_ft5402_rx_order[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+unsigned char g_ft5402_rx_offset[] = {68,68,68,68,68,68,68,68};
+unsigned char g_ft5402_rx_cap[] = {84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84};
+
+
+#endif \ No newline at end of file
diff --git a/drivers/input/touchscreen/ft5x0x/ft5402_ini_config.h b/drivers/input/touchscreen/ft5x0x/ft5402_ini_config.h
new file mode 100755
index 00000000..138f42e2
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ft5402_ini_config.h
@@ -0,0 +1,411 @@
+#ifndef __LINUX_FT5402_INI_CONFIG_H__
+#define __LINUX_FT5402_INI_CONFIG_H
+
+
+/*Init param register address*/
+/*factory mode register from 14-131*/
+#define FT5402_REG_TX_NUM 0x03
+#define FT5402_REG_RX_NUM 0x04
+#define FT5402_REG_VOLTAGE 0x05
+#define FT5402_REG_GAIN 0x07
+#define FT5402_REG_SCAN_SELECT 0x4E
+#define FT5402_REG_TX_ORDER_START 0x50
+#define FT5402_REG_TX_CAP_START 0x78
+#define FT5402_REG_TX_OFFSET_START 0xBF
+#define FT5402_REG_RX_ORDER_START 0xeb
+#define FT5402_REG_RX_CAP_START 0xA0
+#define FT5402_REG_RX_OFFSET_START 0xD3
+#define FT5402_REG_START_RX 0x06
+#define FT5402_REG_ADC_TARGET_HIGH 0x08
+#define FT5402_REG_ADC_TARGET_LOW 0x09
+
+
+#define FT5402_REG_DEVICE_MODE 0x00
+
+
+/*work mode register from 0-13(0,1,12,13verify or Reserved)and 132-177(159 Reserved)*/
+#define FT5402_REG_THGROUP (0x00+0x80)
+#define FT5402_REG_THPEAK (0x01+0x80)
+#define FT5402_REG_PWMODE_CTRL (0x06+0x80)
+#define FT5402_REG_PERIOD_ACTIVE (0x59+0x80)
+#define FT5402_REG_POINTS_SUPPORTED (0x0A+0x80)
+#define FT5402_REG_ESD_FILTER_FRAME (0x4F+0x80)
+
+#define FT5402_REG_RESOLUTION_X_H (0x18+0x80)
+#define FT5402_REG_RESOLUTION_X_L (0x19+0x80)
+#define FT5402_REG_RESOLUTION_Y_H (0x1a+0x80)
+#define FT5402_REG_RESOLUTION_Y_L (0x1b+0x80)
+#define FT5402_REG_KX_H (0x1c+0x80)
+#define FT5402_REG_KX_L (0x9d)
+#define FT5402_REG_KY_H (0x9e)
+#define FT5402_REG_KY_L (0x1f+0x80)
+#define FT5402_REG_CUSTOMER_ID (0xA8)
+#define FT5402_REG_DRAW_LINE_TH (0xAe)
+#define FT5402_REG_FACE_DETECT_MODE (0xB0)
+#define FT5402_REG_MAX_TOUCH_VALUE_HIGH (0xD0)
+#define FT5402_REG_MAX_TOUCH_VALUE_LOW (0xD1)
+
+#define FT5402_REG_DIRECTION (0x53+0x80)
+#define FT5402_REG_LEMDA_X (0x41+0x80)
+#define FT5402_REG_LEMDA_Y (0x42+0x80)
+#define FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM (0x43+0x80)
+#define FT5402_REG_FACE_DETECT_PRE_VALUE (0x44+0x80)
+#define FT5402_REG_FACE_DETECT_NUM (0x45+0x80)
+#define FT5402_REG_BIGAREA_PEAK_VALUE_MIN (0x33+0x80)
+#define FT5402_REG_BIGAREA_DIFF_VALUE_OVER_NUM (0x34+0x80)
+
+/**************************************************************************/
+#define FT5402_REG_FT5402_POINTS_STABLE_MACRO (0x57+0x80)
+#define FT5402_REG_FT5402_MIN_DELTA_X (0x4a+0x80)
+#define FT5402_REG_FT5402_MIN_DELTA_Y (0x4b+0x80)
+#define FT5402_REG_FT5402_MIN_DELTA_STEP (0x4c+0x80)
+
+#define FT5402_REG_FT5402_ESD_NOISE_MACRO (0x58+0x80)
+#define FT5402_REG_FT5402_ESD_DIFF_VAL (0x4d+0x80)
+#define FT5402_REG_FT5402_ESD_NEGTIVE (0xCe)
+#define FT5402_REG_FT5402_ESD_FILTER_FRAMES (0x4f+0x80)
+
+#define FT5402_REG_FT5402_IO_LEVEL_SELECT (0x52+0x80)
+
+#define FT5402_REG_FT5402_POINTID_DELAY_COUNT (0x54+0x80)
+
+#define FT5402_REG_FT5402_LIFTUP_FILTER_MACRO (0x55+0x80)
+
+#define FT5402_REG_FT5402_DIFF_HANDLE_MACRO (0x5A+0x80)
+#define FT5402_REG_FT5402_MIN_WATER (0x5B+0x80)
+#define FT5402_REG_FT5402_MAX_NOISE (0x5C+0x80)
+#define FT5402_REG_FT5402_WATER_START_RX (0x5D+0x80)
+#define FT5402_REG_FT5402_WATER_START_TX (0xDE)
+
+#define FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO (0x38+0x80)
+#define FT5402_REG_FT5402_RAISE_THGROUP (0x36+0x80)
+#define FT5402_REG_FT5402_CHARGER_STATE (0x35+0x80)
+
+#define FT5402_REG_FT5402_FILTERID_START (0x37+0x80)
+
+#define FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO (0x5F+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH (0x60+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH (0x61+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME (0x62+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_BAND_EN (0x63+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH (0x64+0x80)
+/**************************************************************************/
+
+#define FT5402_REG_TEST_MODE 0x04
+#define FT5402_REG_TEST_MODE_2 0x05
+#define FT5402_TX_TEST_MODE_1 0x28
+#define FT5402_RX_TEST_MODE_1 0x1E
+#define FT5402_FACTORYMODE_VALUE 0x40
+#define FT5402_WORKMODE_VALUE 0x00
+
+/************************************************************************/
+/* string */
+/************************************************************************/
+#define STRING_FT5402_KX "FT5X02_KX"
+#define STRING_FT5402_KY "FT5X02_KY"
+#define STRING_FT5402_LEMDA_X "FT5X02_LEMDA_X"
+#define STRING_FT5402_LEMDA_Y "FT5X02_LEMDA_Y"
+#define STRING_FT5402_RESOLUTION_X "FT5X02_RESOLUTION_X"
+#define STRING_FT5402_RESOLUTION_Y "FT5X02_RESOLUTION_Y"
+#define STRING_FT5402_DIRECTION "FT5X02_DIRECTION"
+
+
+
+#define STRING_FT5402_FACE_DETECT_PRE_VALUE "FT5X02_FACE_DETECT_PRE_VALUE"
+#define STRING_FT5402_FACE_DETECT_NUM "FT5X02_FACE_DETECT_NUM"
+#define STRING_FT5402_BIGAREA_PEAK_VALUE_MIN "FT5X02_BIGAREA_PEAK_VALUE_MIN"
+#define STRING_FT5402_BIGAREA_DIFF_VALUE_OVER_NUM "FT5X02_BIGAREA_DIFF_VALUE_OVER_NUM"
+#define STRING_FT5402_CUSTOMER_ID "FT5X02_CUSTOMER_ID"
+#define STRING_FT5402_PERIOD_ACTIVE "FT5X02_RV_G_PERIOD_ACTIVE"
+#define STRING_FT5402_FACE_DETECT_STATISTICS_TX_NUM "FT5X02_FACE_DETECT_STATISTICS_TX_NUM"
+
+#define STRING_FT5402_THGROUP "FT5X02_THGROUP"
+#define STRING_FT5402_THPEAK "FT5X02_THPEAK"
+#define STRING_FT5402_FACE_DETECT_MODE "FT5X02_FACE_DETECT_MODE"
+#define STRING_FT5402_MAX_TOUCH_VALUE "FT5X02_MAX_TOUCH_VALUE"
+
+#define STRING_FT5402_PWMODE_CTRL "FT5X02_PWMODE_CTRL"
+#define STRING_FT5402_DRAW_LINE_TH "FT5X02_DRAW_LINE_TH"
+
+#define STRING_FT5402_POINTS_SUPPORTED "FT5X02_POINTS_SUPPORTED"
+
+#define STRING_FT5402_START_RX "FT5X02_START_RX"
+#define STRING_FT5402_ADC_TARGET "FT5X02_ADC_TARGET"
+#define STRING_FT5402_ESD_FILTER_FRAME "FT5X02_ESD_FILTER_FRAME"
+
+#define STRING_FT5402_POINTS_STABLE_MACRO "FT5X02_POINTS_STABLE_MACRO"
+#define STRING_FT5402_MIN_DELTA_X "FT5X02_MIN_DELTA_X"
+#define STRING_FT5402_MIN_DELTA_Y "FT5X02_MIN_DELTA_Y"
+#define STRING_FT5402_MIN_DELTA_STEP "FT5X02_MIN_DELTA_STEP"
+
+#define STRING_FT5402_ESD_NOISE_MACRO "FT5X02_ESD_NOISE_MACRO"
+#define STRING_FT5402_ESD_DIFF_VAL "FT5X02_ESD_DIFF_VAL"
+#define STRING_FT5402_ESD_NEGTIVE "FT5X02_ESD_NEGTIVE"
+#define STRING_FT5402_ESD_FILTER_FRAME "FT5X02_ESD_FILTER_FRAME"
+
+#define STRING_FT5402_IO_LEVEL_SELECT "FT5X02_IO_LEVEL_SELECT"
+#define STRING_FT5402_POINTID_DELAY_COUNT "FT5X02_POINTID_DELAY_COUNT"
+
+#define STRING_FT5402_LIFTUP_FILTER_MACRO "FT5X02_LIFTUP_FILTER_MACRO"
+
+#define STRING_FT5402_DIFFDATA_HANDLE "FT5X02_DIFFDATA_HANDLE" //_MACRO
+#define STRING_FT5402_MIN_WATER_VAL "FT5X02_MIN_WATER_VAL"
+#define STRING_FT5402_MAX_NOISE_VAL "FT5X02_MAX_NOISE_VAL"
+#define STRING_FT5402_WATER_HANDLE_START_RX "FT5X02_WATER_HANDLE_START_RX"
+#define STRING_FT5402_WATER_HANDLE_START_TX "FT5X02_WATER_HANDLE_START_TX"
+
+#define STRING_FT5402_HOST_NUMBER_SUPPORTED "FT5X02_HOST_NUMBER_SUPPORTED"
+#define STRING_FT5402_RV_G_RAISE_THGROUP "FT5X02_RV_G_RAISE_THGROUP"
+#define STRING_FT5402_RV_G_CHARGER_STATE "FT5X02_RV_G_CHARGER_STATE"
+
+#define STRING_FT5402_RV_G_FILTERID_START "FT5X02_RV_G_FILTERID_START"
+
+#define STRING_FT5402_FRAME_FILTER_EN "FT5X02_FRAME_FILTER_EN"
+#define STRING_FT5402_FRAME_FILTER_SUB_MAX_TH "FT5X02_FRAME_FILTER_SUB_MAX_TH"
+#define STRING_FT5402_FRAME_FILTER_ADD_MAX_TH "FT5X02_FRAME_FILTER_ADD_MAX_TH"
+#define STRING_FT5402_FRAME_FILTER_SKIP_START_FRAME "FT5X02_FRAME_FILTER_SKIP_START_FRAME"
+#define STRING_FT5402_FRAME_FILTER_BAND_EN "FT5X02_FRAME_FILTER_BAND_EN"
+#define STRING_FT5402_FRAME_FILTER_BAND_WIDTH "FT5X02_FRAME_FILTER_BAND_WIDTH"
+
+
+#define STRING_ft5402_tx_num "FT5X02_tx_num"
+#define STRING_ft5402_rx_num "FT5X02_rx_num"
+#define STRING_ft5402_gain "FT5X02_gain"
+#define STRING_ft5402_voltage "FT5X02_voltage"
+#define STRING_ft5402_scanselect "FT5X02_scanselect"
+
+#define STRING_ft5402_tx_order "FT5X02_tx_order"
+#define STRING_ft5402_tx_offset "FT5X02_tx_offset"
+#define STRING_ft5402_tx_cap "FT5X02_tx_cap"
+
+#define STRING_ft5402_rx_order "FT5X02_rx_order"
+#define STRING_ft5402_rx_offset "FT5X02_rx_offset"
+#define STRING_ft5402_rx_cap "FT5X02_rx_cap"
+
+struct Struct_Param_FT5402 {
+ short ft5402_KX;
+ short ft5402_KY;
+ unsigned char ft5402_LEMDA_X;
+ unsigned char ft5402_LEMDA_Y;
+ short ft5402_RESOLUTION_X;
+ short ft5402_RESOLUTION_Y;
+ unsigned char ft5402_DIRECTION;
+ unsigned char ft5402_FACE_DETECT_PRE_VALUE;
+ unsigned char ft5402_FACE_DETECT_NUM;
+
+ unsigned char ft5402_BIGAREA_PEAK_VALUE_MIN;
+ unsigned char ft5402_BIGAREA_DIFF_VALUE_OVER_NUM;
+ unsigned char ft5402_CUSTOMER_ID;
+ unsigned char ft5402_PERIOD_ACTIVE;
+ unsigned char ft5402_FACE_DETECT_STATISTICS_TX_NUM;
+
+ short ft5402_THGROUP;
+ unsigned char ft5402_THPEAK;
+ unsigned char ft5402_FACE_DETECT_MODE;
+ short ft5402_MAX_TOUCH_VALUE;
+
+ unsigned char ft5402_PWMODE_CTRL;
+ unsigned char ft5402_DRAW_LINE_TH;
+ unsigned char ft5402_POINTS_SUPPORTED;
+
+ unsigned char ft5402_START_RX;
+ short ft5402_ADC_TARGET;
+ unsigned char ft5402_ESD_FILTER_FRAME;
+
+ unsigned char ft5402_POINTS_STABLE_MACRO;
+ unsigned char ft5402_MIN_DELTA_X;
+ unsigned char ft5402_MIN_DELTA_Y;
+ unsigned char ft5402_MIN_DELTA_STEP;
+
+ unsigned char ft5402_ESD_NOISE_MACRO;
+ unsigned char ft5402_ESD_DIFF_VAL;
+ char ft5402_ESD_NEGTIVE; //negtive
+ unsigned char ft5402_ESD_FILTER_FRAMES;
+
+ unsigned char ft5402_IO_LEVEL_SELECT;
+
+ unsigned char ft5402_POINTID_DELAY_COUNT;
+
+ unsigned char ft5402_LIFTUP_FILTER_MACRO;
+
+ unsigned char ft5402_DIFF_HANDLE_MACRO;
+ char ft5402_MIN_WATER; //negtive
+ unsigned char ft5402_MAX_NOISE;
+ unsigned char ft5402_WATER_START_RX;
+ unsigned char ft5402_WATER_START_TX;
+
+ unsigned char ft5402_HOST_NUMBER_SUPPORTED_MACRO;
+ unsigned char ft5402_RAISE_THGROUP;
+ unsigned char ft5402_CHARGER_STATE;
+
+ unsigned char ft5402_FILTERID_START;
+
+ unsigned char ft5402_FRAME_FILTER_EN_MACRO;
+ unsigned char ft5402_FRAME_FILTER_SUB_MAX_TH;
+ unsigned char ft5402_FRAME_FILTER_ADD_MAX_TH;
+ unsigned char ft5402_FRAME_FILTER_SKIP_START_FRAME;
+ unsigned char ft5402_FRAME_FILTER_BAND_EN;
+ unsigned char ft5402_FRAME_FILTER_BAND_WIDTH;
+
+};
+
+struct Struct_Param_FT5402 g_param_ft5402 = {
+ FT5402_KX,
+ FT5402_KY,
+ FT5402_LEMDA_X,
+ FT5402_LEMDA_Y,
+ FT5402_RESOLUTION_X,
+ FT5402_RESOLUTION_Y,
+ FT5402_DIRECTION,
+
+ FT5402_FACE_DETECT_PRE_VALUE,
+ FT5402_FACE_DETECT_NUM,
+ FT5402_BIGAREA_PEAK_VALUE_MIN,
+ FT5402_BIGAREA_DIFF_VALUE_OVER_NUM,
+ FT5402_CUSTOMER_ID,
+ FT5402_RV_G_PERIOD_ACTIVE,
+ FT5402_FACE_DETECT_STATISTICS_TX_NUM,
+
+ FT5402_THGROUP,
+ FT5402_THPEAK,
+ FT5402_FACE_DETECT_MODE,
+ FT5402_MAX_TOUCH_VALUE,
+
+ FT5402_PWMODE_CTRL,
+ FT5402_DRAW_LINE_TH,
+ FT5402_POINTS_SUPPORTED,
+
+ FT5402_START_RX,
+ FT5402_ADC_TARGET,
+ FT5402_ESD_FILTER_FRAME,
+
+ FT5402_POINTS_STABLE_MACRO,
+ FT5402_MIN_DELTA_X,
+ FT5402_MIN_DELTA_Y,
+ FT5402_MIN_DELTA_STEP,
+
+ FT5402_ESD_NOISE_MACRO,
+ FT5402_ESD_DIFF_VAL,
+ FT5402_ESD_NEGTIVE,
+ FT5402_ESD_FILTER_FRAME,
+
+ FT5402_IO_LEVEL_SELECT,
+
+ FT5402_POINTID_DELAY_COUNT,
+
+ FT5402_LIFTUP_FILTER_MACRO,
+
+ FT5402_DIFFDATA_HANDLE,
+ FT5402_MIN_WATER_VAL,
+ FT5402_MAX_NOISE_VAL,
+ FT5402_WATER_HANDLE_START_RX,
+ FT5402_WATER_HANDLE_START_TX,
+
+ FT5402_HOST_NUMBER_SUPPORTED,
+ FT5402_RV_G_RAISE_THGROUP,
+ FT5402_RV_G_CHARGER_STATE,
+
+ FT5402_RV_G_FILTERID_START,
+
+ FT5402_FRAME_FILTER_EN,
+ FT5402_FRAME_FILTER_SUB_MAX_TH,
+ FT5402_FRAME_FILTER_ADD_MAX_TH,
+ FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ FT5402_FRAME_FILTER_BAND_EN,
+ FT5402_FRAME_FILTER_BAND_WIDTH,
+};
+
+char String_Param_FT5402[][64] = {
+ STRING_FT5402_KX,
+ STRING_FT5402_KY,
+ STRING_FT5402_LEMDA_X,
+ STRING_FT5402_LEMDA_Y,
+ STRING_FT5402_RESOLUTION_X,
+ STRING_FT5402_RESOLUTION_Y,
+ STRING_FT5402_DIRECTION,
+ STRING_FT5402_FACE_DETECT_PRE_VALUE,
+ STRING_FT5402_FACE_DETECT_NUM,
+ STRING_FT5402_BIGAREA_PEAK_VALUE_MIN,
+ STRING_FT5402_BIGAREA_DIFF_VALUE_OVER_NUM,
+ STRING_FT5402_CUSTOMER_ID,
+ STRING_FT5402_PERIOD_ACTIVE,
+ STRING_FT5402_FACE_DETECT_STATISTICS_TX_NUM,
+
+ STRING_FT5402_THGROUP,
+ STRING_FT5402_THPEAK,
+ STRING_FT5402_FACE_DETECT_MODE,
+ STRING_FT5402_MAX_TOUCH_VALUE,
+
+ STRING_FT5402_PWMODE_CTRL,
+ STRING_FT5402_DRAW_LINE_TH,
+ STRING_FT5402_POINTS_SUPPORTED,
+
+ STRING_FT5402_START_RX,
+ STRING_FT5402_ADC_TARGET,
+ STRING_FT5402_ESD_FILTER_FRAME,
+
+
+ STRING_ft5402_tx_num,
+ STRING_ft5402_rx_num,
+ STRING_ft5402_gain,
+ STRING_ft5402_voltage ,
+ STRING_ft5402_scanselect,
+
+ STRING_ft5402_tx_order,
+ STRING_ft5402_tx_offset,
+ STRING_ft5402_tx_cap,
+
+ STRING_ft5402_rx_order,
+ STRING_ft5402_rx_offset,
+ STRING_ft5402_rx_cap,
+
+ STRING_FT5402_POINTS_STABLE_MACRO,
+ STRING_FT5402_MIN_DELTA_X,
+ STRING_FT5402_MIN_DELTA_Y,
+ STRING_FT5402_MIN_DELTA_STEP,
+
+ STRING_FT5402_ESD_NOISE_MACRO,
+ STRING_FT5402_ESD_DIFF_VAL,
+ STRING_FT5402_ESD_NEGTIVE,
+ STRING_FT5402_ESD_FILTER_FRAME,
+
+ STRING_FT5402_IO_LEVEL_SELECT,
+
+ STRING_FT5402_POINTID_DELAY_COUNT,
+
+ STRING_FT5402_LIFTUP_FILTER_MACRO,
+
+ STRING_FT5402_DIFFDATA_HANDLE,
+ STRING_FT5402_MIN_WATER_VAL,
+ STRING_FT5402_MAX_NOISE_VAL,
+ STRING_FT5402_WATER_HANDLE_START_RX,
+ STRING_FT5402_WATER_HANDLE_START_TX,
+
+ STRING_FT5402_HOST_NUMBER_SUPPORTED,
+ STRING_FT5402_RV_G_RAISE_THGROUP,
+ STRING_FT5402_RV_G_CHARGER_STATE,
+
+ STRING_FT5402_RV_G_FILTERID_START,
+
+ STRING_FT5402_FRAME_FILTER_EN,
+ STRING_FT5402_FRAME_FILTER_SUB_MAX_TH,
+ STRING_FT5402_FRAME_FILTER_ADD_MAX_TH,
+ STRING_FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ STRING_FT5402_FRAME_FILTER_BAND_EN,
+ STRING_FT5402_FRAME_FILTER_BAND_WIDTH,
+
+};
+
+#define FT5402_APP_NAME "FT5X02_param"
+
+#define FT5402_APP_LEGAL "Legal_File"
+#define FT5402_APP_LEGAL_BYTE_1_STR "BYTE_1"
+#define FT5402_APP_LEGAL_BYTE_2_STR "BYTE_2"
+
+#define FT5402_APP_LEGAL_BYTE_1_VALUE 107
+#define FT5402_APP_LEGAL_BYTE_2_VALUE 201
+
+
+#define FT5402_INI_FILEPATH "/system/etc/firmware/"
+
+#endif
diff --git a/drivers/input/touchscreen/ft5x0x/ft5x0x.c b/drivers/input/touchscreen/ft5x0x/ft5x0x.c
new file mode 100755
index 00000000..9bb63b83
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ft5x0x.c
@@ -0,0 +1,937 @@
+/*
+ * drivers/input/touchscreen/ft5x0x/ft5x0x.c
+ *
+ * FocalTech ft5x0x TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * note: only support mulititouch Wenfs 2010-10-01
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/slab.h>
+#include "ft5x0x.h"
+#include "../../../video/backlight/wmt_bl.h"
+
+struct ft5x0x_data *pContext=NULL;
+static struct i2c_client *l_client=NULL;
+
+#ifdef TOUCH_KEY
+static int keycodes[NUM_KEYS] ={
+ KEY_MENU,
+ KEY_HOME,
+ KEY_BACK,
+ KEY_SEARCH
+};
+#endif
+
+#define FT5402_CONFIG_NAME "fttpconfig_5402public.ini"
+extern int ft5x0x_read_fw_ver(void);
+extern int ft5x0x_auto_clb(void);
+extern int ft5x0x_upg_fw_bin(struct ft5x0x_data *ft5x0x, int check_ver);
+extern int ft5402_Get_Param_From_Ini(char *config_name);
+extern int ft5402_Init_IC_Param(struct i2c_client *client);
+extern int ft5402_get_ic_param(struct i2c_client *client);
+extern int ft5402_read_reg(struct i2c_client * client, u8 regaddr, u8 * regvalue);
+
+extern int register_bl_notifier(struct notifier_block *nb);
+
+extern int unregister_bl_notifier(struct notifier_block *nb);
+
+
+int ft5x0x_i2c_rxdata(char *rxdata, int length)
+{
+ int ret;
+ struct i2c_msg msg[2];
+
+ msg[0].addr = pContext->addr;
+ msg[0].flags = 0 | I2C_M_NOSTART;
+ msg[0].len = 1;
+ msg[0].buf = rxdata;
+
+ msg[1].addr = pContext->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = length;
+ msg[1].buf = rxdata;
+
+ ret = i2c_transfer(pContext->client->adapter, msg, 2);
+ if (ret <= 0)
+ dbg_err("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+
+int ft5x0x_i2c_txdata(char *txdata, int length)
+{
+ int ret;
+ struct i2c_msg msg[1];
+
+ msg[0].addr = pContext->addr;
+ msg[0].flags = 0;
+ msg[0].len = length;
+ msg[0].buf = txdata;
+
+ ret = i2c_transfer(pContext->client->adapter, msg, 1);
+ if (ret <= 0)
+ dbg_err("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+static void ft5x0x_penup(struct ft5x0x_data *ft5x0x)
+{
+ input_mt_sync(ft5x0x->input_dev);
+ input_sync(ft5x0x->input_dev);
+#ifdef TOUCH_KEY
+ if(ft5x0x->tskey_used && ft5x0x->tkey_pressed && ft5x0x->tkey_idx < NUM_KEYS ){
+ input_report_key(ft5x0x->input_dev, keycodes[ft5x0x->tkey_idx], 1);
+ input_sync(ft5x0x->input_dev);
+ input_report_key(ft5x0x->input_dev, keycodes[ft5x0x->tkey_idx], 0);
+ input_sync(ft5x0x->input_dev);
+ dbg("report as key event %d \n",ft5x0x->tkey_idx);
+ }
+#endif
+
+ dbg("pen up\n");
+ return;
+}
+
+#ifdef TOUCH_KEY
+static int ft5x0x_read_tskey(struct ft5x0x_data *ft5x0x,int x,int y)
+{
+ int px,py;
+
+ if(ft5x0x->tkey.axis){
+ px = y;
+ py = x;
+ }else{
+ px = x;
+ py = y;
+ }
+
+ if(px >= ft5x0x->tkey.x_lower && px<=ft5x0x->tkey.x_upper){
+ ft5x0x->tkey_pressed = 1;
+ if(py>= ft5x0x->tkey.ypos[0].y_lower && py<= ft5x0x->tkey.ypos[0].y_upper){
+ ft5x0x->tkey_idx= 0;
+ }else if(py>= ft5x0x->tkey.ypos[1].y_lower && py<= ft5x0x->tkey.ypos[1].y_upper){
+ ft5x0x->tkey_idx = 1;
+ }else if(py>= ft5x0x->tkey.ypos[2].y_lower && py<= ft5x0x->tkey.ypos[2].y_upper){
+ ft5x0x->tkey_idx = 2;
+ }else if(py>= ft5x0x->tkey.ypos[3].y_lower && py<= ft5x0x->tkey.ypos[3].y_upper){
+ ft5x0x->tkey_idx = 3;
+ }else{
+ ft5x0x->tkey_idx = NUM_KEYS;
+ }
+
+ return 1;
+ }
+
+ ft5x0x->tkey_pressed = 0;
+ return 0;
+}
+#endif
+
+static int ft5x0x_read_data(struct ft5x0x_data *ft5x0x)
+{
+ int ret = -1;
+ int i = 0;
+ u16 x,y,px,py;
+ u8 buf[64] = {0}, id;
+ struct ts_event *event = &ft5x0x->event;
+
+ if(ft5x0x->nt == 10)
+ ret = ft5x0x_i2c_rxdata(buf, 64);
+ else if(ft5x0x->nt == 5)
+ ret = ft5x0x_i2c_rxdata(buf, 31);
+
+ if (ret <= 0) {
+ dbg_err("read_data i2c_rxdata failed: %d\n", ret);
+ return ret;
+ }
+
+ memset(event, 0, sizeof(struct ts_event));
+ //event->tpoint = buf[2] & 0x03;// 0000 0011
+ //event->tpoint = buf[2] & 0x07;// 000 0111
+ event->tpoint = buf[2]&0x0F;
+ if (event->tpoint == 0) {
+ ft5x0x_penup(ft5x0x);
+ return 1;
+ }
+
+ if (event->tpoint > ft5x0x->nt){
+ dbg_err("tounch pointnum=%d > max:%d\n", event->tpoint,ft5x0x->nt);
+ return -1;
+ }
+
+ for (i = 0; i < event->tpoint; i++){
+ id = (buf[5+i*6] >>4) & 0x0F;//get track id
+ if(ft5x0x->swap){
+ px = (buf[3+i*6] & 0x0F)<<8 |buf[4+i*6];
+ py = (buf[5+i*6] & 0x0F)<<8 |buf[6+i*6];
+ }else{
+ px = (buf[5+i*6] & 0x0F)<<8 |buf[6+i*6];
+ py = (buf[3+i*6] & 0x0F)<<8 |buf[4+i*6];
+ }
+
+ x = px;
+ y = py;
+
+ if(ft5x0x->xch)
+ x = ft5x0x->reslx - px;
+
+ if(ft5x0x->ych)
+ y = ft5x0x->resly - py;
+
+ if(ft5x0x->dbg) printk("F%d: Tid=%d,px=%d,py=%d; x=%d,y=%d\n", i, id, px, py, x, y);
+
+#ifdef TOUCH_KEY
+ if(ft5x0x->tskey_used && event->tpoint==1) {
+ if(ft5x0x_read_tskey(ft5x0x,px,py) > 0) return -1;
+ }
+#endif
+ if (ft5x0x->lcd_exchg) {
+ int tmp;
+ tmp = x;
+ x = y;
+ y = ft5x0x->reslx - tmp;
+ }
+ event->x[i] = x;
+ event->y[i] = y;
+ event->tid[i] = id;
+
+ }
+
+ return 0;
+}
+
+static void ft5x0x_report(struct ft5x0x_data *ft5x0x)
+{
+ int i = 0;
+ struct ts_event *event = &ft5x0x->event;
+
+ for (i = 0; i < event->tpoint; i++){
+ input_report_abs(ft5x0x->input_dev, ABS_MT_TRACKING_ID, event->tid[i]);
+ input_report_abs(ft5x0x->input_dev, ABS_MT_POSITION_X, event->x[i]);
+ input_report_abs(ft5x0x->input_dev, ABS_MT_POSITION_Y, event->y[i]);
+ input_mt_sync(ft5x0x->input_dev);
+ }
+ input_sync(ft5x0x->input_dev);
+
+ return;
+}
+
+static void ft5x0x_read_work(struct work_struct *work)
+{
+ int ret = -1;
+ struct ft5x0x_data *ft5x0x = container_of(work, struct ft5x0x_data, read_work);
+
+ mutex_lock(&ft5x0x->ts_mutex);
+ ret = ft5x0x_read_data(ft5x0x);
+
+ if (ret == 0) ft5x0x_report(ft5x0x);
+
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+ mutex_unlock(&ft5x0x->ts_mutex);
+
+ return;
+}
+
+static irqreturn_t ft5x0x_interrupt(int irq, void *dev)
+{
+ struct ft5x0x_data *ft5x0x = dev;
+
+ if (gpio_irqstatus(ft5x0x->irqgpio))
+ {
+ wmt_gpio_ack_irq(ft5x0x->irqgpio);
+ if (is_gpio_irqenable(ft5x0x->irqgpio))
+ {
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if(!ft5x0x->earlysus) queue_work(ft5x0x->workqueue, &ft5x0x->read_work);
+#else
+ queue_work(ft5x0x->workqueue, &ft5x0x->read_work);
+#endif
+
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static void ft5x0x_reset(struct ft5x0x_data *ft5x0x)
+{
+ gpio_set_value(ft5x0x->rstgpio, 1);
+ mdelay(5);
+ gpio_set_value(ft5x0x->rstgpio, 0);
+ mdelay(20);
+ gpio_set_value(ft5x0x->rstgpio, 1);
+ mdelay(5);
+
+ return;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ft5x0x_early_suspend(struct early_suspend *handler)
+{
+ struct ft5x0x_data *ft5x0x = container_of(handler, struct ft5x0x_data, early_suspend);
+ ft5x0x->earlysus = 1;
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+ return;
+}
+
+static void ft5x0x_late_resume(struct early_suspend *handler)
+{
+ struct ft5x0x_data *ft5x0x = container_of(handler, struct ft5x0x_data, early_suspend);
+
+ ft5x0x_reset(ft5x0x);
+ ft5x0x->earlysus = 0;
+
+ wmt_gpio_set_irq_type(ft5x0x->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+
+ return;
+}
+#endif //CONFIG_HAS_EARLYSUSPEND
+
+#ifdef CONFIG_PM
+static int ft5x0x_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ft5x0x_data *ft5x0x = dev_get_drvdata(&pdev->dev);
+ ft5x0x->earlysus = 1;
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+ return 0;
+}
+
+static int ft5x0x_resume(struct platform_device *pdev)
+{
+ struct ft5x0x_data *ft5x0x = dev_get_drvdata(&pdev->dev);
+ ft5x0x_reset(ft5x0x);
+ ft5x0x->earlysus = 0;
+
+ if (ft5x0x->load_cfg) {
+ msleep(350);
+ ft5402_Init_IC_Param(ft5x0x->client);
+ //msleep(50);
+ ft5402_get_ic_param(ft5x0x->client);
+ }
+ wmt_gpio_set_irq_type(ft5x0x->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+ return 0;
+}
+
+#else
+#define ft5x0x_suspend NULL
+#define ft5x0x_resume NULL
+#endif
+
+static ssize_t cat_dbg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "dbg \n");
+}
+
+static ssize_t echo_dbg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int dbg = simple_strtoul(buf, NULL, 10);
+ struct ft5x0x_data *ft5x0x = pContext;
+
+ if(dbg){
+ ft5x0x->dbg = 1;
+ }else{
+ ft5x0x->dbg = 0;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(dbg, S_IRUGO | S_IWUSR, cat_dbg, echo_dbg);
+
+static ssize_t cat_clb(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "calibrate \n");
+}
+
+static ssize_t echo_clb(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int cal = simple_strtoul(buf, NULL, 10);
+
+ if(cal){
+ if(ft5x0x_auto_clb()) printk("Calibrate Failed.\n");
+ }else{
+ printk("calibrate --echo 1 >clb.\n");
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(clb, S_IRUGO | S_IWUSR, cat_clb, echo_clb);
+
+static ssize_t cat_fupg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "fupg \n");
+}
+
+static ssize_t echo_fupg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ft5x0x_data *ft5x0x = pContext;
+ unsigned int upg = simple_strtoul(buf, NULL, 10);
+
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+ if(upg){
+ if(ft5x0x_upg_fw_bin(ft5x0x, 0)) printk("Upgrade Failed.\n");
+ }else{
+ printk("upgrade --echo 1 > fupg.\n");
+ }
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+
+ return count;
+}
+static DEVICE_ATTR(fupg, S_IRUGO | S_IWUSR, cat_fupg, echo_fupg);
+
+
+static ssize_t cat_fver(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int fw_ver = ft5x0x_read_fw_ver();
+ return sprintf(buf, "firmware version:0x%02x \n",fw_ver);
+}
+
+static ssize_t echo_fver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ return count;
+}
+static DEVICE_ATTR(fver, S_IRUGO | S_IWUSR, cat_fver, echo_fver);
+
+static ssize_t cat_addr(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret;
+ u8 addrs[32];
+ int cnt=0;
+ struct i2c_msg msg[2];
+ struct ft5x0x_data *ft5x0x = pContext;
+ u8 ver[1]= {0xa6};
+
+ ft5x0x->addr = 1;
+
+ msg[0].addr = ft5x0x->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = ver;
+
+ msg[1].addr = ft5x0x->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = ver;
+
+ while(ft5x0x->addr < 0x80){
+ ret = i2c_transfer(ft5x0x->client->adapter, msg, 2);
+ if(ret == 2) sprintf(&addrs[5*cnt++], " 0x%02x",ft5x0x->addr);
+
+ ft5x0x->addr++;
+ msg[0].addr = msg[1].addr = ft5x0x->addr;
+ }
+
+ return sprintf(buf, "i2c addr:%s\n",addrs);
+}
+
+static ssize_t echo_addr(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int addr;
+ struct ft5x0x_data *ft5x0x = pContext;
+
+ sscanf(buf,"%x", &addr);
+ ft5x0x->addr = addr;
+
+ return count;
+}
+static DEVICE_ATTR(addr, S_IRUGO | S_IWUSR, cat_addr, echo_addr);
+
+static struct attribute *ft5x0x_attributes[] = {
+ &dev_attr_clb.attr,
+ &dev_attr_fupg.attr,
+ &dev_attr_fver.attr,
+ &dev_attr_dbg.attr,
+ &dev_attr_addr.attr,
+ NULL
+};
+
+static const struct attribute_group ft5x0x_group = {
+ .attrs = ft5x0x_attributes,
+};
+
+static int ft5x0x_sysfs_create_group(struct ft5x0x_data *ft5x0x, const struct attribute_group *group)
+{
+ int err;
+
+ ft5x0x->kobj = kobject_create_and_add("wmtts", NULL) ;
+ if(!ft5x0x->kobj){
+ dbg_err("kobj create failed.\n");
+ return -ENOMEM;
+ }
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(ft5x0x->kobj, group);
+ if (err < 0){
+ kobject_del(ft5x0x->kobj);
+ dbg_err("Create sysfs group failed!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void ft5x0x_sysfs_remove_group(struct ft5x0x_data *ft5x0x, const struct attribute_group *group)
+{
+ sysfs_remove_group(ft5x0x->kobj, group);
+ kobject_del(ft5x0x->kobj);
+ return;
+}
+
+static int bl_notify_irqgpio = -1;
+static int wmt_wakeup_bl_notify(struct notifier_block *nb, unsigned long event,
+ void *dummy)
+{
+ switch (event) {
+ case BL_CLOSE:
+ errlog("ft5x0x: BL_CLOSE, disable irq\n");
+ wmt_gpio_mask_irq(bl_notify_irqgpio);
+ break;
+ case BL_OPEN:
+ errlog("ft5x0x: BL_OPEN, enable irq\n");
+ wmt_gpio_unmask_irq(bl_notify_irqgpio);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wmt_bl_notify = {
+ .notifier_call = wmt_wakeup_bl_notify,
+};
+
+static int ft5x0x_probe(struct platform_device *pdev)
+{
+ int i,err = 0;
+ u8 value = 0;
+ u8 cfg_name[32];
+ struct ft5x0x_data *ft5x0x = platform_get_drvdata( pdev);
+
+ ft5x0x->client = l_client;
+ INIT_WORK(&ft5x0x->read_work, ft5x0x_read_work);
+ mutex_init(&ft5x0x->ts_mutex);
+
+ ft5x0x->workqueue = create_singlethread_workqueue(ft5x0x->name);
+ if (!ft5x0x->workqueue) {
+ err = -ESRCH;
+ goto exit_create_singlethread;
+ }
+
+ err = ft5x0x_sysfs_create_group(ft5x0x, &ft5x0x_group);
+ if(err < 0){
+ dbg("create sysfs group failed.\n");
+ goto exit_create_group;
+ }
+
+ ft5x0x->input_dev = input_allocate_device();
+ if (!ft5x0x->input_dev) {
+ err = -ENOMEM;
+ dbg("failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ ft5x0x->input_dev->name = ft5x0x->name;
+ ft5x0x->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(INPUT_PROP_DIRECT, ft5x0x->input_dev->propbit);
+
+ if (ft5x0x->lcd_exchg) {
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_POSITION_X, 0, ft5x0x->resly, 0, 0);
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_POSITION_Y, 0, ft5x0x->reslx, 0, 0);
+ } else {
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_POSITION_X, 0, ft5x0x->reslx, 0, 0);
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_POSITION_Y, 0, ft5x0x->resly, 0, 0);
+ }
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_TRACKING_ID, 0, 20, 0, 0);
+#ifdef TOUCH_KEY
+ if(ft5x0x->tskey_used){
+ for (i = 0; i <NUM_KEYS; i++)
+ set_bit(keycodes[i], ft5x0x->input_dev->keybit);
+
+ ft5x0x->input_dev->keycode = keycodes;
+ ft5x0x->input_dev->keycodesize = sizeof(unsigned int);
+ ft5x0x->input_dev->keycodemax = NUM_KEYS;
+ }
+#endif
+
+ err = input_register_device(ft5x0x->input_dev);
+ if (err) {
+ dbg_err("ft5x0x_ts_probe: failed to register input device.\n");
+ goto exit_input_register_device_failed;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ft5x0x->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ft5x0x->early_suspend.suspend = ft5x0x_early_suspend;
+ ft5x0x->early_suspend.resume = ft5x0x_late_resume;
+ register_early_suspend(&ft5x0x->early_suspend);
+#endif
+
+ if(ft5x0x->upg){
+ if(ft5x0x_upg_fw_bin(ft5x0x, 1)) printk("Upgrade Failed.\n");
+ else wmt_setsyspara("wmt.io.ts.upg","");
+ ft5x0x->upg = 0x00;
+ }
+
+ if(request_irq(ft5x0x->irq, ft5x0x_interrupt, IRQF_SHARED, ft5x0x->name, ft5x0x) < 0){
+ dbg_err("Could not allocate irq for ts_ft5x0x !\n");
+ err = -1;
+ goto exit_register_irq;
+ }
+
+ { // check if need to load config to IC or not
+ err = ft5402_read_reg(ft5x0x->client, 0xa3, &value);
+ if (err < 0)
+ dbg_err("Read reg 0xa3 failed.\n");
+ else
+ printk("0xa3 reg = %d\n", value);
+ if (value == 3)
+ ft5x0x->load_cfg = 1;
+ else
+ ft5x0x->load_cfg = 0;
+ }
+ ft5x0x_reset(ft5x0x);
+
+ if (ft5x0x->load_cfg) {
+ msleep(350); /*make sure CTP already finish startup process*/
+ sprintf(cfg_name, "%s.ini", ft5x0x->cfg_name);
+ printk("Config file name: %s\n", cfg_name);
+ if (ft5402_Get_Param_From_Ini(cfg_name) >= 0)
+ ft5402_Init_IC_Param(ft5x0x->client);
+ else
+ dbg_err("[FTS]-------Get ft5402 param from INI file failed\n");
+ ft5402_get_ic_param(ft5x0x->client);
+ }
+
+ wmt_gpio_set_irq_type(ft5x0x->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+
+ if(bl_notify_irqgpio < 0){
+ register_bl_notifier(&wmt_bl_notify);
+ bl_notify_irqgpio = ft5x0x->irqgpio;
+ }
+
+ return 0;
+
+exit_register_irq:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ft5x0x->early_suspend);
+#endif
+exit_input_register_device_failed:
+ input_free_device(ft5x0x->input_dev);
+exit_input_dev_alloc_failed:
+ ft5x0x_sysfs_remove_group(ft5x0x, &ft5x0x_group);
+exit_create_group:
+ cancel_work_sync(&ft5x0x->read_work);
+ destroy_workqueue(ft5x0x->workqueue);
+exit_create_singlethread:
+ return err;
+}
+
+static int ft5x0x_remove(struct platform_device *pdev)
+{
+ struct ft5x0x_data *ft5x0x = platform_get_drvdata( pdev);
+
+ if( bl_notify_irqgpio > 0){
+ unregister_bl_notifier(&wmt_bl_notify);
+ bl_notify_irqgpio = -1;
+ }
+ cancel_work_sync(&ft5x0x->read_work);
+ flush_workqueue(ft5x0x->workqueue);
+ destroy_workqueue(ft5x0x->workqueue);
+
+ free_irq(ft5x0x->irq, ft5x0x);
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ft5x0x->early_suspend);
+#endif
+ input_unregister_device(ft5x0x->input_dev);
+
+ ft5x0x_sysfs_remove_group(ft5x0x, &ft5x0x_group);
+
+ mutex_destroy(&ft5x0x->ts_mutex);
+ dbg("remove...\n");
+ return 0;
+}
+
+static void ft5x0x_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device ft5x0x_device = {
+ .name = DEV_FT5X0X,
+ .id = 0,
+ .dev = {.release = ft5x0x_release},
+};
+
+static struct platform_driver ft5x0x_driver = {
+ .driver = {
+ .name = DEV_FT5X0X,
+ .owner = THIS_MODULE,
+ },
+ .probe = ft5x0x_probe,
+ .remove = ft5x0x_remove,
+ .suspend = ft5x0x_suspend,
+ .resume = ft5x0x_resume,
+};
+
+static int check_touch_env(struct ft5x0x_data *ft5x0x)
+{
+ int i,ret = 0;
+ int len = 96;
+ int Enable;
+ char retval[96] = {0};
+ char *p=NULL;
+ char *s=NULL;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ //printk("MST FT5x0x:Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ sscanf(retval,"%d:",&Enable);
+ //check touch enable
+ if(Enable == 0){
+ //printk("FT5x0x Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(retval,':');
+ p++;
+ if(strncmp(p,"ft5301",6)==0){//check touch ID
+ ft5x0x->id = FT5301;
+ ft5x0x->name = DEV_FT5301;
+ }else if(strncmp(p,"ft5406",6)==0){
+ ft5x0x->id = FT5406;
+ ft5x0x->name = DEV_FT5406;
+ }else if(strncmp(p,"ft5206",6)==0){
+ ft5x0x->id = FT5206;
+ ft5x0x->name = DEV_FT5206;
+ }else if(strncmp(p,"ft5606",6)==0){
+ ft5x0x->id = FT5606;
+ ft5x0x->name = DEV_FT5606;
+ }else if(strncmp(p,"ft5306",6)==0){
+ ft5x0x->id = FT5306;
+ ft5x0x->name = DEV_FT5306;
+ }else if(strncmp(p,"ft5302",6)==0){
+ ft5x0x->id = FT5302;
+ ft5x0x->name = DEV_FT5302;
+ }else if(strncmp(p,"ft5",3)==0)
+ {
+ ft5x0x->id = FT5X0X;
+ ft5x0x->name = DEV_FT5X0X;
+ }else{
+ printk("FT5x0x touch disabled.\n");
+ return -ENODEV;
+ }
+
+ s = strchr(p,':');
+ strncpy(ft5x0x->cfg_name, p, s-p);
+
+ p = s + 1;
+ sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%x", &ft5x0x->irqgpio, &ft5x0x->reslx, &ft5x0x->resly, &ft5x0x->rstgpio, &ft5x0x->swap, &ft5x0x->xch, &ft5x0x->ych, &ft5x0x->nt, &ft5x0x->addr);
+
+ ft5x0x->irq = IRQ_GPIO;
+ printk("%s irqgpio=%d, reslx=%d, resly=%d, rstgpio=%d, swap=%d, xch=%d, ych=%d, nt=%d, addr=%x\n", ft5x0x->name, ft5x0x->irqgpio, ft5x0x->reslx, ft5x0x->resly, ft5x0x->rstgpio, ft5x0x->swap, ft5x0x->xch, ft5x0x->ych, ft5x0x->nt, ft5x0x->addr);
+
+ memset(retval,0x00,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.ts.upg", retval, &len);
+ if(!ret){
+ ft5x0x->upg = 1;
+ strncpy(ft5x0x->fw_name, retval, sizeof(ft5x0x->fw_name));
+ }
+
+#ifdef TOUCH_KEY
+ memset(retval,0x00,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.tskey", retval, &len);
+ if(!ret){
+ sscanf(retval,"%d:", &ft5x0x->nkeys);
+ p = strchr(retval,':');
+ p++;
+ for(i=0; i < ft5x0x->nkeys; i++ ){
+ sscanf(p,"%d:%d", &ft5x0x->tkey.ypos[i].y_lower, &ft5x0x->tkey.ypos[i].y_upper);
+ p = strchr(p,':');
+ p++;
+ p = strchr(p,':');
+ p++;
+ }
+ sscanf(p,"%d:%d:%d", &ft5x0x->tkey.axis, &ft5x0x->tkey.x_lower, &ft5x0x->tkey.x_upper);
+ ft5x0x->tskey_used = 1;
+ }
+#endif
+
+ memset(retval,0x00,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ ft5x0x->lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = TS_I2C_NAME,
+ .flags = 0x00,
+ .addr = FT5406_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+
+ //ts_i2c_board_info.addr = FT5406_I2C_ADDR;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(FT5X0X_I2C_BUS);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+static int __init ft5x0x_init(void)
+{
+ int ret = -ENOMEM;
+ struct ft5x0x_data *ft5x0x=NULL;
+
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ ft5x0x = kzalloc(sizeof(struct ft5x0x_data), GFP_KERNEL);
+ if(!ft5x0x){
+ dbg_err("mem alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ pContext = ft5x0x;
+ ret = check_touch_env(ft5x0x);
+ if(ret < 0)
+ goto exit_free_mem;
+
+ ret = gpio_request(ft5x0x->irqgpio, "ts_irq");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen irq request fail\n", ft5x0x->irqgpio);
+ goto exit_free_mem;
+ }
+ wmt_gpio_setpull(ft5x0x->irqgpio, WMT_GPIO_PULL_UP);
+ gpio_direction_input(ft5x0x->irqgpio);
+
+ ret = gpio_request(ft5x0x->rstgpio, "ts_rst");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen reset request fail\n", ft5x0x->rstgpio);
+ goto exit_free_irqgpio;
+ }
+ gpio_direction_output(ft5x0x->rstgpio, 1);
+
+
+ ret = platform_device_register(&ft5x0x_device);
+ if(ret){
+ dbg_err("register platform drivver failed!\n");
+ goto exit_free_gpio;
+ }
+ platform_set_drvdata(&ft5x0x_device, ft5x0x);
+
+ ret = platform_driver_register(&ft5x0x_driver);
+ if(ret){
+ dbg_err("register platform device failed!\n");
+ goto exit_unregister_pdev;
+ }
+
+ return ret;
+
+exit_unregister_pdev:
+ platform_device_unregister(&ft5x0x_device);
+exit_free_gpio:
+ gpio_free(ft5x0x->rstgpio);
+exit_free_irqgpio:
+ gpio_free(ft5x0x->irqgpio);
+exit_free_mem:
+ kfree(ft5x0x);
+ pContext = NULL;
+ return ret;
+}
+
+static void ft5x0x_exit(void)
+{
+ if(!pContext) return;
+
+ gpio_free(pContext->rstgpio);
+ gpio_free(pContext->irqgpio);
+ platform_driver_unregister(&ft5x0x_driver);
+ platform_device_unregister(&ft5x0x_device);
+ kfree(pContext);
+ ts_i2c_unregister_device();
+ return;
+}
+
+late_initcall(ft5x0x_init);
+module_exit(ft5x0x_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("FocalTech.Touch");
diff --git a/drivers/input/touchscreen/ft5x0x/ft5x0x.h b/drivers/input/touchscreen/ft5x0x/ft5x0x.h
new file mode 100755
index 00000000..03836b49
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ft5x0x.h
@@ -0,0 +1,207 @@
+#ifndef __LINUX_FT5X0X_TS_H__
+#define __LINUX_FT5X0X_TS_H__
+
+#define DEV_FT5206 "touch_ft5206"
+#define DEV_FT5301 "touch_ft5301"
+#define DEV_FT5302 "touch_ft5302"
+#define DEV_FT5306 "touch_ft5306"
+#define DEV_FT5406 "touch_ft5406"
+#define DEV_FT5606 "touch_ft5606"
+
+#define DEV_FT5X0X "touch_ft5x0x"
+#define TS_I2C_NAME "ft5x0x-ts"
+#define FT5406_I2C_ADDR 0x38
+#define FT5X0X_I2C_BUS 0x01
+
+enum FT5X0X_ID{
+ FT5206 =1,
+ FT5301,
+ FT5302,
+ FT5306,
+ FT5406,
+ FT5606,
+ FT5X0X,
+};
+
+struct vt1603_ts_cal_info {
+ int a1;
+ int b1;
+ int c1;
+ int a2;
+ int b2;
+ int c2;
+ int delta;
+};
+
+#define SUPPORT_POINT_NUM 10
+struct ts_event {
+ int x[SUPPORT_POINT_NUM];
+ int y[SUPPORT_POINT_NUM];
+ int tid[SUPPORT_POINT_NUM];
+ int tpoint;
+};
+
+#define TOUCH_KEY
+
+#ifdef TOUCH_KEY
+#define NUM_KEYS 4
+struct key_pos{
+ int y_lower;
+ int y_upper;
+};
+
+struct ts_key{
+ int axis;
+ int x_lower;
+ int x_upper;
+ struct key_pos ypos[NUM_KEYS];
+};
+#endif
+
+struct ft5x0x_data {
+ int id;
+ unsigned int addr;
+ const char *name;
+ u8 fw_name[64];
+ u8 cfg_name[32];
+
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct ts_event event;
+ struct work_struct read_work;
+ struct workqueue_struct *workqueue;
+ struct mutex ts_mutex;
+ struct kobject *kobj;
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ int earlysus;
+
+ int reslx;
+ int resly;
+
+ int tw;
+ int th;
+
+ int irq;
+ int irqgpio;
+ int rstgpio;
+/*
+ int igp_idx;
+ int igp_bit;
+
+ int rgp_idx;
+ int rgp_bit;
+*/
+
+ int nt;
+ int nb;
+ int xch;
+ int ych;
+ int swap;
+ int lcd_exchg;
+
+ int upg;
+ int load_cfg;
+ int dbg;
+#ifdef TOUCH_KEY
+ int tskey_used;
+ int tkey_pressed;
+ int nkeys;
+ int tkey_idx;
+ struct ts_key tkey;
+#endif
+
+};
+
+enum ft5x0x_ts_regs {
+ FT5X0X_REG_THGROUP = 0x80, /* touch threshold, related to sensitivity */
+ FT5X0X_REG_THPEAK = 0x81,
+ FT5X0X_REG_THCAL = 0x82,
+ FT5X0X_REG_THWATER = 0x83,
+ FT5X0X_REG_THTEMP = 0x84,
+ FT5X0X_REG_THDIFF = 0x85,
+ FT5X0X_REG_CTRL = 0x86,
+ FT5X0X_REG_TIMEENTERMONITOR = 0x87,
+ FT5X0X_REG_PERIODACTIVE = 0x88, /* report rate */
+ FT5X0X_REG_PERIODMONITOR = 0x89,
+ FT5X0X_REG_HEIGHT_B = 0x8a,
+ FT5X0X_REG_MAX_FRAME = 0x8b,
+ FT5X0X_REG_DIST_MOVE = 0x8c,
+ FT5X0X_REG_DIST_POINT = 0x8d,
+ FT5X0X_REG_FEG_FRAME = 0x8e,
+ FT5X0X_REG_SINGLE_CLICK_OFFSET = 0x8f,
+ FT5X0X_REG_DOUBLE_CLICK_TIME_MIN = 0x90,
+ FT5X0X_REG_SINGLE_CLICK_TIME = 0x91,
+ FT5X0X_REG_LEFT_RIGHT_OFFSET = 0x92,
+ FT5X0X_REG_UP_DOWN_OFFSET = 0x93,
+ FT5X0X_REG_DISTANCE_LEFT_RIGHT = 0x94,
+ FT5X0X_REG_DISTANCE_UP_DOWN = 0x95,
+ FT5X0X_REG_ZOOM_DIS_SQR = 0x96,
+ FT5X0X_REG_RADIAN_VALUE =0x97,
+ FT5X0X_REG_MAX_X_HIGH = 0x98,
+ FT5X0X_REG_MAX_X_LOW = 0x99,
+ FT5X0X_REG_MAX_Y_HIGH = 0x9a,
+ FT5X0X_REG_MAX_Y_LOW = 0x9b,
+ FT5X0X_REG_K_X_HIGH = 0x9c,
+ FT5X0X_REG_K_X_LOW = 0x9d,
+ FT5X0X_REG_K_Y_HIGH = 0x9e,
+ FT5X0X_REG_K_Y_LOW = 0x9f,
+ FT5X0X_REG_AUTO_CLB_MODE = 0xa0,
+ FT5X0X_REG_LIB_VERSION_H = 0xa1,
+ FT5X0X_REG_LIB_VERSION_L = 0xa2,
+ FT5X0X_REG_CIPHER = 0xa3,
+ FT5X0X_REG_MODE = 0xa4,
+ FT5X0X_REG_PMODE = 0xa5, /* Power Consume Mode */
+ FT5X0X_REG_FIRMID = 0xa6, /* Firmware version */
+ FT5X0X_REG_STATE = 0xa7,
+ FT5X0X_REG_FT5201ID = 0xa8,
+ FT5X0X_REG_ERR = 0xa9,
+ FT5X0X_REG_CLB = 0xaa,
+};
+
+//FT5X0X_REG_PMODE
+#define PMODE_ACTIVE 0x00
+#define PMODE_MONITOR 0x01
+#define PMODE_STANDBY 0x02
+#define PMODE_HIBERNATE 0x03
+
+#define DEV_NAME "wmtts"
+#define DEV_MAJOR 11
+
+#define TS_IOC_MAGIC 't'
+#define TS_IOCTL_CAL_START _IO(TS_IOC_MAGIC, 1)
+#define TS_IOCTL_CAL_DONE _IOW(TS_IOC_MAGIC, 2, int*)
+#define TS_IOCTL_GET_RAWDATA _IOR(TS_IOC_MAGIC, 3, int*)
+#define TS_IOCTL_CAL_QUIT _IOW(TS_IOC_MAGIC, 4, int*)
+#define TS_IOCTL_CAL_CAP _IOW(TS_IOC_MAGIC, 5, int*)
+#define TS_IOC_MAXNR 5
+
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_i2c_xfer_continue_if_4(struct i2c_msg *msg, unsigned int num,int bus_id);
+
+//#define FT_DEBUG
+
+#undef dbg
+#ifdef FT_DEBUG
+ #define dbg(fmt,args...) printk("DBG:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+#else
+ #define dbg(fmt,args...)
+#endif
+
+#undef dbg_err
+#define dbg_err(fmt,args...) printk("ERR:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+
+//#define FTS_DBG
+#ifdef FTS_DBG
+#define DBG(fmt, args...) printk("[FTS]" fmt, ## args)
+#else
+#define DBG(fmt, args...) do{}while(0)
+#endif
+
+#ifndef errlog
+#define errlog(fmt, args...) printk(KERN_ERR "[%s:%d]: " fmt, __FUNCTION__,__LINE__, ## args)
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ft5x0x/ft5x0x_upg.c b/drivers/input/touchscreen/ft5x0x/ft5x0x_upg.c
new file mode 100755
index 00000000..9db72130
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ft5x0x_upg.c
@@ -0,0 +1,506 @@
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mount.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/syscalls.h>
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "ft5x0x.h"
+
+typedef enum
+{
+ ERR_OK,
+ ERR_MODE,
+ ERR_READID,
+ ERR_ERASE,
+ ERR_STATUS,
+ ERR_ECC,
+ ERR_DL_ERASE_FAIL,
+ ERR_DL_PROGRAM_FAIL,
+ ERR_DL_VERIFY_FAIL,
+ ERR_FMID
+}E_UPGRADE_ERR_TYPE;
+
+#define FT5X_CTPM_ID_L 0X79
+#define FT5X_CTPM_ID_H 0X03
+
+#define FT56_CTPM_ID_L 0X79
+#define FT56_CTPM_ID_H 0X06
+
+#define FTS_PACKET_LENGTH 128
+
+extern struct ft5x0x_data *pContext;
+extern int ft5x0x_i2c_rxdata(char *rxdata, int length);
+extern int ft5x0x_i2c_txdata(char *txdata, int length);
+
+static int ft5x0x_write_reg(u8 addr, u8 para)
+{
+ u8 buf[2];
+ int ret = -1;
+
+ buf[0] = addr;
+ buf[1] = para;
+ ret = ft5x0x_i2c_txdata(buf, 2);
+ if (ret <= 0) {
+ printk("write reg failed! %x ret: %d", buf[0], ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ft5x0x_read_reg(u8 addr, u8 *pdata)
+{
+ int ret;
+ u8 buf[2];
+ struct i2c_msg msgs[2];
+
+ //
+ buf[0] = addr; //register address
+
+ msgs[0].addr = pContext->addr;
+ msgs[0].flags = 0 | I2C_M_NOSTART;
+ msgs[0].len = 1;
+ msgs[0].buf = buf;
+
+ msgs[1].addr = pContext->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = pdata;
+
+ //ret = wmt_i2c_xfer_continue_if_4(msgs, 2, FT5X0X_I2C_BUS);
+ ret = i2c_transfer(pContext->client->adapter, msgs, 2);
+ if (ret <= 0)
+ printk("msg %s i2c read error: %d\n", __func__, ret);
+
+ return ret;
+
+}
+
+
+/*
+[function]:
+ send a command to ctpm.
+[parameters]:
+ btcmd[in] :command code;
+ btPara1[in] :parameter 1;
+ btPara2[in] :parameter 2;
+ btPara3[in] :parameter 3;
+ num[in] :the valid input parameter numbers, if only command code needed and no parameters followed,then the num is 1;
+[return]:
+ FTS_TRUE :success;
+ FTS_FALSE :io fail;
+*/
+static u8 cmd_write(u8 *cmd,u8 num)
+{
+ return ft5x0x_i2c_txdata(cmd, num);
+}
+
+/*
+[function]:
+ write data to ctpm , the destination address is 0.
+[parameters]:
+ pbt_buf[in] :point to data buffer;
+ bt_len[in] :the data numbers;
+[return]:
+ FTS_TRUE :success;
+ FTS_FALSE :io fail;
+*/
+static u8 byte_write(u8* pbt_buf, int dw_len)
+{
+
+ return ft5x0x_i2c_txdata( pbt_buf, dw_len);
+}
+
+/*
+[function]:
+ read out data from ctpm,the destination address is 0.
+[parameters]:
+ pbt_buf[out] :point to data buffer;
+ bt_len[in] :the data numbers;
+[return]:
+ FTS_TRUE :success;
+ FTS_FALSE :io fail;
+*/
+static u8 byte_read(u8* pbt_buf, u8 bt_len)
+{
+ int ret;
+ struct i2c_msg msg[1];
+
+ msg[0].addr = pContext->addr;
+ msg[0].flags = I2C_M_RD;
+ msg[0].len = bt_len;
+ msg[0].buf = pbt_buf;
+
+ ret = i2c_transfer(pContext->client->adapter, msg, 1);
+ //ret = wmt_i2c_xfer_continue_if_4(msg, 1, FT5X0X_I2C_BUS);
+ if (ret <= 0)
+ printk("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+
+/*
+[function]:
+ burn the FW to ctpm.
+[parameters]:(ref. SPEC)
+ pbt_buf[in] :point to Head+FW ;
+ dw_lenth[in]:the length of the FW + 6(the Head length);
+ bt_ecc[in] :the ECC of the FW
+[return]:
+ ERR_OK :no error;
+ ERR_MODE :fail to switch to UPDATE mode;
+ ERR_READID :read id fail;
+ ERR_ERASE :erase chip fail;
+ ERR_STATUS :status error;
+ ERR_ECC :ecc error.
+*/
+static E_UPGRADE_ERR_TYPE ft5x0x_fw_upgrade(struct ft5x0x_data *ft5x0x, u8* pbt_buf, int dw_lenth)
+{
+ int i = 0,j = 0,i_ret;
+ int packet_number;
+ int temp,lenght;
+ u8 packet_buf[FTS_PACKET_LENGTH + 6];
+ u8 auc_i2c_write_buf[10];
+ u8 reg_val[2] = {0};
+ u8 ctpm_id[2] = {0};
+ u8 cmd[4];
+ u8 bt_ecc;
+
+ /*********Step 1:Reset CTPM *****/
+ /*write 0xaa to register 0xfc*/
+ ft5x0x_write_reg(0xfc,0xaa);
+ msleep(50);
+ /*write 0x55 to register 0xfc*/
+ ft5x0x_write_reg(0xfc,0x55);
+ printk("[FTS] Step 1: Reset CTPM.\n");
+ msleep(30);
+
+ /*********Step 2:Enter upgrade mode *****/
+ auc_i2c_write_buf[0] = 0x55;
+ auc_i2c_write_buf[1] = 0xaa;
+ do{
+ i ++;
+ i_ret = byte_write(auc_i2c_write_buf, 2);
+ mdelay(5);
+ }while(i_ret <= 0 && i < 5 );
+ msleep(20);
+
+ /*********Step 3:check READ-ID**********/
+ if(ft5x0x->id == FT5606){
+ ctpm_id[0] = FT56_CTPM_ID_L;
+ ctpm_id[1] = FT56_CTPM_ID_H;
+ }else{
+ ctpm_id[0] = FT5X_CTPM_ID_L;
+ ctpm_id[1] = FT5X_CTPM_ID_H;
+ }
+
+ cmd[0] = 0x90;
+ cmd[1] = 0x00;
+ cmd[2] = 0x00;
+ cmd[3] = 0x00;
+ cmd_write(cmd,4);
+ byte_read(reg_val,2);
+ if (reg_val[0] == ctpm_id[0] && reg_val[1] == ctpm_id[1]){
+ printk("[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",reg_val[0],reg_val[1]);
+ }else{
+ printk("[FTS] ID_ERROR: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",reg_val[0],reg_val[1]);
+ return ERR_READID;
+ }
+
+ cmd[0] = 0xcd;
+ cmd_write(cmd,1);
+ byte_read(reg_val,1);
+ printk("[FTS] bootloader version = 0x%x\n", reg_val[0]);
+
+ /******Step 4:erase app and panel paramenter area *********/
+ cmd[0] = 0x61;
+ cmd_write(cmd,1); //erase app area
+ msleep(1500);
+ cmd[0] = 0x63;
+ cmd_write(cmd,1); //erase panel parameter area
+ msleep(100);
+ printk("[FTS] Step 4: erase. \n");
+
+ /*********Step 5:write firmware(FW) to ctpm flash*********/
+ bt_ecc = 0;
+ printk("[FTS] Step 5: start upgrade. \n");
+ dw_lenth = dw_lenth - 8;
+ packet_number = (dw_lenth) / FTS_PACKET_LENGTH;
+ packet_buf[0] = 0xbf;
+ packet_buf[1] = 0x00;
+ for (j=0;j<packet_number;j++){
+ temp = j * FTS_PACKET_LENGTH;
+ packet_buf[2] = (u8)(temp>>8);
+ packet_buf[3] = (u8)temp;
+ lenght = FTS_PACKET_LENGTH;
+ packet_buf[4] = (u8)(lenght>>8);
+ packet_buf[5] = (u8)lenght;
+
+ for (i=0;i<FTS_PACKET_LENGTH;i++){
+ packet_buf[6+i] = pbt_buf[j*FTS_PACKET_LENGTH + i];
+ bt_ecc ^= packet_buf[6+i];
+ }
+
+ byte_write(&packet_buf[0],FTS_PACKET_LENGTH + 6);
+ mdelay(FTS_PACKET_LENGTH/6 + 1);
+ if ((j * FTS_PACKET_LENGTH % 1024) == 0){
+ printk("[FTS] upgrade the 0x%x th byte.\n", ((unsigned int)j) * FTS_PACKET_LENGTH);
+ }
+ }
+
+ if ((dw_lenth) % FTS_PACKET_LENGTH > 0){
+ temp = packet_number * FTS_PACKET_LENGTH;
+ packet_buf[2] = (u8)(temp>>8);
+ packet_buf[3] = (u8)temp;
+
+ temp = (dw_lenth) % FTS_PACKET_LENGTH;
+ packet_buf[4] = (u8)(temp>>8);
+ packet_buf[5] = (u8)temp;
+
+ for (i=0;i<temp;i++){
+ packet_buf[6+i] = pbt_buf[ packet_number*FTS_PACKET_LENGTH + i];
+ bt_ecc ^= packet_buf[6+i];
+ }
+
+ byte_write(&packet_buf[0],temp+6);
+ mdelay(20);
+ }
+
+ //send the last six byte
+ for (i = 0; i<6; i++){
+ temp = 0x6ffa + i;
+ packet_buf[2] = (u8)(temp>>8);
+ packet_buf[3] = (u8)temp;
+ temp =1;
+ packet_buf[4] = (u8)(temp>>8);
+ packet_buf[5] = (u8)temp;
+ packet_buf[6] = pbt_buf[ dw_lenth + i];
+ bt_ecc ^= packet_buf[6];
+
+ byte_write(&packet_buf[0],7);
+ mdelay(20);
+ }
+
+ /*********Step 6: read out checksum********************/
+ /*send the opration head*/
+ cmd[0] = 0xcc;
+ cmd_write(cmd,1);
+ byte_read(reg_val,1);
+ printk("[FTS] Step 6:read ECC 0x%x, firmware ECC 0x%x. \n", reg_val[0], bt_ecc);
+ if(reg_val[0] != bt_ecc){
+ return ERR_ECC;
+ }
+
+ /*********Step 7: reset the new FW***********************/
+ cmd[0] = 0x07;
+ cmd_write(cmd,1);
+
+ msleep(300); //make sure CTP startup normally
+
+ return ERR_OK;
+}
+
+int ft5x0x_auto_clb(void)
+{
+ u8 uc_temp;
+ u8 i ;
+
+ printk("[FTS] start auto CLB.\n");
+ msleep(200);
+ ft5x0x_write_reg(0, 0x40);
+ msleep(100); //make sure already enter factory mode
+ ft5x0x_write_reg(2, 0x4); //write command to start calibration
+ msleep(300);
+ for(i=0;i<100;i++){
+ ft5x0x_read_reg(0,&uc_temp);
+ if ( ((uc_temp&0x70)>>4) == 0x0){ //return to normal mode, calibration finish
+ break;
+ }
+ msleep(200);
+ printk("[FTS] waiting calibration %d\n",i);
+ }
+ printk("[FTS] calibration OK.\n");
+
+ msleep(300);
+ ft5x0x_write_reg(0, 0x40); //goto factory mode
+ msleep(100); //make sure already enter factory mode
+ ft5x0x_write_reg(2, 0x5); //store CLB result
+ msleep(300);
+ ft5x0x_write_reg(0, 0x0); //return to normal mode
+ msleep(300);
+ printk("[FTS] store CLB result OK.\n");
+ return 0;
+}
+
+static int ft5x0x_get_bin_ver(const u8 *fw, int fw_szie)
+{
+ if (fw_szie > 2){
+ return fw[fw_szie - 2];
+ }else{
+ return 0xff; //default value
+ }
+ return 0xff;
+}
+
+int ft5x0x_read_fw_ver(void)
+{
+ u8 ver=0;
+ int ret=0;
+
+ ret = ft5x0x_read_reg(FT5X0X_REG_FIRMID, &ver);
+ if(ret > 0)
+ return ver;
+
+ return ret;
+}
+
+
+static int ft5x0x_get_fw_szie(const char *fw_name)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize = 0;
+
+ if(fw_name == NULL){
+ dbg_err("Firmware name error.\n");
+ return -EFAULT;
+ }
+
+ if (NULL == pfile)
+ pfile = filp_open(fw_name, O_RDONLY, 0);
+
+ if (IS_ERR(pfile)) {
+ dbg_err("File open error: %s.\n", fw_name);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ filp_close(pfile, NULL);
+ return fsize;
+}
+
+static int ft5x0x_read_fw(const char *fw_name, u8 *buf)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize;
+ loff_t pos;
+ mm_segment_t fs;
+
+ if(fw_name == NULL){
+ dbg_err("Firmware name error.\n");
+ return -EFAULT;
+ }
+
+ if (NULL == pfile)
+ pfile = filp_open(fw_name, O_RDONLY, 0);
+ if (IS_ERR(pfile)) {
+ dbg_err("File open error: %s.\n", fw_name);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ pos = 0;
+ vfs_read(pfile, buf, fsize, &pos);
+ filp_close(pfile, NULL);
+ set_fs(fs);
+
+ return 0;
+}
+
+#define FW_SUFFFIX ".bin"
+#define SD_UPG_BIN_PATH "/sdcard/_wmt_ft5x0x_fw_app.bin"
+#define FS_UPG_BIN_PATH "/lib/firmware/"
+
+int ft5x0x_upg_fw_bin(struct ft5x0x_data *ft5x0x, int check_ver)
+{
+ int i_ret = 0;
+ int fwsize = 0;
+ int hw_fw_ver;
+ int bin_fw_ver;
+ int do_upg;
+ u8 *pbt_buf = NULL;
+ u8 fw_path[128] = {0};
+
+ if(ft5x0x->upg)
+ sprintf(fw_path,"%s%s%s", FS_UPG_BIN_PATH, ft5x0x->fw_name,FW_SUFFFIX);//get fw binary file from filesystem
+ else
+ strcpy(fw_path,SD_UPG_BIN_PATH); //get fw binary file from SD card
+
+ fwsize = ft5x0x_get_fw_szie(fw_path);
+ if (fwsize <= 0) {
+ dbg_err("Get firmware size failed\n");
+ return -EIO;
+ }
+
+ if (fwsize < 8 || fwsize > 32 * 1024) {
+ dbg_err("FW length error\n");
+ return -EIO;
+ }
+
+ pbt_buf = kmalloc(fwsize + 1, GFP_KERNEL);
+ if (ft5x0x_read_fw(fw_path, pbt_buf)) {
+ dbg_err("Request_firmware failed\n");
+ i_ret = -EIO;
+ goto exit;
+ }
+
+ hw_fw_ver =ft5x0x_read_fw_ver();
+ if(hw_fw_ver <= 0){
+ dbg_err("Read firmware version failed\n");
+ i_ret = hw_fw_ver;
+ goto exit;
+ }
+
+ bin_fw_ver = ft5x0x_get_bin_ver(pbt_buf, fwsize);
+ printk("[FTS] hardware fw ver 0x%0x, binary ver 0x%0x\n",hw_fw_ver, bin_fw_ver);
+
+ if(check_ver){
+ if(hw_fw_ver == 0xa6 || hw_fw_ver < bin_fw_ver)
+ do_upg = 1;
+ else
+ do_upg = 0;
+ }else{
+ do_upg = 1;
+ }
+
+ if(do_upg){
+ if ((pbt_buf[fwsize - 8] ^ pbt_buf[fwsize - 6]) == 0xFF &&
+ (pbt_buf[fwsize - 7] ^ pbt_buf[fwsize - 5]) == 0xFF &&
+ (pbt_buf[fwsize - 3] ^ pbt_buf[fwsize - 4]) == 0xFF) {
+ i_ret = ft5x0x_fw_upgrade(ft5x0x, pbt_buf, fwsize);
+ if (i_ret)
+ dbg_err("Upgrade failed, i_ret=%d\n",i_ret);
+ else {
+ hw_fw_ver = ft5x0x_read_fw_ver();
+ printk("[FTS] upgrade to new version 0x%x\n", hw_fw_ver);
+ }
+ } else {
+ dbg_err("FW format error\n");
+ }
+ }
+
+ ft5x0x_auto_clb();/*start auto CLB*/
+
+exit:
+ kfree(pbt_buf);
+ return i_ret;
+}
+
+
diff --git a/drivers/input/touchscreen/ft5x0x/ini.c b/drivers/input/touchscreen/ft5x0x/ini.c
new file mode 100755
index 00000000..a4f8dc38
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ini.c
@@ -0,0 +1,406 @@
+#include <linux/string.h>
+#include <asm/unistd.h>
+#include <linux/slab.h>
+
+#include "ini.h"
+
+
+char CFG_SSL = '['; /* Ïî±êÖ¾·ûSection Symbol --¿É¸ù¾ÝÌØÊâÐèÒª½øÐж¨Òå¸ü¸Ä£¬Èç { }µÈ*/
+char CFG_SSR = ']'; /* Ïî±êÖ¾·ûSection Symbol --¿É¸ù¾ÝÌØÊâÐèÒª½øÐж¨Òå¸ü¸Ä£¬Èç { }µÈ*/
+char CFG_NIS = ':'; /* name Óë index Ö®¼äµÄ·Ö¸ô·û */
+char CFG_NTS = '#'; /* ×¢ÊÍ·û*/
+
+static char * ini_str_trim_r(char * buf);
+static char * ini_str_trim_l(char * buf);
+static int ini_file_get_line(char *filedata, char *buffer, int maxlen);
+static int ini_split_key_value(char *buf, char **key, char **val);
+static long atol(char *nptr);
+
+
+/*************************************************************
+Function: »ñµÃkeyµÄÖµ
+Input: char * filedata¡¡Îļþ£»char * section¡¡ÏîÖµ£»char * key¡¡¼üÖµ
+Output: char * value¡¡keyµÄÖµ
+Return: 0 SUCCESS
+ -1 δÕÒµ½section
+ -2 δÕÒµ½key
+ -10 Îļþ´ò¿ªÊ§°Ü
+ -12 ¶ÁÈ¡Îļþʧ°Ü
+ -14 Îļþ¸ñʽ´íÎó
+ -22 ³¬³ö»º³åÇø´óС
+Note:
+*************************************************************/
+int ini_get_key(char *filedata, char * section, char * key, char * value)
+{
+ //char buf1[MAX_CFG_BUF + 1], buf2[MAX_CFG_BUF + 1];
+ char *buf1, *buf2;
+ char *key_ptr, *val_ptr;
+ int n, ret;
+ int dataoff = 0;
+
+ *value='\0';
+
+ buf1 = kzalloc(MAX_CFG_BUF + 1, GFP_KERNEL);
+ if(!buf1){
+ printk("buf1: mem alloc failed.\n");
+ return -ENOMEM;
+ }
+ buf2 = kzalloc(MAX_CFG_BUF + 1, GFP_KERNEL);
+ if(!buf2){
+ printk("buf2: mem alloc failed.\n");
+ kfree(buf1);
+ return -ENOMEM;
+ }
+
+ while(1) { /* ËÑÕÒÏîsection */
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf1, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto r_cfg_end;
+ ret = CFG_SECTION_NOT_FOUND;
+ if(n < 0)
+ goto r_cfg_end; /* Îļþβ£¬Î´·¢ÏÖ */
+
+ n = strlen(ini_str_trim_l(ini_str_trim_r(buf1)));
+ if(n == 0 || buf1[0] == CFG_NTS)
+ continue; /* ¿ÕÐÐ »ò ×¢ÊÍÐÐ */
+
+ ret = CFG_ERR_FILE_FORMAT;
+ if(n > 2 && ((buf1[0] == CFG_SSL && buf1[n-1] != CFG_SSR)))
+ goto r_cfg_end;
+ if(buf1[0] == CFG_SSL) {
+ buf1[n-1] = 0x00;
+ if(strcmp(buf1+1, section) == 0)
+ break; /* ÕÒµ½Ïîsection */
+ }
+ }
+
+ while(1){ /* ËÑÕÒkey */
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf1, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto r_cfg_end;
+ ret = CFG_KEY_NOT_FOUND;
+ if(n < 0)
+ goto r_cfg_end;/* Îļþβ£¬Î´·¢ÏÖkey */
+
+ n = strlen(ini_str_trim_l(ini_str_trim_r(buf1)));
+ if(n == 0 || buf1[0] == CFG_NTS)
+ continue; /* ¿ÕÐÐ »ò ×¢ÊÍÐÐ */
+ ret = CFG_KEY_NOT_FOUND;
+ if(buf1[0] == CFG_SSL)
+ goto r_cfg_end;
+ if(buf1[n-1] == '+') { /* Óö+ºÅ±íʾÏÂÒ»ÐмÌÐø */
+ buf1[n-1] = 0x00;
+ while(1) {
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf2, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto r_cfg_end;
+ if(n < 0)
+ break;/* Îļþ½áÊø */
+
+ n = strlen(ini_str_trim_r(buf2));
+ ret = CFG_ERR_EXCEED_BUF_SIZE;
+ if(n > 0 && buf2[n-1] == '+'){/* Óö+ºÅ±íʾÏÂÒ»ÐмÌÐø */
+ buf2[n-1] = 0x00;
+ if( (strlen(buf1) + strlen(buf2)) > MAX_CFG_BUF)
+ goto r_cfg_end;
+ strcat(buf1, buf2);
+ continue;
+ }
+ if(strlen(buf1) + strlen(buf2) > MAX_CFG_BUF)
+ goto r_cfg_end;
+ strcat(buf1, buf2);
+ break;
+ }
+ }
+ ret = CFG_ERR_FILE_FORMAT;
+ if(ini_split_key_value(buf1, &key_ptr, &val_ptr) != 1)
+ goto r_cfg_end;
+ ini_str_trim_l(ini_str_trim_r(key_ptr));
+ if(strcmp(key_ptr, key) != 0)
+ continue; /* ºÍkeyÖµ²»Æ¥Åä */
+ strcpy(value, val_ptr);
+ break;
+ }
+ ret = CFG_OK;
+r_cfg_end:
+ //if(fp != NULL) fclose(fp);
+ kfree(buf1);
+ kfree(buf2);
+ return ret;
+}
+/*************************************************************
+Function: »ñµÃËùÓÐsection
+Input: char *filename¡¡Îļþ,int max ×î´ó¿É·µ»ØµÄsectionµÄ¸öÊý
+Output: char *sections[]¡¡´æ·ÅsectionÃû×Ö
+Return: ·µ»Øsection¸öÊý¡£Èô³ö´í£¬·µ»Ø¸ºÊý¡£
+ -10 Îļþ´ò¿ª³ö´í
+ -12 Îļþ¶ÁÈ¡´íÎó
+ -14 Îļþ¸ñʽ´íÎó
+Note:
+*************************************************************/
+int ini_get_sections(char *filedata, unsigned char * sections[], int max)
+{
+ //FILE *fp;
+ char buf1[MAX_CFG_BUF + 1];
+ int n, n_sections = 0, ret;
+ int dataoff = 0;
+
+// if((fp = fopen(filename, "rb")) == NULL)
+// return CFG_ERR_OPEN_FILE;
+
+ while(1) {/*ËÑÕÒÏîsection */
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf1, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto cfg_scts_end;
+ if(n < 0)
+ break;/* Îļþβ */
+ n = strlen(ini_str_trim_l(ini_str_trim_r(buf1)));
+ if(n == 0 || buf1[0] == CFG_NTS)
+ continue; /* ¿ÕÐÐ »ò ×¢ÊÍÐÐ */
+ ret = CFG_ERR_FILE_FORMAT;
+ if(n > 2 && ((buf1[0] == CFG_SSL && buf1[n-1] != CFG_SSR)))
+ goto cfg_scts_end;
+ if(buf1[0] == CFG_SSL) {
+ if (max!=0){
+ buf1[n-1] = 0x00;
+ strcpy((char *)sections[n_sections], buf1+1);
+ if (n_sections>=max)
+ break; /* ³¬¹ý¿É·µ»Ø×î´ó¸öÊý */
+ }
+ n_sections++;
+ }
+
+ }
+ ret = n_sections;
+cfg_scts_end:
+// if(fp != NULL)
+// fclose(fp);
+ return ret;
+}
+
+
+/*************************************************************
+Function: È¥³ý×Ö·û´®ÓұߵĿÕ×Ö·û
+Input: char * buf ×Ö·û´®Ö¸Õë
+Output:
+Return: ×Ö·û´®Ö¸Õë
+Note:
+*************************************************************/
+static char * ini_str_trim_r(char * buf)
+{
+ int len,i;
+ char tmp[128];
+
+ memset(tmp, 0, sizeof(tmp));
+ len = strlen(buf);
+// tmp = (char *)malloc(len);
+
+ memset(tmp,0x00,len);
+ for(i = 0;i < len;i++) {
+ if (buf[i] !=' ')
+ break;
+ }
+ if (i < len) {
+ strncpy(tmp,(buf+i),(len-i));
+ }
+ strncpy(buf,tmp,len);
+// free(tmp);
+ return buf;
+}
+
+/*************************************************************
+Function: È¥³ý×Ö·û´®×ó±ßµÄ¿Õ×Ö·û
+Input: char * buf ×Ö·û´®Ö¸Õë
+Output:
+Return: ×Ö·û´®Ö¸Õë
+Note:
+*************************************************************/
+static char * ini_str_trim_l(char * buf)
+{
+ int len,i;
+ char tmp[128];
+
+ memset(tmp, 0, sizeof(tmp));
+ len = strlen(buf);
+ //tmp = (char *)malloc(len);
+
+ memset(tmp,0x00,len);
+
+ for(i = 0;i < len;i++) {
+ if (buf[len-i-1] !=' ')
+ break;
+ }
+ if (i < len) {
+ strncpy(tmp,buf,len-i);
+ }
+ strncpy(buf,tmp,len);
+ //free(tmp);
+ return buf;
+}
+/*************************************************************
+Function: ´ÓÎļþÖжÁÈ¡Ò»ÐÐ
+Input: FILE *fp Îļþ¾ä±ú£»int maxlen »º³åÇø×î´ó³¤¶È
+Output: char *buffer Ò»ÐÐ×Ö·û´®
+Return: >0 ʵ¼Ê¶ÁµÄ³¤¶È
+ -1 Îļþ½áÊø
+ -2 ¶ÁÎļþ³ö´í
+Note:
+*************************************************************/
+static int ini_file_get_line(char *filedata, char *buffer, int maxlen)
+{
+ int i, j;
+ char ch1;
+
+ for(i=0, j=0; i<maxlen; j++) {
+ ch1 = filedata[j];
+ if(ch1 == '\n' || ch1 == 0x00)
+ break; /* »»ÐÐ */
+ if(ch1 == '\f' || ch1 == 0x1A) { /* '\f':»»Ò³·ûÒ²ËãÓÐЧ×Ö·û */
+ buffer[i++] = ch1;
+ break;
+ }
+ if(ch1 != '\r') buffer[i++] = ch1; /* ºöÂԻسµ·û */
+ }
+ buffer[i] = '\0';
+ return i+2;
+}
+/*************************************************************
+Function: ·ÖÀëkeyºÍvalue
+ key=val
+ jack = liaoyuewang
+ | | |
+ k1 k2 i
+Input: char *buf
+Output: char **key, char **val
+Return: 1 --- ok
+ 0 --- blank line
+ -1 --- no key, "= val"
+ -2 --- only key, no '='
+Note:
+*************************************************************/
+static int ini_split_key_value(char *buf, char **key, char **val)
+{
+ int i, k1, k2, n;
+
+ if((n = strlen((char *)buf)) < 1)
+ return 0;
+ for(i = 0; i < n; i++)
+ if(buf[i] != ' ' && buf[i] != '\t')
+ break;
+
+ if(i >= n)
+ return 0;
+
+ if(buf[i] == '=')
+ return -1;
+
+ k1 = i;
+ for(i++; i < n; i++)
+ if(buf[i] == '=')
+ break;
+
+ if(i >= n)
+ return -2;
+ k2 = i;
+
+ for(i++; i < n; i++)
+ if(buf[i] != ' ' && buf[i] != '\t')
+ break;
+
+ buf[k2] = '\0';
+
+ *key = buf + k1;
+ *val = buf + i;
+ return 1;
+}
+
+int my_atoi(const char *str)
+{
+ int result = 0;
+ int signal = 1; /* ĬÈÏΪÕýÊý */
+ if((*str>='0'&&*str<='9')||*str=='-'||*str=='+') {
+ if(*str=='-'||*str=='+') {
+ if(*str=='-')
+ signal = -1; /*ÊäÈ븺Êý*/
+ str++;
+ }
+ }
+ else
+ return 0;
+ /*¿ªÊ¼×ª»»*/
+ while(*str>='0' && *str<='9')
+ result = result*10 + (*str++ - '0' );
+
+ return signal*result;
+}
+
+int isspace(int x)
+{
+ if(x==' '||x=='\t'||x=='\n'||x=='\f'||x=='\b'||x=='\r')
+ return 1;
+ else
+ return 0;
+}
+
+int isdigit(int x)
+{
+ if(x<='9' && x>='0')
+ return 1;
+ else
+ return 0;
+
+}
+
+static long atol(char *nptr)
+{
+ int c; /* current char */
+ long total; /* current total */
+ int sign; /* if ''-'', then negative, otherwise positive */
+ /* skip whitespace */
+ while ( isspace((int)(unsigned char)*nptr) )
+ ++nptr;
+ c = (int)(unsigned char)*nptr++;
+ sign = c; /* save sign indication */
+ if (c == '-' || c == '+')
+ c = (int)(unsigned char)*nptr++; /* skip sign */
+ total = 0;
+ while (isdigit(c)) {
+ total = 10 * total + (c - '0'); /* accumulate digit */
+ c = (int)(unsigned char)*nptr++; /* get next char */
+ }
+ if (sign == '-')
+ return -total;
+ else
+ return total; /* return result, negated if necessary */
+}
+/***
+*int atoi(char *nptr) - Convert string to long
+*
+*Purpose:
+* Converts ASCII string pointed to by nptr to binary.
+* Overflow is not detected. Because of this, we can just use
+* atol().
+*
+*Entry:
+* nptr = ptr to string to convert
+*
+*Exit:
+* return int value of the string
+*
+*Exceptions:
+* None - overflow is not detected.
+*
+*******************************************************************************/
+int atoi(char *nptr)
+{
+ return (int)atol(nptr);
+}
+
diff --git a/drivers/input/touchscreen/ft5x0x/ini.h b/drivers/input/touchscreen/ft5x0x/ini.h
new file mode 100755
index 00000000..72434b53
--- /dev/null
+++ b/drivers/input/touchscreen/ft5x0x/ini.h
@@ -0,0 +1,43 @@
+#ifndef INI_H
+#define INI_H
+
+#define MAX_CFG_BUF 512
+#define SUCCESS 0
+/* return value */
+#define CFG_OK SUCCESS
+#define CFG_SECTION_NOT_FOUND -1
+#define CFG_KEY_NOT_FOUND -2
+#define CFG_ERR -10
+
+#define CFG_ERR_OPEN_FILE -10
+#define CFG_ERR_CREATE_FILE -11
+#define CFG_ERR_READ_FILE -12
+#define CFG_ERR_WRITE_FILE -13
+#define CFG_ERR_FILE_FORMAT -14
+
+
+#define CFG_ERR_EXCEED_BUF_SIZE -22
+
+#define COPYF_OK SUCCESS
+#define COPYF_ERR_OPEN_FILE -10
+#define COPYF_ERR_CREATE_FILE -11
+#define COPYF_ERR_READ_FILE -12
+#define COPYF_ERR_WRITE_FILE -13
+
+
+struct ini_key_location {
+ int ini_section_line_no;
+ int ini_key_line_no;
+ int ini_key_lines;
+};
+
+
+int ini_get_key(char *filedata, char * section, char * key, char * value);
+int ini_get_sections(char *filedata, unsigned char * sections[], int max);
+
+int ini_split_section(char *section, char **name, char **index);
+//int ini_join_section(char **section, char *name, char *index);
+
+int atoi(char *nptr);
+
+#endif
diff --git a/drivers/input/touchscreen/ft6x0x/Kconfig b/drivers/input/touchscreen/ft6x0x/Kconfig
new file mode 100755
index 00000000..eb11a558
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/Kconfig
@@ -0,0 +1,11 @@
+config TOUCHSCREEN_FT6X0X
+ tristate "FT5X0X Capacity Touchscreen Device Support"
+ default y
+ depends on ARCH_WMT
+ ---help---
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+ If unsure, say N.
+ To compile this driver as a module, choose M here: the
+ module will be called ft5x0x.
+
diff --git a/drivers/input/touchscreen/ft6x0x/Makefile b/drivers/input/touchscreen/ft6x0x/Makefile
new file mode 100755
index 00000000..859008b8
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/Makefile
@@ -0,0 +1,34 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_ft6x0x
+
+#obj-$(CONFIG_TOUCHSCREEN_FT5X0X) := $(MY_MODULE_NAME).o
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := ft5x0x.o ft5x0x_upg.o ft5402_config.o ft6x06_ex_fun.o ini.o ft6x06_ts.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ @rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ @rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
+
diff --git a/drivers/input/touchscreen/ft6x0x/focaltech_ctl.h b/drivers/input/touchscreen/ft6x0x/focaltech_ctl.h
new file mode 100755
index 00000000..7c58a15a
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/focaltech_ctl.h
@@ -0,0 +1,27 @@
+#ifndef __FOCALTECH_CTL_H__
+#define __FOCALTECH_CTL_H__
+
+#define FT_RW_IIC_DRV "ft_rw_iic_drv"
+#define FT_RW_IIC_DRV_MAJOR 210 /*Ô¤ÉèµÄft_rw_iic_drvµÄÖ÷É豸ºÅ*/
+
+#define FT_I2C_RDWR_MAX_QUEUE 36
+#define FT_I2C_SLAVEADDR 11
+#define FT_I2C_RW 12
+#define FT_RESET_TP 13
+
+typedef struct ft_rw_i2c
+{
+ u8 *buf;
+ u8 flag; /*0-write 1-read*/
+ __u16 length; //the length of data
+}*pft_rw_i2c;
+
+typedef struct ft_rw_i2c_queue
+{
+ struct ft_rw_i2c __user *i2c_queue;
+ int queuenum;
+}*pft_rw_i2c_queue;
+
+int ft_rw_iic_drv_init(struct i2c_client *client);
+void ft_rw_iic_drv_exit(void);
+#endif \ No newline at end of file
diff --git a/drivers/input/touchscreen/ft6x0x/ft5402_config.c b/drivers/input/touchscreen/ft6x0x/ft5402_config.c
new file mode 100755
index 00000000..58683ebd
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft5402_config.c
@@ -0,0 +1,2295 @@
+#include "ft5402_config.h"
+//#include <linux/i2c/ft5402_ts.h>
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/semaphore.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+
+#include <linux/syscalls.h>
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+#include "ini.h"
+#include "ft5402_ini_config.h"
+#include "ft5x0x.h"
+
+
+extern int ft5x0x_i2c_txdata(char *txdata, int length);
+
+int ft5402_write_reg(struct i2c_client * client, u8 regaddr, u8 regvalue)
+{
+ unsigned char buf[2] = {0};
+ buf[0] = regaddr;
+ buf[1] = regvalue;
+
+ return ft5x0x_i2c_txdata(buf, 2);
+}
+
+int ft5402_read_reg(struct i2c_client * client, u8 regaddr, u8 * regvalue)
+{
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &regaddr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = regvalue,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ pr_err("function:%s. i2c read error: %d\n", __func__, ret);
+ return ret;
+}
+
+/*set tx order
+*@txNO: offset from tx order start
+*@txNO1: tx NO.
+*/
+static int ft5402_set_tx_order(struct i2c_client * client, u8 txNO, u8 txNO1)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_write_reg(client, FT5402_REG_TX_ORDER_START + txNO,
+ txNO1);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_ORDER_START + txNO - FT5402_TX_TEST_MODE_1,
+ txNO1);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*set tx order
+*@txNO: offset from tx order start
+*@pTxNo: return value of tx NO.
+*/
+static int ft5402_get_tx_order(struct i2c_client * client, u8 txNO, u8 *pTxNo)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client, FT5402_REG_TX_ORDER_START + txNO,
+ pTxNo);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if(ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_ORDER_START + txNO - FT5402_TX_TEST_MODE_1,
+ pTxNo);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*set tx cap
+*@txNO: tx NO.
+*@cap_value: value of cap
+*/
+static int ft5402_set_tx_cap(struct i2c_client * client, u8 txNO, u8 cap_value)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_write_reg(client, FT5402_REG_TX_CAP_START + txNO,
+ cap_value);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_CAP_START + txNO - FT5402_TX_TEST_MODE_1,
+ cap_value);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*get tx cap*/
+static int ft5402_get_tx_cap(struct i2c_client * client, u8 txNO, u8 *pCap)
+{
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client, FT5402_REG_TX_CAP_START + txNO,
+ pCap);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_CAP_START + txNO - FT5402_TX_TEST_MODE_1,
+ pCap);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+ return ReCode;
+}
+
+/*set tx offset*/
+static int ft5402_set_tx_offset(struct i2c_client * client, u8 txNO, u8 offset_value)
+{
+ unsigned char temp=0;
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1), &temp);
+ if (ReCode >= 0) {
+ if (txNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1),
+ (temp&0xf0) + (offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1),
+ (temp&0x0f) + (offset_value<<4));
+ }
+ } else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_DEVICE_MODE+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ &temp); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ if(txNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_TX_OFFSET_START+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value<<4));
+ }
+ }
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*get tx offset*/
+static int ft5402_get_tx_offset(struct i2c_client * client, u8 txNO, u8 *pOffset)
+{
+ unsigned char temp=0;
+ unsigned char ReCode = 0;
+ if (txNO < FT5402_TX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_OFFSET_START + (txNO>>1), &temp);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_TX_OFFSET_START+((txNO-FT5402_TX_TEST_MODE_1)>>1),
+ &temp);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ if (ReCode >= 0)
+ (txNO%2 == 0) ? (*pOffset = (temp&0x0f)) : (*pOffset = (temp>>4));
+ return ReCode;
+}
+
+/*set rx order*/
+static int ft5402_set_rx_order(struct i2c_client * client, u8 rxNO, u8 rxNO1)
+{
+ unsigned char ReCode = 0;
+ ReCode = ft5402_write_reg(client, FT5402_REG_RX_ORDER_START + rxNO,
+ rxNO1);
+ return ReCode;
+}
+
+/*get rx order*/
+static int ft5402_get_rx_order(struct i2c_client * client, u8 rxNO, u8 *prxNO1)
+{
+ unsigned char ReCode = 0;
+ ReCode = ft5402_read_reg(client, FT5402_REG_RX_ORDER_START + rxNO,
+ prxNO1);
+ return ReCode;
+}
+
+/*set rx cap*/
+static int ft5402_set_rx_cap(struct i2c_client * client, u8 rxNO, u8 cap_value)
+{
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1)
+ ReCode = ft5402_write_reg(client, FT5402_REG_RX_CAP_START + rxNO,
+ cap_value);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if(ReCode >= 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_CAP_START + rxNO - FT5402_RX_TEST_MODE_1,
+ cap_value);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*get rx cap*/
+static int ft5402_get_rx_cap(struct i2c_client * client, u8 rxNO, u8 *pCap)
+{
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client, FT5402_REG_RX_CAP_START + rxNO,
+ pCap);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if(ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_CAP_START + rxNO - FT5402_RX_TEST_MODE_1,
+ pCap);
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*set rx offset*/
+static int ft5402_set_rx_offset(struct i2c_client * client, u8 rxNO, u8 offset_value)
+{
+ unsigned char temp=0;
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1), &temp);
+ if (ReCode >= 0) {
+ if (rxNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1),
+ (temp&0xf0) + (offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1),
+ (temp&0x0f) + (offset_value<<4));
+ }
+ }
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_DEVICE_MODE+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ &temp); /*enter Test mode 2*/
+ if (ReCode >= 0) {
+ if (rxNO%2 == 0)
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value&0x0f));
+ else
+ ReCode = ft5402_write_reg(client,
+ FT5402_REG_RX_OFFSET_START+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ (temp&0xf0)+(offset_value<<4));
+ }
+ }
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ return ReCode;
+}
+
+/*get rx offset*/
+static int ft5402_get_rx_offset(struct i2c_client * client, u8 rxNO, u8 *pOffset)
+{
+ unsigned char temp = 0;
+ unsigned char ReCode = 0;
+ if (rxNO < FT5402_RX_TEST_MODE_1)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_OFFSET_START + (rxNO>>1), &temp);
+ else {
+ ReCode = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE_2<<4); /*enter Test mode 2*/
+ if (ReCode >= 0)
+ ReCode = ft5402_read_reg(client,
+ FT5402_REG_RX_OFFSET_START+((rxNO-FT5402_RX_TEST_MODE_1)>>1),
+ &temp);
+
+ ft5402_write_reg(client, FT5402_REG_DEVICE_MODE,
+ FT5402_REG_TEST_MODE<<4); /*enter Test mode*/
+ }
+
+ if (ReCode >= 0) {
+ if (0 == (rxNO%2))
+ *pOffset = (temp&0x0f);
+ else
+ *pOffset = (temp>>4);
+ }
+
+ return ReCode;
+}
+
+/*set tx num*/
+static int ft5402_set_tx_num(struct i2c_client *client, u8 txnum)
+{
+ return ft5402_write_reg(client, FT5402_REG_TX_NUM, txnum);
+}
+
+/*get tx num*/
+static int ft5402_get_tx_num(struct i2c_client *client, u8 *ptxnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_TX_NUM, ptxnum);
+}
+
+/*set rx num*/
+static int ft5402_set_rx_num(struct i2c_client *client, u8 rxnum)
+{
+ return ft5402_write_reg(client, FT5402_REG_RX_NUM, rxnum);
+}
+
+/*get rx num*/
+static int ft5402_get_rx_num(struct i2c_client *client, u8 *prxnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_RX_NUM, prxnum);
+}
+
+/*set resolution*/
+static int ft5402_set_Resolution(struct i2c_client *client, u16 x, u16 y)
+{
+ unsigned char cRet = 0;
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_X_H, ((unsigned char)(x>>8)));
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_X_L, ((unsigned char)(x&0x00ff)));
+
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_Y_H, ((unsigned char)(y>>8)));
+ cRet &= ft5402_write_reg(client,
+ FT5402_REG_RESOLUTION_Y_L, ((unsigned char)(y&0x00ff)));
+
+ return cRet;
+}
+
+/*get resolution*/
+static int ft5402_get_Resolution(struct i2c_client *client,
+ u16 *px, u16 *py)
+{
+ unsigned char cRet = 0, temp1 = 0, temp2 = 0;
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_X_H, &temp1);
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_X_L, &temp2);
+ (*px) = (((u16)temp1) << 8) | ((u16)temp2);
+
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_Y_H, &temp1);
+ cRet &= ft5402_read_reg(client,
+ FT5402_REG_RESOLUTION_Y_L, &temp2);
+ (*py) = (((u16)temp1) << 8) | ((u16)temp2);
+
+ return cRet;
+}
+
+
+/*set voltage*/
+static int ft5402_set_vol(struct i2c_client *client, u8 Vol)
+{
+ return ft5402_write_reg(client, FT5402_REG_VOLTAGE, Vol);
+}
+
+/*get voltage*/
+static int ft5402_get_vol(struct i2c_client *client, u8 *pVol)
+{
+ return ft5402_read_reg(client, FT5402_REG_VOLTAGE, pVol);
+}
+
+/*set gain*/
+static int ft5402_set_gain(struct i2c_client *client, u8 Gain)
+{
+ return ft5402_write_reg(client, FT5402_REG_GAIN, Gain);
+}
+
+/*get gain*/
+static int ft5402_get_gain(struct i2c_client *client, u8 *pGain)
+{
+ return ft5402_read_reg(client, FT5402_REG_GAIN, pGain);
+}
+
+/*get start rx*/
+static int ft5402_get_start_rx(struct i2c_client *client, u8 *pRx)
+{
+ return ft5402_read_reg(client, FT5402_REG_START_RX, pRx);
+}
+
+
+/*get adc target*/
+static int ft5402_get_adc_target(struct i2c_client *client, u16 *pvalue)
+{
+ int err = 0;
+ u8 tmp1, tmp2;
+ err = ft5402_read_reg(client, FT5402_REG_ADC_TARGET_HIGH,
+ &tmp1);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get adc target high failed\n",
+ __func__);
+ err = ft5402_read_reg(client, FT5402_REG_ADC_TARGET_LOW,
+ &tmp2);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get adc target low failed\n",
+ __func__);
+
+ *pvalue = ((u16)tmp1<<8) + (u16)tmp2;
+ return err;
+}
+
+static int ft5402_set_face_detect_statistics_tx_num(struct i2c_client *client, u8 prevalue)
+{
+ return ft5402_write_reg(client, FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM,
+ prevalue);
+}
+
+static int ft5402_get_face_detect_statistics_tx_num(struct i2c_client *client, u8 *pprevalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM,
+ pprevalue);
+}
+
+static int ft5402_set_face_detect_pre_value(struct i2c_client *client, u8 prevalue)
+{
+ return ft5402_write_reg(client, FT5402_REG_FACE_DETECT_PRE_VALUE,
+ prevalue);
+}
+
+static int ft5402_get_face_detect_pre_value(struct i2c_client *client, u8 *pprevalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_FACE_DETECT_PRE_VALUE,
+ pprevalue);
+}
+
+static int ft5402_set_face_detect_num(struct i2c_client *client, u8 num)
+{
+ return ft5402_write_reg(client, FT5402_REG_FACE_DETECT_NUM,
+ num);
+}
+
+static int ft5402_get_face_detect_num(struct i2c_client *client, u8 *pnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_FACE_DETECT_NUM,
+ pnum);
+}
+
+
+static int ft5402_set_peak_value_min(struct i2c_client *client, u8 min)
+{
+ return ft5402_write_reg(client, FT5402_REG_BIGAREA_PEAK_VALUE_MIN,
+ min);
+}
+
+static int ft5402_get_peak_value_min(struct i2c_client *client, u8 *pmin)
+{
+ return ft5402_read_reg(client, FT5402_REG_BIGAREA_PEAK_VALUE_MIN,
+ pmin);
+}
+
+static int ft5402_set_diff_value_over_num(struct i2c_client *client, u8 num)
+{
+ return ft5402_write_reg(client, FT5402_REG_BIGAREA_DIFF_VALUE_OVER_NUM,
+ num);
+}
+static int ft5402_get_diff_value_over_num(struct i2c_client *client, u8 *pnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_BIGAREA_DIFF_VALUE_OVER_NUM,
+ pnum);
+}
+
+
+static int ft5402_set_customer_id(struct i2c_client *client, u8 num)
+{
+ return ft5402_write_reg(client, FT5402_REG_CUSTOMER_ID,
+ num);
+}
+static int ft5402_get_customer_id(struct i2c_client *client, u8 *pnum)
+{
+ return ft5402_read_reg(client, FT5402_REG_CUSTOMER_ID,
+ pnum);
+}
+
+static int ft5402_set_kx(struct i2c_client *client, u16 value)
+{
+ int err = 0;
+ err = ft5402_write_reg(client, FT5402_REG_KX_H,
+ value >> 8);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set kx high failed\n",
+ __func__);
+ err = ft5402_write_reg(client, FT5402_REG_KX_L,
+ value);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set kx low failed\n",
+ __func__);
+
+ return err;
+}
+
+static int ft5402_get_kx(struct i2c_client *client, u16 *pvalue)
+{
+ int err = 0;
+ u8 tmp1, tmp2;
+ err = ft5402_read_reg(client, FT5402_REG_KX_H,
+ &tmp1);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get kx high failed\n",
+ __func__);
+ err = ft5402_read_reg(client, FT5402_REG_KX_L,
+ &tmp2);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get kx low failed\n",
+ __func__);
+
+ *pvalue = ((u16)tmp1<<8) + (u16)tmp2;
+ return err;
+}
+static int ft5402_set_ky(struct i2c_client *client, u16 value)
+{
+ int err = 0;
+ err = ft5402_write_reg(client, FT5402_REG_KY_H,
+ value >> 8);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set ky high failed\n",
+ __func__);
+ err = ft5402_write_reg(client, FT5402_REG_KY_L,
+ value);
+ if (err < 0)
+ dev_err(&client->dev, "%s:set ky low failed\n",
+ __func__);
+
+ return err;
+}
+
+static int ft5402_get_ky(struct i2c_client *client, u16 *pvalue)
+{
+ int err = 0;
+ u8 tmp1, tmp2;
+ err = ft5402_read_reg(client, FT5402_REG_KY_H,
+ &tmp1);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get ky high failed\n",
+ __func__);
+ err = ft5402_read_reg(client, FT5402_REG_KY_L,
+ &tmp2);
+ if (err < 0)
+ dev_err(&client->dev, "%s:get ky low failed\n",
+ __func__);
+
+ *pvalue = ((u16)tmp1<<8) + (u16)tmp2;
+ return err;
+}
+static int ft5402_set_lemda_x(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_LEMDA_X,
+ value);
+}
+
+static int ft5402_get_lemda_x(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_LEMDA_X,
+ pvalue);
+}
+static int ft5402_set_lemda_y(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_LEMDA_Y,
+ value);
+}
+
+static int ft5402_get_lemda_y(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_LEMDA_Y,
+ pvalue);
+}
+static int ft5402_set_pos_x(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_DIRECTION,
+ value);
+}
+
+static int ft5402_get_pos_x(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_DIRECTION,
+ pvalue);
+}
+
+static int ft5402_set_scan_select(struct i2c_client *client, u8 value)
+{
+ return ft5402_write_reg(client, FT5402_REG_SCAN_SELECT,
+ value);
+}
+
+static int ft5402_get_scan_select(struct i2c_client *client, u8 *pvalue)
+{
+ return ft5402_read_reg(client, FT5402_REG_SCAN_SELECT,
+ pvalue);
+}
+
+static int ft5402_set_other_param(struct i2c_client *client)
+{
+ int err = 0;
+ err = ft5402_write_reg(client, FT5402_REG_THGROUP, (u8)(g_param_ft5402.ft5402_THGROUP));
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THGROUP failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_THPEAK, g_param_ft5402.ft5402_THPEAK);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THPEAK failed.\n",
+ __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_PWMODE_CTRL,
+ g_param_ft5402.ft5402_PWMODE_CTRL);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PERIOD_CTRL failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_PERIOD_ACTIVE,
+ g_param_ft5402.ft5402_PERIOD_ACTIVE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PERIOD_ACTIVE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM,
+ g_param_ft5402.ft5402_FACE_DETECT_STATISTICS_TX_NUM);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FACE_DETECT_STATISTICS_TX_NUM failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_MAX_TOUCH_VALUE_HIGH,
+ g_param_ft5402.ft5402_MAX_TOUCH_VALUE>>8);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_HIGH failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_MAX_TOUCH_VALUE_LOW,
+ g_param_ft5402.ft5402_MAX_TOUCH_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_LOW failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FACE_DETECT_MODE,
+ g_param_ft5402.ft5402_FACE_DETECT_MODE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FACE_DETECT_MODE failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_DRAW_LINE_TH,
+ g_param_ft5402.ft5402_DRAW_LINE_TH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write DRAW_LINE_TH failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_POINTS_SUPPORTED,
+ g_param_ft5402.ft5402_POINTS_SUPPORTED);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write POINTS_SUPPORTED failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_ESD_FILTER_FRAME,
+ g_param_ft5402.ft5402_ESD_FILTER_FRAME);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_FILTER_FRAME failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_POINTS_STABLE_MACRO,
+ g_param_ft5402.ft5402_POINTS_STABLE_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_POINTS_STABLE_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_DELTA_X,
+ g_param_ft5402.ft5402_MIN_DELTA_X);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_DELTA_X failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_DELTA_Y,
+ g_param_ft5402.ft5402_MIN_DELTA_Y);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_DELTA_Y failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_DELTA_STEP,
+ g_param_ft5402.ft5402_MIN_DELTA_STEP);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_DELTA_STEP failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_NOISE_MACRO,
+ g_param_ft5402.ft5402_ESD_NOISE_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_NOISE_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_DIFF_VAL,
+ g_param_ft5402.ft5402_ESD_DIFF_VAL);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_DIFF_VAL failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_NEGTIVE,
+ g_param_ft5402.ft5402_ESD_NEGTIVE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_NEGTIVE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_ESD_FILTER_FRAMES,
+ g_param_ft5402.ft5402_ESD_FILTER_FRAMES);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_ESD_FILTER_FRAMES failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_IO_LEVEL_SELECT,
+ g_param_ft5402.ft5402_IO_LEVEL_SELECT);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_IO_LEVEL_SELECT failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_POINTID_DELAY_COUNT,
+ g_param_ft5402.ft5402_POINTID_DELAY_COUNT);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_POINTID_DELAY_COUNT failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_LIFTUP_FILTER_MACRO,
+ g_param_ft5402.ft5402_LIFTUP_FILTER_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_LIFTUP_FILTER_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_DIFF_HANDLE_MACRO,
+ g_param_ft5402.ft5402_DIFF_HANDLE_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_DIFF_HANDLE_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MIN_WATER,
+ g_param_ft5402.ft5402_MIN_WATER);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MIN_WATER failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_MAX_NOISE,
+ g_param_ft5402.ft5402_MAX_NOISE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_MAX_NOISE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_WATER_START_RX,
+ g_param_ft5402.ft5402_WATER_START_RX);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_WATER_START_RX failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_WATER_START_TX,
+ g_param_ft5402.ft5402_WATER_START_TX);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_WATER_START_TX failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO,
+ g_param_ft5402.ft5402_HOST_NUMBER_SUPPORTED_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_HOST_NUMBER_SUPPORTED_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_RAISE_THGROUP,
+ g_param_ft5402.ft5402_RAISE_THGROUP);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_RAISE_THGROUP failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_CHARGER_STATE,
+ g_param_ft5402.ft5402_CHARGER_STATE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_CHARGER_STATE failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FILTERID_START,
+ g_param_ft5402.ft5402_FILTERID_START);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FILTERID_START failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO,
+ g_param_ft5402.ft5402_FRAME_FILTER_EN_MACRO);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_EN_MACRO failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH,
+ g_param_ft5402.ft5402_FRAME_FILTER_SUB_MAX_TH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_SUB_MAX_TH failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH,
+ g_param_ft5402.ft5402_FRAME_FILTER_ADD_MAX_TH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_ADD_MAX_TH failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ g_param_ft5402.ft5402_FRAME_FILTER_SKIP_START_FRAME);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_SKIP_START_FRAME failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_EN,
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_EN);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_BAND_EN failed.\n", __func__);
+ return err;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH,
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_WIDTH);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_FRAME_FILTER_BAND_WIDTH failed.\n", __func__);
+ return err;
+ }
+
+ return err;
+}
+
+static int ft5402_get_other_param(struct i2c_client *client)
+{
+ int err = 0;
+ u8 value = 0x00;
+ err = ft5402_read_reg(client, FT5402_REG_THGROUP, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THGROUP failed.\n", __func__);
+ return err;
+ } else
+ DBG("THGROUP=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_THPEAK, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write THPEAK failed.\n",
+ __func__);
+ return err;
+ } else
+ DBG("THPEAK=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_PWMODE_CTRL, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PWMODE_CTRL failed.\n", __func__);
+ return err;
+ } else
+ DBG("CTRL=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_PERIOD_ACTIVE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write PERIOD_ACTIVE failed.\n", __func__);
+ return err;
+ } else
+ DBG("PERIOD_ACTIVE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_MAX_TOUCH_VALUE_HIGH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_HIGH failed.\n", __func__);
+ return err;
+ } else
+ DBG("MAX_TOUCH_VALUE_HIGH=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_MAX_TOUCH_VALUE_LOW,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write MAX_TOUCH_VALUE_LOW failed.\n", __func__);
+ return err;
+ } else
+ DBG("MAX_TOUCH_VALUE_LOW=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_FACE_DETECT_MODE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FACE_DETECT_MODE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FACE_DEC_MODE=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_DRAW_LINE_TH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write DRAW_LINE_TH failed.\n", __func__);
+ return err;
+ } else
+ DBG("DRAW_LINE_TH=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_POINTS_SUPPORTED,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ft5402_POINTS_SUPPORTED failed.\n", __func__);
+ return err;
+ } else
+ DBG("ft5402_POINTS_SUPPORTED=%02x\n", value);
+ err = ft5402_read_reg(client, FT5402_REG_ESD_FILTER_FRAME,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_ESD_FILTER_FRAME failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_ESD_FILTER_FRAME=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_POINTS_STABLE_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_POINTS_STABLE_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_POINTS_STABLE_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_DELTA_X,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_DELTA_X failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_DELTA_X=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_DELTA_Y,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_DELTA_Y failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_DELTA_Y=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_DELTA_STEP,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_DELTA_STEP failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_DELTA_STEP=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_NOISE_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_NOISE_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_NOISE_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_DIFF_VAL,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_DIFF_VAL failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_DIFF_VAL=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_NEGTIVE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_NEGTIVE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_NEGTIVE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_ESD_FILTER_FRAMES,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_ESD_FILTER_FRAMES failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_ESD_FILTER_FRAMES=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_IO_LEVEL_SELECT,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_IO_LEVEL_SELECT failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_IO_LEVEL_SELECT=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_POINTID_DELAY_COUNT,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_POINTID_DELAY_COUNT failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_POINTID_DELAY_COUNT=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_LIFTUP_FILTER_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_LIFTUP_FILTER_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_LIFTUP_FILTER_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_DIFF_HANDLE_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_DIFF_HANDLE_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_DIFF_HANDLE_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MIN_WATER,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MIN_WATER failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MIN_WATER=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_MAX_NOISE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_MAX_NOISE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_MAX_NOISE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_WATER_START_RX,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_WATER_START_RX failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_WATER_START_RX=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_WATER_START_TX,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_WATER_START_TX failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_WATER_START_TX=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_RAISE_THGROUP,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_RAISE_THGROUP failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_RAISE_THGROUP=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_CHARGER_STATE,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_CHARGER_STATE failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_CHARGER_STATE=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FILTERID_START,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FILTERID_START failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FILTERID_START=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_EN,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_BAND_EN failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_BAND_EN=%02x\n", value);
+
+ err = ft5402_read_reg(client, FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH failed.\n", __func__);
+ return err;
+ } else
+ DBG("FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH=%02x\n", value);
+
+ return err;
+}
+int ft5402_get_ic_param(struct i2c_client *client)
+{
+ int err = 0;
+ int i = 0;
+ u8 value = 0x00;
+ u16 xvalue = 0x0000, yvalue = 0x0000;
+
+ /*enter factory mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_FACTORYMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter factory mode failed.\n", __func__);
+ goto RETURN_WORK;
+ }
+
+ for (i = 0; i < g_ft5402_tx_num; i++) {
+ DBG("tx%d:", i);
+ /*get tx order*/
+ err = ft5402_get_tx_order(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("order=%d ", value);
+ /*get tx cap*/
+ err = ft5402_get_tx_cap(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("cap=%02x\n", value);
+ }
+ /*get tx offset*/
+ err = ft5402_get_tx_offset(client, 0, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx 0 offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("tx offset = %02x\n", value);
+
+ /*get rx offset and cap*/
+ for (i = 0; i < g_ft5402_rx_num; i++) {
+ /*get rx order*/
+ DBG("rx%d:", i);
+ err = ft5402_get_rx_order(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("order=%d ", value);
+ /*get rx cap*/
+ err = ft5402_get_rx_cap(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ DBG("cap=%02x ", value);
+ err = ft5402_get_rx_offset(client, i, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ DBG("offset=%02x\n", value);
+ }
+
+ /*get scan select*/
+ err = ft5402_get_scan_select(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get scan select.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("scan select = %02x\n", value);
+
+ /*get tx number*/
+ err = ft5402_get_tx_num(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get tx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("tx num = %02x\n", value);
+ /*get rx number*/
+ err = ft5402_get_rx_num(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get rx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("rx num = %02x\n", value);
+
+ /*get gain*/
+ err = ft5402_get_gain(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get gain.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("gain = %02x\n", value);
+ /*get voltage*/
+ err = ft5402_get_vol(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get voltage.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("voltage = %02x\n", value);
+ /*get start rx*/
+ err = ft5402_get_start_rx(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get start rx.\n",
+ __func__);
+ goto RETURN_WORK;
+ } else
+ DBG("start rx = %02x\n", value);
+ /*get adc target*/
+ err = ft5402_get_adc_target(client, &xvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get adc target.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("ADC target = %02x\n", xvalue);
+
+
+RETURN_WORK:
+ /*enter work mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_WORKMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter work mode failed.\n", __func__);
+ goto ERR_EXIT;
+ }
+
+ /*get resolution*/
+ err = ft5402_get_Resolution(client, &xvalue, &yvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get resolution.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("resolution X = %d Y = %d\n", xvalue, yvalue);
+
+
+ /*get face detect statistics tx num*/
+ err = ft5402_get_face_detect_statistics_tx_num(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not get face detect statistics tx num.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_FACE_DETECT_STATISTICS_TX_NUM = %02x\n", value);
+ /*get face detect pre value*/
+ err = ft5402_get_face_detect_pre_value(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not get face detect pre value.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_FACE_DETECT_PRE_VALUE = %02x\n", value);
+ /*get face detect num*/
+ err = ft5402_get_face_detect_num(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get face detect num.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("face detect num = %02x\n", value);
+
+ /*get min peak value*/
+ err = ft5402_get_peak_value_min(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get min peak value.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_BIGAREA_PEAK_VALUE_MIN = %02x\n", value);
+ /*get diff value over num*/
+ err = ft5402_get_diff_value_over_num(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get diff value over num.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_BIGAREA_DIFF_VALUE_OVER_NUM = %02x\n", value);
+ /*get customer id*/
+ err = ft5402_get_customer_id(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get customer id.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("FT5402_CUSTOMER_ID = %02x\n", value);
+ /*get kx*/
+ err = ft5402_get_kx(client, &xvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get kx.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("kx = %02x\n", xvalue);
+ /*get ky*/
+ err = ft5402_get_ky(client, &xvalue);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get ky.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("ky = %02x\n", xvalue);
+ /*get lemda x*/
+ err = ft5402_get_lemda_x(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get lemda x.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("lemda x = %02x\n", value);
+ /*get lemda y*/
+ err = ft5402_get_lemda_y(client,
+ &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get lemda y.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("lemda y = %02x\n", value);
+ /*get pos x*/
+ err = ft5402_get_pos_x(client, &value);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not get pos x.\n",
+ __func__);
+ goto ERR_EXIT;
+ } else
+ DBG("pos x = %02x\n", value);
+
+ err = ft5402_get_other_param(client);
+
+ERR_EXIT:
+ return err;
+}
+
+int ft5402_Init_IC_Param(struct i2c_client *client)
+{
+ int err = 0;
+ int i = 0;
+
+ /*enter factory mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_FACTORYMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter factory mode failed.\n", __func__);
+ goto RETURN_WORK;
+ }
+
+ for (i = 0; i < g_ft5402_tx_num; i++) {
+ if (g_ft5402_tx_order[i] != 0xFF) {
+ /*set tx order*/
+ err = ft5402_set_tx_order(client, i, g_ft5402_tx_order[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ }
+ /*set tx cap*/
+ err = ft5402_set_tx_cap(client, i, g_ft5402_tx_cap[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ }
+ /*set tx offset*/
+ err = ft5402_set_tx_offset(client, 0, g_ft5402_tx_offset);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx 0 offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ /*set rx offset and cap*/
+ for (i = 0; i < g_ft5402_rx_num; i++) {
+ /*set rx order*/
+ err = ft5402_set_rx_order(client, i, g_ft5402_rx_order[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx%d order.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ /*set rx cap*/
+ err = ft5402_set_rx_cap(client, i, g_ft5402_rx_cap[i]);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx%d cap.\n",
+ __func__, i);
+ goto RETURN_WORK;
+ }
+ }
+ for (i = 0; i < g_ft5402_rx_num/2; i++) {
+ err = ft5402_set_rx_offset(client, i*2, g_ft5402_rx_offset[i]>>4);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ err = ft5402_set_rx_offset(client, i*2+1, g_ft5402_rx_offset[i]&0x0F);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx offset.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ }
+
+ /*set scan select*/
+ err = ft5402_set_scan_select(client, g_ft5402_scanselect);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set scan select.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ /*set tx number*/
+ err = ft5402_set_tx_num(client, g_ft5402_tx_num);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set tx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ /*set rx number*/
+ err = ft5402_set_rx_num(client, g_ft5402_rx_num);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set rx num.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ /*set gain*/
+ err = ft5402_set_gain(client, g_ft5402_gain);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set gain.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+ /*set voltage*/
+ err = ft5402_set_vol(client, g_ft5402_voltage);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set voltage.\n",
+ __func__);
+ goto RETURN_WORK;
+ }
+
+ err = ft5402_write_reg(client, FT5402_REG_ADC_TARGET_HIGH,
+ g_param_ft5402.ft5402_ADC_TARGET>>8);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ADC_TARGET_HIGH failed.\n", __func__);
+ return err;
+ }
+ err = ft5402_write_reg(client, FT5402_REG_ADC_TARGET_LOW,
+ g_param_ft5402.ft5402_ADC_TARGET);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:write ADC_TARGET_LOW failed.\n", __func__);
+ return err;
+ }
+
+RETURN_WORK:
+ /*enter work mode*/
+ err = ft5402_write_reg(client, FT5402_REG_DEVICE_MODE, FT5402_WORKMODE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:enter work mode failed.\n", __func__);
+ goto ERR_EXIT;
+ }
+
+ /*set resolution*/
+ err = ft5402_set_Resolution(client, g_param_ft5402.ft5402_RESOLUTION_X,
+ g_param_ft5402.ft5402_RESOLUTION_Y);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set resolution.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+
+ /*set face detect statistics tx num*/
+ err = ft5402_set_face_detect_statistics_tx_num(client,
+ g_param_ft5402.ft5402_FACE_DETECT_STATISTICS_TX_NUM);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not set face detect statistics tx num.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set face detect pre value*/
+ err = ft5402_set_face_detect_pre_value(client,
+ g_param_ft5402.ft5402_FACE_DETECT_PRE_VALUE);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "%s:could not set face detect pre value.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set face detect num*/
+ err = ft5402_set_face_detect_num(client,
+ g_param_ft5402.ft5402_FACE_DETECT_NUM);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set face detect num.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+
+ /*set min peak value*/
+ err = ft5402_set_peak_value_min(client,
+ g_param_ft5402.ft5402_BIGAREA_PEAK_VALUE_MIN);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set min peak value.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set diff value over num*/
+ err = ft5402_set_diff_value_over_num(client,
+ g_param_ft5402.ft5402_BIGAREA_DIFF_VALUE_OVER_NUM);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set diff value over num.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set customer id*/
+ err = ft5402_set_customer_id(client,
+ g_param_ft5402.ft5402_CUSTOMER_ID);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set customer id.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set kx*/
+ err = ft5402_set_kx(client, g_param_ft5402.ft5402_KX);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set kx.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set ky*/
+ err = ft5402_set_ky(client, g_param_ft5402.ft5402_KY);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set ky.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set lemda x*/
+ err = ft5402_set_lemda_x(client,
+ g_param_ft5402.ft5402_LEMDA_X);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set lemda x.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set lemda y*/
+ err = ft5402_set_lemda_y(client,
+ g_param_ft5402.ft5402_LEMDA_Y);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set lemda y.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+ /*set pos x*/
+ err = ft5402_set_pos_x(client, g_param_ft5402.ft5402_DIRECTION);
+ if (err < 0) {
+ dev_err(&client->dev, "%s:could not set pos x.\n",
+ __func__);
+ goto ERR_EXIT;
+ }
+
+ err = ft5402_set_other_param(client);
+
+ERR_EXIT:
+ return err;
+}
+
+
+char dst[512];
+static char * ft5402_sub_str(char * src, int n)
+{
+ char *p = src;
+ int i;
+ int m = 0;
+ int len = strlen(src);
+
+ while (n >= 1 && m <= len) {
+ i = 0;
+ dst[10] = ' ';
+ n--;
+ while ( *p != ',' && *p != ' ') {
+ dst[i++] = *(p++);
+ m++;
+ if (i >= len)
+ break;
+ }
+ dst[i++] = '\0';
+ p++;
+ }
+ return dst;
+}
+static int ft5402_GetInISize(char *config_name)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize = 0;
+ char filepath[128];
+ memset(filepath, 0, sizeof(filepath));
+
+ sprintf(filepath, "%s%s", FT5402_INI_FILEPATH, config_name);
+
+ if (NULL == pfile)
+ pfile = filp_open(filepath, O_RDONLY, 0);
+
+ if (IS_ERR(pfile)) {
+ pr_err("error occured while opening file %s.\n", filepath);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ filp_close(pfile, NULL);
+ return fsize;
+}
+
+static int ft5x0x_ReadInIData(char *config_name,
+ char *config_buf)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize;
+ char filepath[128];
+ loff_t pos;
+ mm_segment_t old_fs;
+
+ memset(filepath, 0, sizeof(filepath));
+ sprintf(filepath, "%s%s", FT5402_INI_FILEPATH, config_name);
+ if (NULL == pfile)
+ pfile = filp_open(filepath, O_RDONLY, 0);
+ if (IS_ERR(pfile)) {
+ pr_err("error occured while opening file %s.\n", filepath);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ pos = 0;
+ vfs_read(pfile, config_buf, fsize, &pos);
+ filp_close(pfile, NULL);
+ set_fs(old_fs);
+
+ return 0;
+}
+
+int ft5402_Get_Param_From_Ini(char *config_name)
+{
+ char key[64];
+ char value[512];
+ char section[64];
+ int i = 0;//,ret=0;
+ int j = 0;
+ char *filedata = NULL;
+ unsigned char legal_byte1 = 0x00;
+ unsigned char legal_byte2 = 0x00;
+
+ int inisize = ft5402_GetInISize(config_name);
+
+ if (inisize <= 0) {
+ pr_err("%s ERROR:Get firmware size failed\n",
+ __func__);
+ return -EIO;
+ }
+
+ filedata = kmalloc(inisize + 1, GFP_ATOMIC);
+
+ if (ft5x0x_ReadInIData(config_name, filedata)) {
+ pr_err("%s() - ERROR: request_firmware failed\n",
+ __func__);
+ kfree(filedata);
+ return -EIO;
+ }
+
+ /*check ini if it is illegal*/
+ sprintf(section, "%s", FT5402_APP_LEGAL);
+ sprintf(key, "%s", FT5402_APP_LEGAL_BYTE_1_STR);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ legal_byte1 = atoi(value);
+ DBG("legal_byte1=%s\n", value);
+ sprintf(key, "%s", FT5402_APP_LEGAL_BYTE_2_STR);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ legal_byte2 = atoi(value);
+ DBG("lega2_byte1=%s\n", value);
+ if(FT5402_APP_LEGAL_BYTE_1_VALUE == legal_byte1 &&
+ FT5402_APP_LEGAL_BYTE_2_VALUE == legal_byte2)
+ DBG("the ini file is valid\n");
+ else {
+ pr_err("[FTS]-----the ini file is invalid!please check it.\n");
+ goto ERROR_RETURN;
+ }
+
+ /*get ini param*/
+ sprintf(section, "%s", FT5402_APP_NAME);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_KX = atoi(value);
+ DBG("ft5402_KX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_KY = atoi(value);
+ DBG("ft5402_KY=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_LEMDA_X = atoi(value);
+ DBG("ft5402_LEMDA_X=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_LEMDA_Y = atoi(value);
+ DBG("ft5402_LEMDA_Y=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_RESOLUTION_X = atoi(value);
+ DBG("ft5402_RESOLUTION_X=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_RESOLUTION_Y = atoi(value);
+ DBG("ft5402_RESOLUTION_Y=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_DIRECTION= atoi(value);
+ DBG("ft5402_DIRECTION=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_PRE_VALUE = atoi(value);
+ DBG("ft5402_FACE_DETECT_PRE_VALUE=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_NUM = atoi(value);
+ DBG("ft5402_FACE_DETECT_NUM=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_BIGAREA_PEAK_VALUE_MIN = atoi(value);/*The min value to be decided as the big point*/
+ DBG("ft5402_BIGAREA_PEAK_VALUE_MIN=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_BIGAREA_DIFF_VALUE_OVER_NUM = atoi(value);/*The min big points of the big area*/
+ DBG("ft5402_BIGAREA_DIFF_VALUE_OVER_NUM=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_CUSTOMER_ID = atoi(value);
+ DBG("ft5402_CUSTOM_ID=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_PERIOD_ACTIVE = atoi(value);
+ DBG("ft5402_PERIOD_ACTIVE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_STATISTICS_TX_NUM = atoi(value);
+ DBG("ft5402_FACE_DETECT_STATISTICS_TX_NUM=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_THGROUP = atoi(value);
+ DBG("ft5402_THGROUP=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_THPEAK = atoi(value);
+ DBG("ft5402_THPEAK=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FACE_DETECT_MODE = atoi(value);
+ DBG("ft5402_FACE_DETECT_MODE=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MAX_TOUCH_VALUE = atoi(value);
+ DBG("ft5402_MAX_TOUCH_VALUE=%s\n", value);
+
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_PWMODE_CTRL= atoi(value);
+ DBG("ft5402_PWMODE_CTRL=%s\n", value);
+
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+
+ i++;
+ g_param_ft5402.ft5402_DRAW_LINE_TH = atoi(value);
+ DBG("ft5402_DRAW_LINE_TH=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_POINTS_SUPPORTED= atoi(value);
+ DBG("ft5402_POINTS_SUPPORTED=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_START_RX = atoi(value);
+ DBG("ft5402_START_RX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+
+ g_param_ft5402.ft5402_ADC_TARGET = atoi(value);
+ DBG("ft5402_ADC_TARGET=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+
+ g_param_ft5402.ft5402_ESD_FILTER_FRAME = atoi(value);
+ DBG("ft5402_ESD_FILTER_FRAME=%s\n", value);
+
+/*********************************************************************/
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_tx_num = atoi(value);
+ DBG("ft5402_tx_num=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_rx_num = atoi(value);
+ DBG("ft5402_rx_num=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_gain = atoi(value);
+ DBG("ft5402_gain=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_voltage = atoi(value);
+ DBG("ft5402_voltage=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_scanselect = atoi(value);
+ DBG("ft5402_scanselect=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_tx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_tx_order[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_tx_order=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_ft5402_tx_offset = atoi(value);
+ DBG("ft5402_tx_offset=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_tx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_tx_cap[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_tx_cap=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_rx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_rx_order[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_rx_order=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_rx_num/2; j++)
+ {
+ char * psrc = value;
+ g_ft5402_rx_offset[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_rx_offset=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ for(j = 0; j < g_ft5402_rx_num; j++)
+ {
+ char * psrc = value;
+ g_ft5402_rx_cap[j] = atoi(ft5402_sub_str(psrc, j+1));
+ }
+ DBG("ft5402_rx_cap=%s\n", value);
+
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_POINTS_STABLE_MACRO = atoi(value);
+ DBG("ft5402_POINTS_STABLE_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_DELTA_X = atoi(value);
+ DBG("ft5402_MIN_DELTA_X=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_DELTA_Y = atoi(value);
+ DBG("ft5402_MIN_DELTA_Y=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_DELTA_STEP = atoi(value);
+ DBG("ft5402_MIN_DELTA_STEP=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_NOISE_MACRO = atoi(value);
+ DBG("ft5402_ESD_NOISE_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_DIFF_VAL = atoi(value);
+ DBG("ft5402_ESD_DIFF_VAL=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_NEGTIVE = atoi(value);
+ DBG("ft5402_ESD_NEGTIVE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_ESD_FILTER_FRAMES = atoi(value);
+ DBG("ft5402_ESD_FILTER_FRAMES=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_IO_LEVEL_SELECT = atoi(value);
+ DBG("ft5402_IO_LEVEL_SELECT=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_POINTID_DELAY_COUNT = atoi(value);
+ DBG("ft5402_POINTID_DELAY_COUNT=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_LIFTUP_FILTER_MACRO = atoi(value);
+ DBG("ft5402_LIFTUP_FILTER_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_DIFF_HANDLE_MACRO = atoi(value);
+ DBG("ft5402_DIFF_HANDLE_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MIN_WATER = atoi(value);
+ DBG("ft5402_MIN_WATER=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_MAX_NOISE = atoi(value);
+ DBG("ft5402_MAX_NOISE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_WATER_START_RX = atoi(value);
+ DBG("ft5402_WATER_START_RX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_WATER_START_TX = atoi(value);
+ DBG("ft5402_WATER_START_TX=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_HOST_NUMBER_SUPPORTED_MACRO = atoi(value);
+ DBG("ft5402_HOST_NUMBER_SUPPORTED_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_RAISE_THGROUP = atoi(value);
+ DBG("ft5402_RAISE_THGROUP=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_CHARGER_STATE = atoi(value);
+ DBG("ft5402_CHARGER_STATE=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FILTERID_START = atoi(value);
+ DBG("ft5402_FILTERID_START=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_EN_MACRO = atoi(value);
+ DBG("ft5402_FRAME_FILTER_EN_MACRO=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_SUB_MAX_TH = atoi(value);
+ DBG("ft5402_FRAME_FILTER_SUB_MAX_TH=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_ADD_MAX_TH = atoi(value);
+ DBG("ft5402_FRAME_FILTER_ADD_MAX_TH=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_SKIP_START_FRAME = atoi(value);
+ DBG("ft5402_FRAME_FILTER_SKIP_START_FRAME=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_EN = atoi(value);
+ DBG("ft5402_FRAME_FILTER_BAND_EN=%s\n", value);
+
+ sprintf(key, "%s", String_Param_FT5402[i]);
+ if (ini_get_key(filedata,section,key,value)<0)
+ goto ERROR_RETURN;
+ i++;
+ g_param_ft5402.ft5402_FRAME_FILTER_BAND_WIDTH = atoi(value);
+ DBG("ft5402_FRAME_FILTER_BAND_WIDTH=%s\n", value);
+
+
+ if (filedata)
+ kfree(filedata);
+ return 0;
+ERROR_RETURN:
+ if (filedata)
+ kfree(filedata);
+ return -1;
+}
+
diff --git a/drivers/input/touchscreen/ft6x0x/ft5402_config.h b/drivers/input/touchscreen/ft6x0x/ft5402_config.h
new file mode 100755
index 00000000..4d4935ed
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft5402_config.h
@@ -0,0 +1,71 @@
+#ifndef __FT5402_CONFIG_H__
+#define __FT5402_CONFIG_H__
+/*FT5402 config*/
+
+
+#define FT5402_START_RX 0
+#define FT5402_ADC_TARGET 8500
+#define FT5402_KX 120
+#define FT5402_KY 120
+#define FT5402_RESOLUTION_X 480
+#define FT5402_RESOLUTION_Y 800
+#define FT5402_LEMDA_X 0
+#define FT5402_LEMDA_Y 0
+#define FT5402_PWMODE_CTRL 1
+#define FT5402_POINTS_SUPPORTED 5
+#define FT5402_DRAW_LINE_TH 150
+#define FT5402_FACE_DETECT_MODE 0
+#define FT5402_FACE_DETECT_STATISTICS_TX_NUM 3
+#define FT5402_FACE_DETECT_PRE_VALUE 20
+#define FT5402_FACE_DETECT_NUM 10
+#define FT5402_THGROUP 25
+#define FT5402_THPEAK 60
+#define FT5402_BIGAREA_PEAK_VALUE_MIN 100
+#define FT5402_BIGAREA_DIFF_VALUE_OVER_NUM 50
+#define FT5402_MIN_DELTA_X 2
+#define FT5402_MIN_DELTA_Y 2
+#define FT5402_MIN_DELTA_STEP 2
+#define FT5402_ESD_DIFF_VAL 20
+#define FT5402_ESD_NEGTIVE -50
+#define FT5402_ESD_FILTER_FRAME 10
+#define FT5402_MAX_TOUCH_VALUE 600
+#define FT5402_CUSTOMER_ID 121
+#define FT5402_IO_LEVEL_SELECT 0
+#define FT5402_DIRECTION 1
+#define FT5402_POINTID_DELAY_COUNT 3
+#define FT5402_LIFTUP_FILTER_MACRO 1
+#define FT5402_POINTS_STABLE_MACRO 1
+#define FT5402_ESD_NOISE_MACRO 1
+#define FT5402_RV_G_PERIOD_ACTIVE 16
+#define FT5402_DIFFDATA_HANDLE 1
+#define FT5402_MIN_WATER_VAL -50
+#define FT5402_MAX_NOISE_VAL 10
+#define FT5402_WATER_HANDLE_START_RX 0
+#define FT5402_WATER_HANDLE_START_TX 0
+#define FT5402_HOST_NUMBER_SUPPORTED 1
+#define FT5402_RV_G_RAISE_THGROUP 30
+#define FT5402_RV_G_CHARGER_STATE 0
+#define FT5402_RV_G_FILTERID_START 2
+#define FT5402_FRAME_FILTER_EN 1
+#define FT5402_FRAME_FILTER_SUB_MAX_TH 2
+#define FT5402_FRAME_FILTER_ADD_MAX_TH 2
+#define FT5402_FRAME_FILTER_SKIP_START_FRAME 6
+#define FT5402_FRAME_FILTER_BAND_EN 1
+#define FT5402_FRAME_FILTER_BAND_WIDTH 128
+#define FT5402_OTP_PARAM_ID 0
+
+
+unsigned char g_ft5402_tx_num = 27;
+unsigned char g_ft5402_rx_num = 16;
+unsigned char g_ft5402_gain = 10;
+unsigned char g_ft5402_voltage = 3;
+unsigned char g_ft5402_scanselect = 8;
+unsigned char g_ft5402_tx_order[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26};
+unsigned char g_ft5402_tx_offset = 2;
+unsigned char g_ft5402_tx_cap[] = {42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42};
+unsigned char g_ft5402_rx_order[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+unsigned char g_ft5402_rx_offset[] = {68,68,68,68,68,68,68,68};
+unsigned char g_ft5402_rx_cap[] = {84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84};
+
+
+#endif \ No newline at end of file
diff --git a/drivers/input/touchscreen/ft6x0x/ft5402_ini_config.h b/drivers/input/touchscreen/ft6x0x/ft5402_ini_config.h
new file mode 100755
index 00000000..138f42e2
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft5402_ini_config.h
@@ -0,0 +1,411 @@
+#ifndef __LINUX_FT5402_INI_CONFIG_H__
+#define __LINUX_FT5402_INI_CONFIG_H
+
+
+/*Init param register address*/
+/*factory mode register from 14-131*/
+#define FT5402_REG_TX_NUM 0x03
+#define FT5402_REG_RX_NUM 0x04
+#define FT5402_REG_VOLTAGE 0x05
+#define FT5402_REG_GAIN 0x07
+#define FT5402_REG_SCAN_SELECT 0x4E
+#define FT5402_REG_TX_ORDER_START 0x50
+#define FT5402_REG_TX_CAP_START 0x78
+#define FT5402_REG_TX_OFFSET_START 0xBF
+#define FT5402_REG_RX_ORDER_START 0xeb
+#define FT5402_REG_RX_CAP_START 0xA0
+#define FT5402_REG_RX_OFFSET_START 0xD3
+#define FT5402_REG_START_RX 0x06
+#define FT5402_REG_ADC_TARGET_HIGH 0x08
+#define FT5402_REG_ADC_TARGET_LOW 0x09
+
+
+#define FT5402_REG_DEVICE_MODE 0x00
+
+
+/*work mode register from 0-13(0,1,12,13verify or Reserved)and 132-177(159 Reserved)*/
+#define FT5402_REG_THGROUP (0x00+0x80)
+#define FT5402_REG_THPEAK (0x01+0x80)
+#define FT5402_REG_PWMODE_CTRL (0x06+0x80)
+#define FT5402_REG_PERIOD_ACTIVE (0x59+0x80)
+#define FT5402_REG_POINTS_SUPPORTED (0x0A+0x80)
+#define FT5402_REG_ESD_FILTER_FRAME (0x4F+0x80)
+
+#define FT5402_REG_RESOLUTION_X_H (0x18+0x80)
+#define FT5402_REG_RESOLUTION_X_L (0x19+0x80)
+#define FT5402_REG_RESOLUTION_Y_H (0x1a+0x80)
+#define FT5402_REG_RESOLUTION_Y_L (0x1b+0x80)
+#define FT5402_REG_KX_H (0x1c+0x80)
+#define FT5402_REG_KX_L (0x9d)
+#define FT5402_REG_KY_H (0x9e)
+#define FT5402_REG_KY_L (0x1f+0x80)
+#define FT5402_REG_CUSTOMER_ID (0xA8)
+#define FT5402_REG_DRAW_LINE_TH (0xAe)
+#define FT5402_REG_FACE_DETECT_MODE (0xB0)
+#define FT5402_REG_MAX_TOUCH_VALUE_HIGH (0xD0)
+#define FT5402_REG_MAX_TOUCH_VALUE_LOW (0xD1)
+
+#define FT5402_REG_DIRECTION (0x53+0x80)
+#define FT5402_REG_LEMDA_X (0x41+0x80)
+#define FT5402_REG_LEMDA_Y (0x42+0x80)
+#define FT5402_REG_FACE_DETECT_STATISTICS_TX_NUM (0x43+0x80)
+#define FT5402_REG_FACE_DETECT_PRE_VALUE (0x44+0x80)
+#define FT5402_REG_FACE_DETECT_NUM (0x45+0x80)
+#define FT5402_REG_BIGAREA_PEAK_VALUE_MIN (0x33+0x80)
+#define FT5402_REG_BIGAREA_DIFF_VALUE_OVER_NUM (0x34+0x80)
+
+/**************************************************************************/
+#define FT5402_REG_FT5402_POINTS_STABLE_MACRO (0x57+0x80)
+#define FT5402_REG_FT5402_MIN_DELTA_X (0x4a+0x80)
+#define FT5402_REG_FT5402_MIN_DELTA_Y (0x4b+0x80)
+#define FT5402_REG_FT5402_MIN_DELTA_STEP (0x4c+0x80)
+
+#define FT5402_REG_FT5402_ESD_NOISE_MACRO (0x58+0x80)
+#define FT5402_REG_FT5402_ESD_DIFF_VAL (0x4d+0x80)
+#define FT5402_REG_FT5402_ESD_NEGTIVE (0xCe)
+#define FT5402_REG_FT5402_ESD_FILTER_FRAMES (0x4f+0x80)
+
+#define FT5402_REG_FT5402_IO_LEVEL_SELECT (0x52+0x80)
+
+#define FT5402_REG_FT5402_POINTID_DELAY_COUNT (0x54+0x80)
+
+#define FT5402_REG_FT5402_LIFTUP_FILTER_MACRO (0x55+0x80)
+
+#define FT5402_REG_FT5402_DIFF_HANDLE_MACRO (0x5A+0x80)
+#define FT5402_REG_FT5402_MIN_WATER (0x5B+0x80)
+#define FT5402_REG_FT5402_MAX_NOISE (0x5C+0x80)
+#define FT5402_REG_FT5402_WATER_START_RX (0x5D+0x80)
+#define FT5402_REG_FT5402_WATER_START_TX (0xDE)
+
+#define FT5402_REG_FT5402_HOST_NUMBER_SUPPORTED_MACRO (0x38+0x80)
+#define FT5402_REG_FT5402_RAISE_THGROUP (0x36+0x80)
+#define FT5402_REG_FT5402_CHARGER_STATE (0x35+0x80)
+
+#define FT5402_REG_FT5402_FILTERID_START (0x37+0x80)
+
+#define FT5402_REG_FT5402_FRAME_FILTER_EN_MACRO (0x5F+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_SUB_MAX_TH (0x60+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_ADD_MAX_TH (0x61+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_SKIP_START_FRAME (0x62+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_BAND_EN (0x63+0x80)
+#define FT5402_REG_FT5402_FRAME_FILTER_BAND_WIDTH (0x64+0x80)
+/**************************************************************************/
+
+#define FT5402_REG_TEST_MODE 0x04
+#define FT5402_REG_TEST_MODE_2 0x05
+#define FT5402_TX_TEST_MODE_1 0x28
+#define FT5402_RX_TEST_MODE_1 0x1E
+#define FT5402_FACTORYMODE_VALUE 0x40
+#define FT5402_WORKMODE_VALUE 0x00
+
+/************************************************************************/
+/* string */
+/************************************************************************/
+#define STRING_FT5402_KX "FT5X02_KX"
+#define STRING_FT5402_KY "FT5X02_KY"
+#define STRING_FT5402_LEMDA_X "FT5X02_LEMDA_X"
+#define STRING_FT5402_LEMDA_Y "FT5X02_LEMDA_Y"
+#define STRING_FT5402_RESOLUTION_X "FT5X02_RESOLUTION_X"
+#define STRING_FT5402_RESOLUTION_Y "FT5X02_RESOLUTION_Y"
+#define STRING_FT5402_DIRECTION "FT5X02_DIRECTION"
+
+
+
+#define STRING_FT5402_FACE_DETECT_PRE_VALUE "FT5X02_FACE_DETECT_PRE_VALUE"
+#define STRING_FT5402_FACE_DETECT_NUM "FT5X02_FACE_DETECT_NUM"
+#define STRING_FT5402_BIGAREA_PEAK_VALUE_MIN "FT5X02_BIGAREA_PEAK_VALUE_MIN"
+#define STRING_FT5402_BIGAREA_DIFF_VALUE_OVER_NUM "FT5X02_BIGAREA_DIFF_VALUE_OVER_NUM"
+#define STRING_FT5402_CUSTOMER_ID "FT5X02_CUSTOMER_ID"
+#define STRING_FT5402_PERIOD_ACTIVE "FT5X02_RV_G_PERIOD_ACTIVE"
+#define STRING_FT5402_FACE_DETECT_STATISTICS_TX_NUM "FT5X02_FACE_DETECT_STATISTICS_TX_NUM"
+
+#define STRING_FT5402_THGROUP "FT5X02_THGROUP"
+#define STRING_FT5402_THPEAK "FT5X02_THPEAK"
+#define STRING_FT5402_FACE_DETECT_MODE "FT5X02_FACE_DETECT_MODE"
+#define STRING_FT5402_MAX_TOUCH_VALUE "FT5X02_MAX_TOUCH_VALUE"
+
+#define STRING_FT5402_PWMODE_CTRL "FT5X02_PWMODE_CTRL"
+#define STRING_FT5402_DRAW_LINE_TH "FT5X02_DRAW_LINE_TH"
+
+#define STRING_FT5402_POINTS_SUPPORTED "FT5X02_POINTS_SUPPORTED"
+
+#define STRING_FT5402_START_RX "FT5X02_START_RX"
+#define STRING_FT5402_ADC_TARGET "FT5X02_ADC_TARGET"
+#define STRING_FT5402_ESD_FILTER_FRAME "FT5X02_ESD_FILTER_FRAME"
+
+#define STRING_FT5402_POINTS_STABLE_MACRO "FT5X02_POINTS_STABLE_MACRO"
+#define STRING_FT5402_MIN_DELTA_X "FT5X02_MIN_DELTA_X"
+#define STRING_FT5402_MIN_DELTA_Y "FT5X02_MIN_DELTA_Y"
+#define STRING_FT5402_MIN_DELTA_STEP "FT5X02_MIN_DELTA_STEP"
+
+#define STRING_FT5402_ESD_NOISE_MACRO "FT5X02_ESD_NOISE_MACRO"
+#define STRING_FT5402_ESD_DIFF_VAL "FT5X02_ESD_DIFF_VAL"
+#define STRING_FT5402_ESD_NEGTIVE "FT5X02_ESD_NEGTIVE"
+#define STRING_FT5402_ESD_FILTER_FRAME "FT5X02_ESD_FILTER_FRAME"
+
+#define STRING_FT5402_IO_LEVEL_SELECT "FT5X02_IO_LEVEL_SELECT"
+#define STRING_FT5402_POINTID_DELAY_COUNT "FT5X02_POINTID_DELAY_COUNT"
+
+#define STRING_FT5402_LIFTUP_FILTER_MACRO "FT5X02_LIFTUP_FILTER_MACRO"
+
+#define STRING_FT5402_DIFFDATA_HANDLE "FT5X02_DIFFDATA_HANDLE" //_MACRO
+#define STRING_FT5402_MIN_WATER_VAL "FT5X02_MIN_WATER_VAL"
+#define STRING_FT5402_MAX_NOISE_VAL "FT5X02_MAX_NOISE_VAL"
+#define STRING_FT5402_WATER_HANDLE_START_RX "FT5X02_WATER_HANDLE_START_RX"
+#define STRING_FT5402_WATER_HANDLE_START_TX "FT5X02_WATER_HANDLE_START_TX"
+
+#define STRING_FT5402_HOST_NUMBER_SUPPORTED "FT5X02_HOST_NUMBER_SUPPORTED"
+#define STRING_FT5402_RV_G_RAISE_THGROUP "FT5X02_RV_G_RAISE_THGROUP"
+#define STRING_FT5402_RV_G_CHARGER_STATE "FT5X02_RV_G_CHARGER_STATE"
+
+#define STRING_FT5402_RV_G_FILTERID_START "FT5X02_RV_G_FILTERID_START"
+
+#define STRING_FT5402_FRAME_FILTER_EN "FT5X02_FRAME_FILTER_EN"
+#define STRING_FT5402_FRAME_FILTER_SUB_MAX_TH "FT5X02_FRAME_FILTER_SUB_MAX_TH"
+#define STRING_FT5402_FRAME_FILTER_ADD_MAX_TH "FT5X02_FRAME_FILTER_ADD_MAX_TH"
+#define STRING_FT5402_FRAME_FILTER_SKIP_START_FRAME "FT5X02_FRAME_FILTER_SKIP_START_FRAME"
+#define STRING_FT5402_FRAME_FILTER_BAND_EN "FT5X02_FRAME_FILTER_BAND_EN"
+#define STRING_FT5402_FRAME_FILTER_BAND_WIDTH "FT5X02_FRAME_FILTER_BAND_WIDTH"
+
+
+#define STRING_ft5402_tx_num "FT5X02_tx_num"
+#define STRING_ft5402_rx_num "FT5X02_rx_num"
+#define STRING_ft5402_gain "FT5X02_gain"
+#define STRING_ft5402_voltage "FT5X02_voltage"
+#define STRING_ft5402_scanselect "FT5X02_scanselect"
+
+#define STRING_ft5402_tx_order "FT5X02_tx_order"
+#define STRING_ft5402_tx_offset "FT5X02_tx_offset"
+#define STRING_ft5402_tx_cap "FT5X02_tx_cap"
+
+#define STRING_ft5402_rx_order "FT5X02_rx_order"
+#define STRING_ft5402_rx_offset "FT5X02_rx_offset"
+#define STRING_ft5402_rx_cap "FT5X02_rx_cap"
+
+struct Struct_Param_FT5402 {
+ short ft5402_KX;
+ short ft5402_KY;
+ unsigned char ft5402_LEMDA_X;
+ unsigned char ft5402_LEMDA_Y;
+ short ft5402_RESOLUTION_X;
+ short ft5402_RESOLUTION_Y;
+ unsigned char ft5402_DIRECTION;
+ unsigned char ft5402_FACE_DETECT_PRE_VALUE;
+ unsigned char ft5402_FACE_DETECT_NUM;
+
+ unsigned char ft5402_BIGAREA_PEAK_VALUE_MIN;
+ unsigned char ft5402_BIGAREA_DIFF_VALUE_OVER_NUM;
+ unsigned char ft5402_CUSTOMER_ID;
+ unsigned char ft5402_PERIOD_ACTIVE;
+ unsigned char ft5402_FACE_DETECT_STATISTICS_TX_NUM;
+
+ short ft5402_THGROUP;
+ unsigned char ft5402_THPEAK;
+ unsigned char ft5402_FACE_DETECT_MODE;
+ short ft5402_MAX_TOUCH_VALUE;
+
+ unsigned char ft5402_PWMODE_CTRL;
+ unsigned char ft5402_DRAW_LINE_TH;
+ unsigned char ft5402_POINTS_SUPPORTED;
+
+ unsigned char ft5402_START_RX;
+ short ft5402_ADC_TARGET;
+ unsigned char ft5402_ESD_FILTER_FRAME;
+
+ unsigned char ft5402_POINTS_STABLE_MACRO;
+ unsigned char ft5402_MIN_DELTA_X;
+ unsigned char ft5402_MIN_DELTA_Y;
+ unsigned char ft5402_MIN_DELTA_STEP;
+
+ unsigned char ft5402_ESD_NOISE_MACRO;
+ unsigned char ft5402_ESD_DIFF_VAL;
+ char ft5402_ESD_NEGTIVE; //negtive
+ unsigned char ft5402_ESD_FILTER_FRAMES;
+
+ unsigned char ft5402_IO_LEVEL_SELECT;
+
+ unsigned char ft5402_POINTID_DELAY_COUNT;
+
+ unsigned char ft5402_LIFTUP_FILTER_MACRO;
+
+ unsigned char ft5402_DIFF_HANDLE_MACRO;
+ char ft5402_MIN_WATER; //negtive
+ unsigned char ft5402_MAX_NOISE;
+ unsigned char ft5402_WATER_START_RX;
+ unsigned char ft5402_WATER_START_TX;
+
+ unsigned char ft5402_HOST_NUMBER_SUPPORTED_MACRO;
+ unsigned char ft5402_RAISE_THGROUP;
+ unsigned char ft5402_CHARGER_STATE;
+
+ unsigned char ft5402_FILTERID_START;
+
+ unsigned char ft5402_FRAME_FILTER_EN_MACRO;
+ unsigned char ft5402_FRAME_FILTER_SUB_MAX_TH;
+ unsigned char ft5402_FRAME_FILTER_ADD_MAX_TH;
+ unsigned char ft5402_FRAME_FILTER_SKIP_START_FRAME;
+ unsigned char ft5402_FRAME_FILTER_BAND_EN;
+ unsigned char ft5402_FRAME_FILTER_BAND_WIDTH;
+
+};
+
+struct Struct_Param_FT5402 g_param_ft5402 = {
+ FT5402_KX,
+ FT5402_KY,
+ FT5402_LEMDA_X,
+ FT5402_LEMDA_Y,
+ FT5402_RESOLUTION_X,
+ FT5402_RESOLUTION_Y,
+ FT5402_DIRECTION,
+
+ FT5402_FACE_DETECT_PRE_VALUE,
+ FT5402_FACE_DETECT_NUM,
+ FT5402_BIGAREA_PEAK_VALUE_MIN,
+ FT5402_BIGAREA_DIFF_VALUE_OVER_NUM,
+ FT5402_CUSTOMER_ID,
+ FT5402_RV_G_PERIOD_ACTIVE,
+ FT5402_FACE_DETECT_STATISTICS_TX_NUM,
+
+ FT5402_THGROUP,
+ FT5402_THPEAK,
+ FT5402_FACE_DETECT_MODE,
+ FT5402_MAX_TOUCH_VALUE,
+
+ FT5402_PWMODE_CTRL,
+ FT5402_DRAW_LINE_TH,
+ FT5402_POINTS_SUPPORTED,
+
+ FT5402_START_RX,
+ FT5402_ADC_TARGET,
+ FT5402_ESD_FILTER_FRAME,
+
+ FT5402_POINTS_STABLE_MACRO,
+ FT5402_MIN_DELTA_X,
+ FT5402_MIN_DELTA_Y,
+ FT5402_MIN_DELTA_STEP,
+
+ FT5402_ESD_NOISE_MACRO,
+ FT5402_ESD_DIFF_VAL,
+ FT5402_ESD_NEGTIVE,
+ FT5402_ESD_FILTER_FRAME,
+
+ FT5402_IO_LEVEL_SELECT,
+
+ FT5402_POINTID_DELAY_COUNT,
+
+ FT5402_LIFTUP_FILTER_MACRO,
+
+ FT5402_DIFFDATA_HANDLE,
+ FT5402_MIN_WATER_VAL,
+ FT5402_MAX_NOISE_VAL,
+ FT5402_WATER_HANDLE_START_RX,
+ FT5402_WATER_HANDLE_START_TX,
+
+ FT5402_HOST_NUMBER_SUPPORTED,
+ FT5402_RV_G_RAISE_THGROUP,
+ FT5402_RV_G_CHARGER_STATE,
+
+ FT5402_RV_G_FILTERID_START,
+
+ FT5402_FRAME_FILTER_EN,
+ FT5402_FRAME_FILTER_SUB_MAX_TH,
+ FT5402_FRAME_FILTER_ADD_MAX_TH,
+ FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ FT5402_FRAME_FILTER_BAND_EN,
+ FT5402_FRAME_FILTER_BAND_WIDTH,
+};
+
+char String_Param_FT5402[][64] = {
+ STRING_FT5402_KX,
+ STRING_FT5402_KY,
+ STRING_FT5402_LEMDA_X,
+ STRING_FT5402_LEMDA_Y,
+ STRING_FT5402_RESOLUTION_X,
+ STRING_FT5402_RESOLUTION_Y,
+ STRING_FT5402_DIRECTION,
+ STRING_FT5402_FACE_DETECT_PRE_VALUE,
+ STRING_FT5402_FACE_DETECT_NUM,
+ STRING_FT5402_BIGAREA_PEAK_VALUE_MIN,
+ STRING_FT5402_BIGAREA_DIFF_VALUE_OVER_NUM,
+ STRING_FT5402_CUSTOMER_ID,
+ STRING_FT5402_PERIOD_ACTIVE,
+ STRING_FT5402_FACE_DETECT_STATISTICS_TX_NUM,
+
+ STRING_FT5402_THGROUP,
+ STRING_FT5402_THPEAK,
+ STRING_FT5402_FACE_DETECT_MODE,
+ STRING_FT5402_MAX_TOUCH_VALUE,
+
+ STRING_FT5402_PWMODE_CTRL,
+ STRING_FT5402_DRAW_LINE_TH,
+ STRING_FT5402_POINTS_SUPPORTED,
+
+ STRING_FT5402_START_RX,
+ STRING_FT5402_ADC_TARGET,
+ STRING_FT5402_ESD_FILTER_FRAME,
+
+
+ STRING_ft5402_tx_num,
+ STRING_ft5402_rx_num,
+ STRING_ft5402_gain,
+ STRING_ft5402_voltage ,
+ STRING_ft5402_scanselect,
+
+ STRING_ft5402_tx_order,
+ STRING_ft5402_tx_offset,
+ STRING_ft5402_tx_cap,
+
+ STRING_ft5402_rx_order,
+ STRING_ft5402_rx_offset,
+ STRING_ft5402_rx_cap,
+
+ STRING_FT5402_POINTS_STABLE_MACRO,
+ STRING_FT5402_MIN_DELTA_X,
+ STRING_FT5402_MIN_DELTA_Y,
+ STRING_FT5402_MIN_DELTA_STEP,
+
+ STRING_FT5402_ESD_NOISE_MACRO,
+ STRING_FT5402_ESD_DIFF_VAL,
+ STRING_FT5402_ESD_NEGTIVE,
+ STRING_FT5402_ESD_FILTER_FRAME,
+
+ STRING_FT5402_IO_LEVEL_SELECT,
+
+ STRING_FT5402_POINTID_DELAY_COUNT,
+
+ STRING_FT5402_LIFTUP_FILTER_MACRO,
+
+ STRING_FT5402_DIFFDATA_HANDLE,
+ STRING_FT5402_MIN_WATER_VAL,
+ STRING_FT5402_MAX_NOISE_VAL,
+ STRING_FT5402_WATER_HANDLE_START_RX,
+ STRING_FT5402_WATER_HANDLE_START_TX,
+
+ STRING_FT5402_HOST_NUMBER_SUPPORTED,
+ STRING_FT5402_RV_G_RAISE_THGROUP,
+ STRING_FT5402_RV_G_CHARGER_STATE,
+
+ STRING_FT5402_RV_G_FILTERID_START,
+
+ STRING_FT5402_FRAME_FILTER_EN,
+ STRING_FT5402_FRAME_FILTER_SUB_MAX_TH,
+ STRING_FT5402_FRAME_FILTER_ADD_MAX_TH,
+ STRING_FT5402_FRAME_FILTER_SKIP_START_FRAME,
+ STRING_FT5402_FRAME_FILTER_BAND_EN,
+ STRING_FT5402_FRAME_FILTER_BAND_WIDTH,
+
+};
+
+#define FT5402_APP_NAME "FT5X02_param"
+
+#define FT5402_APP_LEGAL "Legal_File"
+#define FT5402_APP_LEGAL_BYTE_1_STR "BYTE_1"
+#define FT5402_APP_LEGAL_BYTE_2_STR "BYTE_2"
+
+#define FT5402_APP_LEGAL_BYTE_1_VALUE 107
+#define FT5402_APP_LEGAL_BYTE_2_VALUE 201
+
+
+#define FT5402_INI_FILEPATH "/system/etc/firmware/"
+
+#endif
diff --git a/drivers/input/touchscreen/ft6x0x/ft5x0x.c b/drivers/input/touchscreen/ft6x0x/ft5x0x.c
new file mode 100755
index 00000000..a3f6c4c9
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft5x0x.c
@@ -0,0 +1,896 @@
+/*
+ * drivers/input/touchscreen/ft5x0x/ft5x0x.c
+ *
+ * FocalTech ft5x0x TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * note: only support mulititouch Wenfs 2010-10-01
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/slab.h>
+#include "ft5x0x.h"
+
+struct ft5x0x_data *pContext=NULL;
+static struct i2c_client *l_client=NULL;
+
+#ifdef TOUCH_KEY
+static int keycodes[NUM_KEYS] ={
+ KEY_MENU,
+ KEY_HOME,
+ KEY_BACK,
+ KEY_SEARCH
+};
+#endif
+
+#define FT5402_CONFIG_NAME "fttpconfig_5402public.ini"
+extern int ft5x0x_read_fw_ver(void);
+extern int ft5x0x_auto_clb(void);
+extern int ft5x0x_upg_fw_bin(struct ft5x0x_data *ft5x0x, int check_ver);
+extern int ft5402_Get_Param_From_Ini(char *config_name);
+extern int ft5402_Init_IC_Param(struct i2c_client *client);
+extern int ft5402_get_ic_param(struct i2c_client *client);
+extern int ft5402_read_reg(struct i2c_client * client, u8 regaddr, u8 * regvalue);
+static unsigned char ft5x0x_debug = 0;
+
+extern int fts_ctpm_fw_upgrade_with_i_file(struct i2c_client * client);
+int ft5x0x_i2c_rxdata(char *rxdata, int length)
+{
+ int ret;
+ struct i2c_msg msg[2];
+
+ msg[0].addr = pContext->addr;
+ msg[0].flags = 0 | I2C_M_NOSTART;
+ msg[0].len = 1;
+ msg[0].buf = rxdata;
+
+ msg[1].addr = pContext->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = length;
+ msg[1].buf = rxdata;
+
+ ret = i2c_transfer(pContext->client->adapter, msg, 2);
+ if (ret <= 0)
+ dbg_err("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+
+int ft5x0x_i2c_txdata(char *txdata, int length)
+{
+ int ret;
+ struct i2c_msg msg[1];
+
+ msg[0].addr = pContext->addr;
+ msg[0].flags = 0;
+ msg[0].len = length;
+ msg[0].buf = txdata;
+
+ ret = i2c_transfer(pContext->client->adapter, msg, 1);
+ if (ret <= 0)
+ dbg_err("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+static void ft5x0x_penup(struct ft5x0x_data *ft5x0x)
+{
+ input_mt_sync(ft5x0x->input_dev);
+ input_sync(ft5x0x->input_dev);
+#ifdef TOUCH_KEY
+ if(ft5x0x->tskey_used && ft5x0x->tkey_pressed && ft5x0x->tkey_idx < NUM_KEYS ){
+ input_report_key(ft5x0x->input_dev, keycodes[ft5x0x->tkey_idx], 1);
+ input_sync(ft5x0x->input_dev);
+ input_report_key(ft5x0x->input_dev, keycodes[ft5x0x->tkey_idx], 0);
+ input_sync(ft5x0x->input_dev);
+ dbg("report as key event %d \n",ft5x0x->tkey_idx);
+ }
+#endif
+
+ dbg("pen up\n");
+ return;
+}
+
+#ifdef TOUCH_KEY
+static int ft5x0x_read_tskey(struct ft5x0x_data *ft5x0x,int x,int y)
+{
+ int px,py;
+
+ if(ft5x0x->tkey.axis){
+ px = y;
+ py = x;
+ }else{
+ px = x;
+ py = y;
+ }
+
+ if(px >= ft5x0x->tkey.x_lower && px<=ft5x0x->tkey.x_upper){
+ ft5x0x->tkey_pressed = 1;
+ if(py>= ft5x0x->tkey.ypos[0].y_lower && py<= ft5x0x->tkey.ypos[0].y_upper){
+ ft5x0x->tkey_idx= 0;
+ }else if(py>= ft5x0x->tkey.ypos[1].y_lower && py<= ft5x0x->tkey.ypos[1].y_upper){
+ ft5x0x->tkey_idx = 1;
+ }else if(py>= ft5x0x->tkey.ypos[2].y_lower && py<= ft5x0x->tkey.ypos[2].y_upper){
+ ft5x0x->tkey_idx = 2;
+ }else if(py>= ft5x0x->tkey.ypos[3].y_lower && py<= ft5x0x->tkey.ypos[3].y_upper){
+ ft5x0x->tkey_idx = 3;
+ }else{
+ ft5x0x->tkey_idx = NUM_KEYS;
+ }
+
+ return 1;
+ }
+
+ ft5x0x->tkey_pressed = 0;
+ return 0;
+}
+#endif
+
+static int ft5x0x_read_data(struct ft5x0x_data *ft5x0x)
+{
+ int ret = -1;
+ int i = 0;
+ u16 x,y,px,py;
+ u8 buf[64] = {0}, id;
+ struct ts_event *event = &ft5x0x->event;
+
+ if(ft5x0x->nt == 10)
+ ret = ft5x0x_i2c_rxdata(buf, 64);
+ else if(ft5x0x->nt == 5)
+ ret = ft5x0x_i2c_rxdata(buf, 31);
+
+ if (ret <= 0) {
+ dbg_err("read_data i2c_rxdata failed: %d\n", ret);
+ return ret;
+ }
+
+ memset(event, 0, sizeof(struct ts_event));
+ //event->tpoint = buf[2] & 0x03;// 0000 0011
+ //event->tpoint = buf[2] & 0x07;// 000 0111
+ event->tpoint = buf[2]&0x0F;
+ if (event->tpoint == 0) {
+ ft5x0x_penup(ft5x0x);
+ return 1;
+ }
+
+ if (event->tpoint > ft5x0x->nt){
+ dbg_err("tounch pointnum=%d > max:%d\n", event->tpoint,ft5x0x->nt);
+ return -1;
+ }
+
+ for (i = 0; i < event->tpoint; i++){
+ id = (buf[5+i*6] >>4) & 0x0F;//get track id
+ if(ft5x0x->swap){
+ px = (buf[3+i*6] & 0x0F)<<8 |buf[4+i*6];
+ py = (buf[5+i*6] & 0x0F)<<8 |buf[6+i*6];
+ }else{
+ px = (buf[5+i*6] & 0x0F)<<8 |buf[6+i*6];
+ py = (buf[3+i*6] & 0x0F)<<8 |buf[4+i*6];
+ }
+
+ x = px;
+ y = py;
+
+ if(ft5x0x->xch)
+ x = ft5x0x->reslx - px;
+
+ if(ft5x0x->ych)
+ y = ft5x0x->resly - py;
+
+ if(ft5x0x->dbg) printk("F%d: Tid=%d,px=%d,py=%d; x=%d,y=%d\n", i, id, px, py, x, y);
+
+#ifdef TOUCH_KEY
+ if(ft5x0x->tskey_used && event->tpoint==1) {
+ if(ft5x0x_read_tskey(ft5x0x,px,py) > 0) return -1;
+ }
+#endif
+ event->x[i] = x;
+ event->y[i] = y;
+ event->tid[i] = id;
+
+ }
+
+ return 0;
+}
+
+static void ft5x0x_report(struct ft5x0x_data *ft5x0x)
+{
+ int i = 0;
+ struct ts_event *event = &ft5x0x->event;
+
+ for (i = 0; i < event->tpoint; i++){
+ input_report_abs(ft5x0x->input_dev, ABS_MT_TRACKING_ID, event->tid[i]);
+ input_report_abs(ft5x0x->input_dev, ABS_MT_POSITION_X, event->x[i]);
+ input_report_abs(ft5x0x->input_dev, ABS_MT_POSITION_Y, event->y[i]);
+ input_mt_sync(ft5x0x->input_dev);
+ }
+ input_sync(ft5x0x->input_dev);
+
+ return;
+}
+
+static void ft5x0x_read_work(struct work_struct *work)
+{
+ int ret = -1;
+ struct ft5x0x_data *ft5x0x = container_of(work, struct ft5x0x_data, read_work);
+
+ mutex_lock(&ft5x0x->ts_mutex);
+ ret = ft5x0x_read_data(ft5x0x);
+
+ if (ret == 0) ft5x0x_report(ft5x0x);
+
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+ mutex_unlock(&ft5x0x->ts_mutex);
+
+ return;
+}
+
+static irqreturn_t ft5x0x_interrupt(int irq, void *dev)
+{
+ struct ft5x0x_data *ft5x0x = dev;
+
+ if (gpio_irqstatus(ft5x0x->irqgpio))
+ {
+
+ if(ft5x0x_debug == 1)
+ {
+ printk("++++++++++++%s\n",__func__);
+ }
+ wmt_gpio_ack_irq(ft5x0x->irqgpio);
+ if (is_gpio_irqenable(ft5x0x->irqgpio))
+ {
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if(!ft5x0x->earlysus) queue_work(ft5x0x->workqueue, &ft5x0x->read_work);
+#else
+ queue_work(ft5x0x->workqueue, &ft5x0x->read_work);
+#endif
+
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static void ft5x0x_reset(struct ft5x0x_data *ft5x0x)
+{
+ gpio_set_value(ft5x0x->rstgpio, 1);
+ mdelay(5);
+ gpio_set_value(ft5x0x->rstgpio, 0);
+ mdelay(20);
+ gpio_set_value(ft5x0x->rstgpio, 1);
+ mdelay(5);
+
+ return;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ft5x0x_early_suspend(struct early_suspend *handler)
+{
+ struct ft5x0x_data *ft5x0x = container_of(handler, struct ft5x0x_data, early_suspend);
+ ft5x0x->earlysus = 1;
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+ return;
+}
+
+static void ft5x0x_late_resume(struct early_suspend *handler)
+{
+ struct ft5x0x_data *ft5x0x = container_of(handler, struct ft5x0x_data, early_suspend);
+
+ ft5x0x_reset(ft5x0x);
+ ft5x0x->earlysus = 0;
+
+ wmt_gpio_set_irq_type(ft5x0x->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+
+ return;
+}
+#endif //CONFIG_HAS_EARLYSUSPEND
+
+#ifdef CONFIG_PM
+static int ft5x0x_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ft5x0x_data *ft5x0x = dev_get_drvdata(&pdev->dev);
+ ft5x0x->earlysus = 1;
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+ return 0;
+}
+
+static int ft5x0x_resume(struct platform_device *pdev)
+{
+ struct ft5x0x_data *ft5x0x = dev_get_drvdata(&pdev->dev);
+ ft5x0x_reset(ft5x0x);
+ ft5x0x->earlysus = 0;
+
+ if (ft5x0x->load_cfg) {
+ msleep(350);
+ ft5402_Init_IC_Param(ft5x0x->client);
+ //msleep(50);
+ ft5402_get_ic_param(ft5x0x->client);
+ }
+ wmt_gpio_set_irq_type(ft5x0x->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+ return 0;
+}
+
+#else
+#define ft5x0x_suspend NULL
+#define ft5x0x_resume NULL
+#endif
+
+static ssize_t cat_dbg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "dbg \n");
+}
+
+static ssize_t echo_dbg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int dbg = simple_strtoul(buf, NULL, 10);
+ struct ft5x0x_data *ft5x0x = pContext;
+
+ if(dbg){
+ ft5x0x->dbg = 1;
+ }else{
+ ft5x0x->dbg = 0;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(dbg, S_IRUGO | S_IWUSR, cat_dbg, echo_dbg);
+
+static ssize_t cat_clb(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "calibrate \n");
+}
+
+static ssize_t echo_clb(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int cal = simple_strtoul(buf, NULL, 10);
+
+ if(cal){
+ if(ft5x0x_auto_clb()) printk("Calibrate Failed.\n");
+ }else{
+ printk("calibrate --echo 1 >clb.\n");
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(clb, S_IRUGO | S_IWUSR, cat_clb, echo_clb);
+
+static ssize_t cat_fupg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "fupg \n");
+}
+
+static ssize_t echo_fupg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ft5x0x_data *ft5x0x = pContext;
+ unsigned int upg = simple_strtoul(buf, NULL, 10);
+
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+ if(upg){
+ if(ft5x0x_upg_fw_bin(ft5x0x, 0)) printk("Upgrade Failed.\n");
+ }else{
+ printk("upgrade --echo 1 > fupg.\n");
+ }
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+
+ return count;
+}
+static DEVICE_ATTR(fupg, S_IRUGO | S_IWUSR, cat_fupg, echo_fupg);
+
+
+static ssize_t cat_fver(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int fw_ver = ft5x0x_read_fw_ver();
+ return sprintf(buf, "firmware version:0x%02x \n",fw_ver);
+}
+
+static ssize_t echo_fver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ return count;
+}
+static DEVICE_ATTR(fver, S_IRUGO | S_IWUSR, cat_fver, echo_fver);
+
+static ssize_t cat_addr(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret;
+ u8 addrs[32];
+ int cnt=0;
+ struct i2c_msg msg[2];
+ struct ft5x0x_data *ft5x0x = pContext;
+ u8 ver[1]= {0xa6};
+
+ ft5x0x->addr = 1;
+
+ msg[0].addr = ft5x0x->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = ver;
+
+ msg[1].addr = ft5x0x->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = ver;
+
+ while(ft5x0x->addr < 0x80){
+ ret = i2c_transfer(ft5x0x->client->adapter, msg, 2);
+ if(ret == 2) sprintf(&addrs[5*cnt++], " 0x%02x",ft5x0x->addr);
+
+ ft5x0x->addr++;
+ msg[0].addr = msg[1].addr = ft5x0x->addr;
+ }
+
+ return sprintf(buf, "i2c addr:%s\n",addrs);
+}
+
+static ssize_t echo_addr(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int addr;
+ struct ft5x0x_data *ft5x0x = pContext;
+
+ sscanf(buf,"%x", &addr);
+ ft5x0x->addr = addr;
+
+ return count;
+}
+static DEVICE_ATTR(addr, S_IRUGO | S_IWUSR, cat_addr, echo_addr);
+
+static struct attribute *ft5x0x_attributes[] = {
+ &dev_attr_clb.attr,
+ &dev_attr_fupg.attr,
+ &dev_attr_fver.attr,
+ &dev_attr_dbg.attr,
+ &dev_attr_addr.attr,
+ NULL
+};
+
+static const struct attribute_group ft5x0x_group = {
+ .attrs = ft5x0x_attributes,
+};
+
+static int ft5x0x_sysfs_create_group(struct ft5x0x_data *ft5x0x, const struct attribute_group *group)
+{
+ int err;
+
+ ft5x0x->kobj = kobject_create_and_add("wmtts", NULL) ;
+ if(!ft5x0x->kobj){
+ dbg_err("kobj create failed.\n");
+ return -ENOMEM;
+ }
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(ft5x0x->kobj, group);
+ if (err < 0){
+ kobject_del(ft5x0x->kobj);
+ dbg_err("Create sysfs group failed!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void ft5x0x_sysfs_remove_group(struct ft5x0x_data *ft5x0x, const struct attribute_group *group)
+{
+ sysfs_remove_group(ft5x0x->kobj, group);
+ kobject_del(ft5x0x->kobj);
+ return;
+}
+
+static int ft5x0x_probe(struct platform_device *pdev)
+{
+ int i,err = 0;
+ u8 value = 0;
+ u8 cfg_name[32];
+ struct ft5x0x_data *ft5x0x = platform_get_drvdata( pdev);
+
+ ft5x0x->client = l_client;
+ INIT_WORK(&ft5x0x->read_work, ft5x0x_read_work);
+ mutex_init(&ft5x0x->ts_mutex);
+
+ ft5x0x->workqueue = create_singlethread_workqueue(ft5x0x->name);
+ if (!ft5x0x->workqueue) {
+ err = -ESRCH;
+ goto exit_create_singlethread;
+ }
+
+ err = ft5x0x_sysfs_create_group(ft5x0x, &ft5x0x_group);
+ if(err < 0){
+ dbg("create sysfs group failed.\n");
+ goto exit_create_group;
+ }
+
+ ft5x0x->input_dev = input_allocate_device();
+ if (!ft5x0x->input_dev) {
+ err = -ENOMEM;
+ dbg("failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ ft5x0x->input_dev->name = ft5x0x->name;
+ ft5x0x->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(INPUT_PROP_DIRECT, ft5x0x->input_dev->propbit);
+
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_POSITION_X, 0, ft5x0x->reslx, 0, 0);
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_POSITION_Y, 0, ft5x0x->resly, 0, 0);
+ input_set_abs_params(ft5x0x->input_dev,
+ ABS_MT_TRACKING_ID, 0, 20, 0, 0);
+#ifdef TOUCH_KEY
+ if(ft5x0x->tskey_used){
+ for (i = 0; i <NUM_KEYS; i++)
+ set_bit(keycodes[i], ft5x0x->input_dev->keybit);
+
+ ft5x0x->input_dev->keycode = keycodes;
+ ft5x0x->input_dev->keycodesize = sizeof(unsigned int);
+ ft5x0x->input_dev->keycodemax = NUM_KEYS;
+ }
+#endif
+
+ err = input_register_device(ft5x0x->input_dev);
+ if (err) {
+ dbg_err("ft5x0x_ts_probe: failed to register input device.\n");
+ goto exit_input_register_device_failed;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ft5x0x->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ft5x0x->early_suspend.suspend = ft5x0x_early_suspend;
+ ft5x0x->early_suspend.resume = ft5x0x_late_resume;
+ register_early_suspend(&ft5x0x->early_suspend);
+#endif
+
+ if(ft5x0x->upg){
+ if (fts_ctpm_fw_upgrade_with_i_file(ft5x0x->client) < 0) {
+ printk("ft5x0x_probe -----upgrade failed!-\n");
+ }
+ else
+ {
+ printk("ft5x0x_probe -----upgrade successful!-\n");
+ wmt_setsyspara("wmt.io.ts.upg","");
+ ft5x0x_reset(ft5x0x);
+ }
+ ft5x0x->upg = 0x00;
+ }
+
+ if(request_irq(ft5x0x->irq, ft5x0x_interrupt, IRQF_SHARED, ft5x0x->name, ft5x0x) < 0){
+ dbg_err("Could not allocate irq for ts_ft5x0x !\n");
+ err = -1;
+ goto exit_register_irq;
+ }
+
+ { // check if need to load config to IC or not
+ err = ft5402_read_reg(ft5x0x->client, 0xa3, &value);
+ if (err < 0)
+ dbg_err("Read reg 0xa3 failed.\n");
+ else
+ printk("0xa3 reg = %d\n", value);
+ if (value == 3)
+ ft5x0x->load_cfg = 1;
+ else
+ ft5x0x->load_cfg = 0;
+ }
+ ft5x0x_reset(ft5x0x);
+
+ if (ft5x0x->load_cfg) {
+ msleep(350); /*make sure CTP already finish startup process*/
+ sprintf(cfg_name, "%s.ini", ft5x0x->cfg_name);
+ printk("Config file name: %s\n", cfg_name);
+ if (ft5402_Get_Param_From_Ini(cfg_name) >= 0)
+ ft5402_Init_IC_Param(ft5x0x->client);
+ else
+ dbg_err("[FTS]-------Get ft5402 param from INI file failed\n");
+ ft5402_get_ic_param(ft5x0x->client);
+ }
+
+ wmt_gpio_set_irq_type(ft5x0x->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ft5x0x->irqgpio);
+
+
+ return 0;
+
+exit_register_irq:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ft5x0x->early_suspend);
+#endif
+exit_input_register_device_failed:
+ input_free_device(ft5x0x->input_dev);
+exit_input_dev_alloc_failed:
+ ft5x0x_sysfs_remove_group(ft5x0x, &ft5x0x_group);
+exit_create_group:
+ cancel_work_sync(&ft5x0x->read_work);
+ destroy_workqueue(ft5x0x->workqueue);
+exit_create_singlethread:
+ return err;
+}
+
+static int ft5x0x_remove(struct platform_device *pdev)
+{
+ struct ft5x0x_data *ft5x0x = platform_get_drvdata( pdev);
+
+ cancel_work_sync(&ft5x0x->read_work);
+ flush_workqueue(ft5x0x->workqueue);
+ destroy_workqueue(ft5x0x->workqueue);
+
+ free_irq(ft5x0x->irq, ft5x0x);
+ wmt_gpio_mask_irq(ft5x0x->irqgpio);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ft5x0x->early_suspend);
+#endif
+ input_unregister_device(ft5x0x->input_dev);
+
+ ft5x0x_sysfs_remove_group(ft5x0x, &ft5x0x_group);
+
+ mutex_destroy(&ft5x0x->ts_mutex);
+ dbg("remove...\n");
+ return 0;
+}
+
+static void ft5x0x_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device ft5x0x_device = {
+ .name = DEV_FT5X0X,
+ .id = 0,
+ .dev = {.release = ft5x0x_release},
+};
+
+static struct platform_driver ft5x0x_driver = {
+ .driver = {
+ .name = DEV_FT5X0X,
+ .owner = THIS_MODULE,
+ },
+ .probe = ft5x0x_probe,
+ .remove = ft5x0x_remove,
+ .suspend = ft5x0x_suspend,
+ .resume = ft5x0x_resume,
+};
+
+static int check_touch_env(struct ft5x0x_data *ft5x0x)
+{
+ int i,ret = 0;
+ int len = 96;
+ int Enable;
+ char retval[96] = {0};
+ char *p=NULL;
+ char *s=NULL;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ //printk("MST FT5x0x:Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ sscanf(retval,"%d:",&Enable);
+ //check touch enable
+ if(Enable == 0){
+ //printk("FT5x0x Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(retval,':');
+ p++;
+ if(strncmp(p,"ft5301",6)==0){//check touch ID
+ ft5x0x->id = FT5301;
+ ft5x0x->name = DEV_FT5301;
+ }else if(strncmp(p,"ft5406",6)==0){
+ ft5x0x->id = FT5406;
+ ft5x0x->name = DEV_FT5406;
+ }else if(strncmp(p,"ft6336",6)==0){
+ ft5x0x->id = FT6336;
+ ft5x0x->name = DEV_FT6336;
+ }else if(strncmp(p,"ft5206",6)==0){
+ ft5x0x->id = FT5206;
+ ft5x0x->name = DEV_FT5206;
+ }else if(strncmp(p,"ft5606",6)==0){
+ ft5x0x->id = FT5606;
+ ft5x0x->name = DEV_FT5606;
+ }else if(strncmp(p,"ft5306",6)==0){
+ ft5x0x->id = FT5306;
+ ft5x0x->name = DEV_FT5306;
+ }else if(strncmp(p,"ft5302",6)==0){
+ ft5x0x->id = FT5302;
+ ft5x0x->name = DEV_FT5302;
+ }else if(strncmp(p,"ft5",3)==0)
+ {
+ ft5x0x->id = FT5X0X;
+ ft5x0x->name = DEV_FT5X0X;
+ }else{
+ printk("FT5x0x touch disabled.\n");
+ return -ENODEV;
+ }
+
+ s = strchr(p,':');
+ strncpy(ft5x0x->cfg_name, p, s-p);
+
+ p = s + 1;
+ sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%x", &ft5x0x->irqgpio, &ft5x0x->reslx, &ft5x0x->resly, &ft5x0x->rstgpio, &ft5x0x->swap, &ft5x0x->xch, &ft5x0x->ych, &ft5x0x->nt, &ft5x0x->addr);
+
+ ft5x0x->irq = IRQ_GPIO;
+ printk("%s irqgpio=%d, reslx=%d, resly=%d, rstgpio=%d, swap=%d, xch=%d, ych=%d, nt=%d, addr=%x\n", ft5x0x->name, ft5x0x->irqgpio, ft5x0x->reslx, ft5x0x->resly, ft5x0x->rstgpio, ft5x0x->swap, ft5x0x->xch, ft5x0x->ych, ft5x0x->nt, ft5x0x->addr);
+
+ memset(retval,0x00,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.ts.upg", retval, &len);
+ if(!ret){
+ ft5x0x->upg = 1;
+ strncpy(ft5x0x->fw_name, retval, sizeof(ft5x0x->fw_name));
+ }
+
+#ifdef TOUCH_KEY
+ memset(retval,0x00,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.tskey", retval, &len);
+ if(!ret){
+ sscanf(retval,"%d:", &ft5x0x->nkeys);
+ p = strchr(retval,':');
+ p++;
+ for(i=0; i < ft5x0x->nkeys; i++ ){
+ sscanf(p,"%d:%d", &ft5x0x->tkey.ypos[i].y_lower, &ft5x0x->tkey.ypos[i].y_upper);
+ p = strchr(p,':');
+ p++;
+ p = strchr(p,':');
+ p++;
+ }
+ sscanf(p,"%d:%d:%d", &ft5x0x->tkey.axis, &ft5x0x->tkey.x_lower, &ft5x0x->tkey.x_upper);
+ ft5x0x->tskey_used = 1;
+ }
+#endif
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = TS_I2C_NAME,
+ .flags = 0x00,
+ .addr = FT5406_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+
+ //ts_i2c_board_info.addr = FT5406_I2C_ADDR;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(FT5X0X_I2C_BUS);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+static int __init ft5x0x_init(void)
+{
+ int ret = -ENOMEM;
+ struct ft5x0x_data *ft5x0x=NULL;
+
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ ft5x0x = kzalloc(sizeof(struct ft5x0x_data), GFP_KERNEL);
+ if(!ft5x0x){
+ dbg_err("mem alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ pContext = ft5x0x;
+ ret = check_touch_env(ft5x0x);
+ if(ret < 0)
+ goto exit_free_mem;
+
+ ret = gpio_request(ft5x0x->irqgpio, "ts_irq");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen irq request fail\n", ft5x0x->irqgpio);
+ goto exit_free_mem;
+ }
+ wmt_gpio_setpull(ft5x0x->irqgpio, WMT_GPIO_PULL_UP);
+ gpio_direction_input(ft5x0x->irqgpio);
+
+ ret = gpio_request(ft5x0x->rstgpio, "ts_rst");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen reset request fail\n", ft5x0x->rstgpio);
+ goto exit_free_irqgpio;
+ }
+ gpio_direction_output(ft5x0x->rstgpio, 1);
+
+
+ ret = platform_device_register(&ft5x0x_device);
+ if(ret){
+ dbg_err("register platform drivver failed!\n");
+ goto exit_free_gpio;
+ }
+ platform_set_drvdata(&ft5x0x_device, ft5x0x);
+
+ ret = platform_driver_register(&ft5x0x_driver);
+ if(ret){
+ dbg_err("register platform device failed!\n");
+ goto exit_unregister_pdev;
+ }
+
+ return ret;
+
+exit_unregister_pdev:
+ platform_device_unregister(&ft5x0x_device);
+exit_free_gpio:
+ gpio_free(ft5x0x->rstgpio);
+exit_free_irqgpio:
+ gpio_free(ft5x0x->irqgpio);
+exit_free_mem:
+ kfree(ft5x0x);
+ pContext = NULL;
+ return ret;
+}
+
+static void ft5x0x_exit(void)
+{
+ if(!pContext) return;
+
+ gpio_free(pContext->rstgpio);
+ gpio_free(pContext->irqgpio);
+ platform_driver_unregister(&ft5x0x_driver);
+ platform_device_unregister(&ft5x0x_device);
+ kfree(pContext);
+ ts_i2c_unregister_device();
+ return;
+}
+
+late_initcall(ft5x0x_init);
+module_exit(ft5x0x_exit);
+module_param (ft5x0x_debug, byte, 0644);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("FocalTech.Touch");
diff --git a/drivers/input/touchscreen/ft6x0x/ft5x0x.h b/drivers/input/touchscreen/ft6x0x/ft5x0x.h
new file mode 100755
index 00000000..417af1f5
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft5x0x.h
@@ -0,0 +1,205 @@
+#ifndef __LINUX_FT5X0X_TS_H__
+#define __LINUX_FT5X0X_TS_H__
+
+#define DEV_FT5206 "touch_ft5206"
+#define DEV_FT5301 "touch_ft5301"
+#define DEV_FT5302 "touch_ft5302"
+#define DEV_FT5306 "touch_ft5306"
+#define DEV_FT5406 "touch_ft5406"
+#define DEV_FT5606 "touch_ft5606"
+#define DEV_FT6336 "touch_ft6336"
+
+
+#define DEV_FT5X0X "touch_ft5x0x"
+#define TS_I2C_NAME "ft5x0x-ts"
+#define FT5406_I2C_ADDR 0x38
+#define FT5X0X_I2C_BUS 0x01
+
+enum FT5X0X_ID{
+ FT5206 =1,
+ FT5301,
+ FT5302,
+ FT5306,
+ FT5406,
+ FT5606,
+ FT6336,
+ FT5X0X,
+};
+
+struct vt1603_ts_cal_info {
+ int a1;
+ int b1;
+ int c1;
+ int a2;
+ int b2;
+ int c2;
+ int delta;
+};
+
+#define SUPPORT_POINT_NUM 5//10
+struct ts_event {
+ int x[SUPPORT_POINT_NUM];
+ int y[SUPPORT_POINT_NUM];
+ int tid[SUPPORT_POINT_NUM];
+ int tpoint;
+};
+
+#define TOUCH_KEY
+
+#ifdef TOUCH_KEY
+#define NUM_KEYS 4
+struct key_pos{
+ int y_lower;
+ int y_upper;
+};
+
+struct ts_key{
+ int axis;
+ int x_lower;
+ int x_upper;
+ struct key_pos ypos[NUM_KEYS];
+};
+#endif
+
+struct ft5x0x_data {
+ int id;
+ unsigned int addr;
+ const char *name;
+ u8 fw_name[64];
+ u8 cfg_name[32];
+
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct ts_event event;
+ struct work_struct read_work;
+ struct workqueue_struct *workqueue;
+ struct mutex ts_mutex;
+ struct kobject *kobj;
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ int earlysus;
+
+ int reslx;
+ int resly;
+
+ int tw;
+ int th;
+
+ int irq;
+ int irqgpio;
+ int rstgpio;
+/*
+ int igp_idx;
+ int igp_bit;
+
+ int rgp_idx;
+ int rgp_bit;
+*/
+
+ int nt;
+ int nb;
+ int xch;
+ int ych;
+ int swap;
+
+ int upg;
+ int load_cfg;
+ int dbg;
+#ifdef TOUCH_KEY
+ int tskey_used;
+ int tkey_pressed;
+ int nkeys;
+ int tkey_idx;
+ struct ts_key tkey;
+#endif
+
+};
+
+enum ft5x0x_ts_regs {
+ FT5X0X_REG_THGROUP = 0x80, /* touch threshold, related to sensitivity */
+ FT5X0X_REG_THPEAK = 0x81,
+ FT5X0X_REG_THCAL = 0x82,
+ FT5X0X_REG_THWATER = 0x83,
+ FT5X0X_REG_THTEMP = 0x84,
+ FT5X0X_REG_THDIFF = 0x85,
+ FT5X0X_REG_CTRL = 0x86,
+ FT5X0X_REG_TIMEENTERMONITOR = 0x87,
+ FT5X0X_REG_PERIODACTIVE = 0x88, /* report rate */
+ FT5X0X_REG_PERIODMONITOR = 0x89,
+ FT5X0X_REG_HEIGHT_B = 0x8a,
+ FT5X0X_REG_MAX_FRAME = 0x8b,
+ FT5X0X_REG_DIST_MOVE = 0x8c,
+ FT5X0X_REG_DIST_POINT = 0x8d,
+ FT5X0X_REG_FEG_FRAME = 0x8e,
+ FT5X0X_REG_SINGLE_CLICK_OFFSET = 0x8f,
+ FT5X0X_REG_DOUBLE_CLICK_TIME_MIN = 0x90,
+ FT5X0X_REG_SINGLE_CLICK_TIME = 0x91,
+ FT5X0X_REG_LEFT_RIGHT_OFFSET = 0x92,
+ FT5X0X_REG_UP_DOWN_OFFSET = 0x93,
+ FT5X0X_REG_DISTANCE_LEFT_RIGHT = 0x94,
+ FT5X0X_REG_DISTANCE_UP_DOWN = 0x95,
+ FT5X0X_REG_ZOOM_DIS_SQR = 0x96,
+ FT5X0X_REG_RADIAN_VALUE =0x97,
+ FT5X0X_REG_MAX_X_HIGH = 0x98,
+ FT5X0X_REG_MAX_X_LOW = 0x99,
+ FT5X0X_REG_MAX_Y_HIGH = 0x9a,
+ FT5X0X_REG_MAX_Y_LOW = 0x9b,
+ FT5X0X_REG_K_X_HIGH = 0x9c,
+ FT5X0X_REG_K_X_LOW = 0x9d,
+ FT5X0X_REG_K_Y_HIGH = 0x9e,
+ FT5X0X_REG_K_Y_LOW = 0x9f,
+ FT5X0X_REG_AUTO_CLB_MODE = 0xa0,
+ FT5X0X_REG_LIB_VERSION_H = 0xa1,
+ FT5X0X_REG_LIB_VERSION_L = 0xa2,
+ FT5X0X_REG_CIPHER = 0xa3,
+ FT5X0X_REG_MODE = 0xa4,
+ FT5X0X_REG_PMODE = 0xa5, /* Power Consume Mode */
+ FT5X0X_REG_FIRMID = 0xa6, /* Firmware version */
+ FT5X0X_REG_STATE = 0xa7,
+ FT5X0X_REG_FT5201ID = 0xa8,
+ FT5X0X_REG_ERR = 0xa9,
+ FT5X0X_REG_CLB = 0xaa,
+};
+
+//FT5X0X_REG_PMODE
+#define PMODE_ACTIVE 0x00
+#define PMODE_MONITOR 0x01
+#define PMODE_STANDBY 0x02
+#define PMODE_HIBERNATE 0x03
+
+#define DEV_NAME "wmtts"
+#define DEV_MAJOR 11
+
+#define TS_IOC_MAGIC 't'
+#define TS_IOCTL_CAL_START _IO(TS_IOC_MAGIC, 1)
+#define TS_IOCTL_CAL_DONE _IOW(TS_IOC_MAGIC, 2, int*)
+#define TS_IOCTL_GET_RAWDATA _IOR(TS_IOC_MAGIC, 3, int*)
+#define TS_IOCTL_CAL_QUIT _IOW(TS_IOC_MAGIC, 4, int*)
+#define TS_IOCTL_CAL_CAP _IOW(TS_IOC_MAGIC, 5, int*)
+#define TS_IOC_MAXNR 5
+
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_i2c_xfer_continue_if_4(struct i2c_msg *msg, unsigned int num,int bus_id);
+
+//#define FT_DEBUG
+
+#undef dbg
+#ifdef FT_DEBUG
+ #define dbg(fmt,args...) printk("DBG:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+#else
+ #define dbg(fmt,args...)
+#endif
+
+#undef dbg_err
+#define dbg_err(fmt,args...) printk("ERR:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+
+//#define FTS_DBG
+#ifdef FTS_DBG
+#define DBG(fmt, args...) printk("[FTS]" fmt, ## args)
+#else
+#define DBG(fmt, args...) do{}while(0)
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ft6x0x/ft5x0x_upg.c b/drivers/input/touchscreen/ft6x0x/ft5x0x_upg.c
new file mode 100755
index 00000000..9db72130
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft5x0x_upg.c
@@ -0,0 +1,506 @@
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mount.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/syscalls.h>
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "ft5x0x.h"
+
+typedef enum
+{
+ ERR_OK,
+ ERR_MODE,
+ ERR_READID,
+ ERR_ERASE,
+ ERR_STATUS,
+ ERR_ECC,
+ ERR_DL_ERASE_FAIL,
+ ERR_DL_PROGRAM_FAIL,
+ ERR_DL_VERIFY_FAIL,
+ ERR_FMID
+}E_UPGRADE_ERR_TYPE;
+
+#define FT5X_CTPM_ID_L 0X79
+#define FT5X_CTPM_ID_H 0X03
+
+#define FT56_CTPM_ID_L 0X79
+#define FT56_CTPM_ID_H 0X06
+
+#define FTS_PACKET_LENGTH 128
+
+extern struct ft5x0x_data *pContext;
+extern int ft5x0x_i2c_rxdata(char *rxdata, int length);
+extern int ft5x0x_i2c_txdata(char *txdata, int length);
+
+static int ft5x0x_write_reg(u8 addr, u8 para)
+{
+ u8 buf[2];
+ int ret = -1;
+
+ buf[0] = addr;
+ buf[1] = para;
+ ret = ft5x0x_i2c_txdata(buf, 2);
+ if (ret <= 0) {
+ printk("write reg failed! %x ret: %d", buf[0], ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ft5x0x_read_reg(u8 addr, u8 *pdata)
+{
+ int ret;
+ u8 buf[2];
+ struct i2c_msg msgs[2];
+
+ //
+ buf[0] = addr; //register address
+
+ msgs[0].addr = pContext->addr;
+ msgs[0].flags = 0 | I2C_M_NOSTART;
+ msgs[0].len = 1;
+ msgs[0].buf = buf;
+
+ msgs[1].addr = pContext->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = pdata;
+
+ //ret = wmt_i2c_xfer_continue_if_4(msgs, 2, FT5X0X_I2C_BUS);
+ ret = i2c_transfer(pContext->client->adapter, msgs, 2);
+ if (ret <= 0)
+ printk("msg %s i2c read error: %d\n", __func__, ret);
+
+ return ret;
+
+}
+
+
+/*
+[function]:
+ send a command to ctpm.
+[parameters]:
+ btcmd[in] :command code;
+ btPara1[in] :parameter 1;
+ btPara2[in] :parameter 2;
+ btPara3[in] :parameter 3;
+ num[in] :the valid input parameter numbers, if only command code needed and no parameters followed,then the num is 1;
+[return]:
+ FTS_TRUE :success;
+ FTS_FALSE :io fail;
+*/
+static u8 cmd_write(u8 *cmd,u8 num)
+{
+ return ft5x0x_i2c_txdata(cmd, num);
+}
+
+/*
+[function]:
+ write data to ctpm , the destination address is 0.
+[parameters]:
+ pbt_buf[in] :point to data buffer;
+ bt_len[in] :the data numbers;
+[return]:
+ FTS_TRUE :success;
+ FTS_FALSE :io fail;
+*/
+static u8 byte_write(u8* pbt_buf, int dw_len)
+{
+
+ return ft5x0x_i2c_txdata( pbt_buf, dw_len);
+}
+
+/*
+[function]:
+ read out data from ctpm,the destination address is 0.
+[parameters]:
+ pbt_buf[out] :point to data buffer;
+ bt_len[in] :the data numbers;
+[return]:
+ FTS_TRUE :success;
+ FTS_FALSE :io fail;
+*/
+static u8 byte_read(u8* pbt_buf, u8 bt_len)
+{
+ int ret;
+ struct i2c_msg msg[1];
+
+ msg[0].addr = pContext->addr;
+ msg[0].flags = I2C_M_RD;
+ msg[0].len = bt_len;
+ msg[0].buf = pbt_buf;
+
+ ret = i2c_transfer(pContext->client->adapter, msg, 1);
+ //ret = wmt_i2c_xfer_continue_if_4(msg, 1, FT5X0X_I2C_BUS);
+ if (ret <= 0)
+ printk("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+
+/*
+[function]:
+ burn the FW to ctpm.
+[parameters]:(ref. SPEC)
+ pbt_buf[in] :point to Head+FW ;
+ dw_lenth[in]:the length of the FW + 6(the Head length);
+ bt_ecc[in] :the ECC of the FW
+[return]:
+ ERR_OK :no error;
+ ERR_MODE :fail to switch to UPDATE mode;
+ ERR_READID :read id fail;
+ ERR_ERASE :erase chip fail;
+ ERR_STATUS :status error;
+ ERR_ECC :ecc error.
+*/
+static E_UPGRADE_ERR_TYPE ft5x0x_fw_upgrade(struct ft5x0x_data *ft5x0x, u8* pbt_buf, int dw_lenth)
+{
+ int i = 0,j = 0,i_ret;
+ int packet_number;
+ int temp,lenght;
+ u8 packet_buf[FTS_PACKET_LENGTH + 6];
+ u8 auc_i2c_write_buf[10];
+ u8 reg_val[2] = {0};
+ u8 ctpm_id[2] = {0};
+ u8 cmd[4];
+ u8 bt_ecc;
+
+ /*********Step 1:Reset CTPM *****/
+ /*write 0xaa to register 0xfc*/
+ ft5x0x_write_reg(0xfc,0xaa);
+ msleep(50);
+ /*write 0x55 to register 0xfc*/
+ ft5x0x_write_reg(0xfc,0x55);
+ printk("[FTS] Step 1: Reset CTPM.\n");
+ msleep(30);
+
+ /*********Step 2:Enter upgrade mode *****/
+ auc_i2c_write_buf[0] = 0x55;
+ auc_i2c_write_buf[1] = 0xaa;
+ do{
+ i ++;
+ i_ret = byte_write(auc_i2c_write_buf, 2);
+ mdelay(5);
+ }while(i_ret <= 0 && i < 5 );
+ msleep(20);
+
+ /*********Step 3:check READ-ID**********/
+ if(ft5x0x->id == FT5606){
+ ctpm_id[0] = FT56_CTPM_ID_L;
+ ctpm_id[1] = FT56_CTPM_ID_H;
+ }else{
+ ctpm_id[0] = FT5X_CTPM_ID_L;
+ ctpm_id[1] = FT5X_CTPM_ID_H;
+ }
+
+ cmd[0] = 0x90;
+ cmd[1] = 0x00;
+ cmd[2] = 0x00;
+ cmd[3] = 0x00;
+ cmd_write(cmd,4);
+ byte_read(reg_val,2);
+ if (reg_val[0] == ctpm_id[0] && reg_val[1] == ctpm_id[1]){
+ printk("[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",reg_val[0],reg_val[1]);
+ }else{
+ printk("[FTS] ID_ERROR: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",reg_val[0],reg_val[1]);
+ return ERR_READID;
+ }
+
+ cmd[0] = 0xcd;
+ cmd_write(cmd,1);
+ byte_read(reg_val,1);
+ printk("[FTS] bootloader version = 0x%x\n", reg_val[0]);
+
+ /******Step 4:erase app and panel paramenter area *********/
+ cmd[0] = 0x61;
+ cmd_write(cmd,1); //erase app area
+ msleep(1500);
+ cmd[0] = 0x63;
+ cmd_write(cmd,1); //erase panel parameter area
+ msleep(100);
+ printk("[FTS] Step 4: erase. \n");
+
+ /*********Step 5:write firmware(FW) to ctpm flash*********/
+ bt_ecc = 0;
+ printk("[FTS] Step 5: start upgrade. \n");
+ dw_lenth = dw_lenth - 8;
+ packet_number = (dw_lenth) / FTS_PACKET_LENGTH;
+ packet_buf[0] = 0xbf;
+ packet_buf[1] = 0x00;
+ for (j=0;j<packet_number;j++){
+ temp = j * FTS_PACKET_LENGTH;
+ packet_buf[2] = (u8)(temp>>8);
+ packet_buf[3] = (u8)temp;
+ lenght = FTS_PACKET_LENGTH;
+ packet_buf[4] = (u8)(lenght>>8);
+ packet_buf[5] = (u8)lenght;
+
+ for (i=0;i<FTS_PACKET_LENGTH;i++){
+ packet_buf[6+i] = pbt_buf[j*FTS_PACKET_LENGTH + i];
+ bt_ecc ^= packet_buf[6+i];
+ }
+
+ byte_write(&packet_buf[0],FTS_PACKET_LENGTH + 6);
+ mdelay(FTS_PACKET_LENGTH/6 + 1);
+ if ((j * FTS_PACKET_LENGTH % 1024) == 0){
+ printk("[FTS] upgrade the 0x%x th byte.\n", ((unsigned int)j) * FTS_PACKET_LENGTH);
+ }
+ }
+
+ if ((dw_lenth) % FTS_PACKET_LENGTH > 0){
+ temp = packet_number * FTS_PACKET_LENGTH;
+ packet_buf[2] = (u8)(temp>>8);
+ packet_buf[3] = (u8)temp;
+
+ temp = (dw_lenth) % FTS_PACKET_LENGTH;
+ packet_buf[4] = (u8)(temp>>8);
+ packet_buf[5] = (u8)temp;
+
+ for (i=0;i<temp;i++){
+ packet_buf[6+i] = pbt_buf[ packet_number*FTS_PACKET_LENGTH + i];
+ bt_ecc ^= packet_buf[6+i];
+ }
+
+ byte_write(&packet_buf[0],temp+6);
+ mdelay(20);
+ }
+
+ //send the last six byte
+ for (i = 0; i<6; i++){
+ temp = 0x6ffa + i;
+ packet_buf[2] = (u8)(temp>>8);
+ packet_buf[3] = (u8)temp;
+ temp =1;
+ packet_buf[4] = (u8)(temp>>8);
+ packet_buf[5] = (u8)temp;
+ packet_buf[6] = pbt_buf[ dw_lenth + i];
+ bt_ecc ^= packet_buf[6];
+
+ byte_write(&packet_buf[0],7);
+ mdelay(20);
+ }
+
+ /*********Step 6: read out checksum********************/
+ /*send the opration head*/
+ cmd[0] = 0xcc;
+ cmd_write(cmd,1);
+ byte_read(reg_val,1);
+ printk("[FTS] Step 6:read ECC 0x%x, firmware ECC 0x%x. \n", reg_val[0], bt_ecc);
+ if(reg_val[0] != bt_ecc){
+ return ERR_ECC;
+ }
+
+ /*********Step 7: reset the new FW***********************/
+ cmd[0] = 0x07;
+ cmd_write(cmd,1);
+
+ msleep(300); //make sure CTP startup normally
+
+ return ERR_OK;
+}
+
+int ft5x0x_auto_clb(void)
+{
+ u8 uc_temp;
+ u8 i ;
+
+ printk("[FTS] start auto CLB.\n");
+ msleep(200);
+ ft5x0x_write_reg(0, 0x40);
+ msleep(100); //make sure already enter factory mode
+ ft5x0x_write_reg(2, 0x4); //write command to start calibration
+ msleep(300);
+ for(i=0;i<100;i++){
+ ft5x0x_read_reg(0,&uc_temp);
+ if ( ((uc_temp&0x70)>>4) == 0x0){ //return to normal mode, calibration finish
+ break;
+ }
+ msleep(200);
+ printk("[FTS] waiting calibration %d\n",i);
+ }
+ printk("[FTS] calibration OK.\n");
+
+ msleep(300);
+ ft5x0x_write_reg(0, 0x40); //goto factory mode
+ msleep(100); //make sure already enter factory mode
+ ft5x0x_write_reg(2, 0x5); //store CLB result
+ msleep(300);
+ ft5x0x_write_reg(0, 0x0); //return to normal mode
+ msleep(300);
+ printk("[FTS] store CLB result OK.\n");
+ return 0;
+}
+
+static int ft5x0x_get_bin_ver(const u8 *fw, int fw_szie)
+{
+ if (fw_szie > 2){
+ return fw[fw_szie - 2];
+ }else{
+ return 0xff; //default value
+ }
+ return 0xff;
+}
+
+int ft5x0x_read_fw_ver(void)
+{
+ u8 ver=0;
+ int ret=0;
+
+ ret = ft5x0x_read_reg(FT5X0X_REG_FIRMID, &ver);
+ if(ret > 0)
+ return ver;
+
+ return ret;
+}
+
+
+static int ft5x0x_get_fw_szie(const char *fw_name)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize = 0;
+
+ if(fw_name == NULL){
+ dbg_err("Firmware name error.\n");
+ return -EFAULT;
+ }
+
+ if (NULL == pfile)
+ pfile = filp_open(fw_name, O_RDONLY, 0);
+
+ if (IS_ERR(pfile)) {
+ dbg_err("File open error: %s.\n", fw_name);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ filp_close(pfile, NULL);
+ return fsize;
+}
+
+static int ft5x0x_read_fw(const char *fw_name, u8 *buf)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize;
+ loff_t pos;
+ mm_segment_t fs;
+
+ if(fw_name == NULL){
+ dbg_err("Firmware name error.\n");
+ return -EFAULT;
+ }
+
+ if (NULL == pfile)
+ pfile = filp_open(fw_name, O_RDONLY, 0);
+ if (IS_ERR(pfile)) {
+ dbg_err("File open error: %s.\n", fw_name);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ pos = 0;
+ vfs_read(pfile, buf, fsize, &pos);
+ filp_close(pfile, NULL);
+ set_fs(fs);
+
+ return 0;
+}
+
+#define FW_SUFFFIX ".bin"
+#define SD_UPG_BIN_PATH "/sdcard/_wmt_ft5x0x_fw_app.bin"
+#define FS_UPG_BIN_PATH "/lib/firmware/"
+
+int ft5x0x_upg_fw_bin(struct ft5x0x_data *ft5x0x, int check_ver)
+{
+ int i_ret = 0;
+ int fwsize = 0;
+ int hw_fw_ver;
+ int bin_fw_ver;
+ int do_upg;
+ u8 *pbt_buf = NULL;
+ u8 fw_path[128] = {0};
+
+ if(ft5x0x->upg)
+ sprintf(fw_path,"%s%s%s", FS_UPG_BIN_PATH, ft5x0x->fw_name,FW_SUFFFIX);//get fw binary file from filesystem
+ else
+ strcpy(fw_path,SD_UPG_BIN_PATH); //get fw binary file from SD card
+
+ fwsize = ft5x0x_get_fw_szie(fw_path);
+ if (fwsize <= 0) {
+ dbg_err("Get firmware size failed\n");
+ return -EIO;
+ }
+
+ if (fwsize < 8 || fwsize > 32 * 1024) {
+ dbg_err("FW length error\n");
+ return -EIO;
+ }
+
+ pbt_buf = kmalloc(fwsize + 1, GFP_KERNEL);
+ if (ft5x0x_read_fw(fw_path, pbt_buf)) {
+ dbg_err("Request_firmware failed\n");
+ i_ret = -EIO;
+ goto exit;
+ }
+
+ hw_fw_ver =ft5x0x_read_fw_ver();
+ if(hw_fw_ver <= 0){
+ dbg_err("Read firmware version failed\n");
+ i_ret = hw_fw_ver;
+ goto exit;
+ }
+
+ bin_fw_ver = ft5x0x_get_bin_ver(pbt_buf, fwsize);
+ printk("[FTS] hardware fw ver 0x%0x, binary ver 0x%0x\n",hw_fw_ver, bin_fw_ver);
+
+ if(check_ver){
+ if(hw_fw_ver == 0xa6 || hw_fw_ver < bin_fw_ver)
+ do_upg = 1;
+ else
+ do_upg = 0;
+ }else{
+ do_upg = 1;
+ }
+
+ if(do_upg){
+ if ((pbt_buf[fwsize - 8] ^ pbt_buf[fwsize - 6]) == 0xFF &&
+ (pbt_buf[fwsize - 7] ^ pbt_buf[fwsize - 5]) == 0xFF &&
+ (pbt_buf[fwsize - 3] ^ pbt_buf[fwsize - 4]) == 0xFF) {
+ i_ret = ft5x0x_fw_upgrade(ft5x0x, pbt_buf, fwsize);
+ if (i_ret)
+ dbg_err("Upgrade failed, i_ret=%d\n",i_ret);
+ else {
+ hw_fw_ver = ft5x0x_read_fw_ver();
+ printk("[FTS] upgrade to new version 0x%x\n", hw_fw_ver);
+ }
+ } else {
+ dbg_err("FW format error\n");
+ }
+ }
+
+ ft5x0x_auto_clb();/*start auto CLB*/
+
+exit:
+ kfree(pbt_buf);
+ return i_ret;
+}
+
+
diff --git a/drivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.c b/drivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.c
new file mode 100755
index 00000000..08fc6069
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.c
@@ -0,0 +1,1021 @@
+/*
+ *drivers/input/touchscreen/ft5x06_ex_fun.c
+ *
+ *FocalTech ft6x06 expand function for debug.
+ *
+ *Copyright (c) 2010 Focal tech Ltd.
+ *
+ *This software is licensed under the terms of the GNU General Public
+ *License version 2, as published by the Free Software Foundation, and
+ *may be copied, distributed, and modified under those terms.
+ *
+ *This program is distributed in the hope that it will be useful,
+ *but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *GNU General Public License for more details.
+ *
+ *Note:the error code of EIO is the general error in this file.
+ */
+
+
+#include "ft6x06_ex_fun.h"
+#include "ft6x06_ts.h"
+
+#include <linux/mount.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+
+struct Upgrade_Info {
+ u16 delay_aa; /*delay of write FT_UPGRADE_AA */
+ u16 delay_55; /*delay of write FT_UPGRADE_55 */
+ u8 upgrade_id_1; /*upgrade id 1 */
+ u8 upgrade_id_2; /*upgrade id 2 */
+ u16 delay_readid; /*delay of read id */
+ u16 delay_earse_flash; /*delay of earse flash*/
+};
+
+
+int fts_ctpm_fw_upgrade(struct i2c_client *client, u8 *pbt_buf,
+ u32 dw_lenth);
+
+static unsigned char CTPM_FW[] = {
+ #include "FT5406.i"
+};
+
+static struct mutex g_device_mutex;
+
+int ft6x06_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue)
+{
+ unsigned char buf[2] = {0};
+ buf[0] = regaddr;
+ buf[1] = regvalue;
+
+ return ft6x06_i2c_Write(client, buf, sizeof(buf));
+}
+
+
+int ft6x06_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue)
+{
+ return ft6x06_i2c_Read(client, &regaddr, 1, regvalue, 1);
+}
+
+
+int fts_ctpm_auto_clb(struct i2c_client *client)
+{
+ unsigned char uc_temp = 0x00;
+ unsigned char i = 0;
+
+ /*start auto CLB */
+ msleep(200);
+
+ ft6x06_write_reg(client, 0, FTS_FACTORYMODE_VALUE);
+ /*make sure already enter factory mode */
+ msleep(100);
+ /*write command to start calibration */
+ ft6x06_write_reg(client, 2, 0x4);
+ msleep(300);
+ for (i = 0; i < 100; i++) {
+ ft6x06_read_reg(client, 0, &uc_temp);
+ /*return to normal mode, calibration finish */
+ if (0x0 == ((uc_temp & 0x70) >> 4))
+ break;
+ }
+
+ msleep(200);
+ /*calibration OK */
+ msleep(300);
+ ft6x06_write_reg(client, 0, FTS_FACTORYMODE_VALUE); /*goto factory mode for store */
+ msleep(100); /*make sure already enter factory mode */
+ ft6x06_write_reg(client, 2, 0x5); /*store CLB result */
+ msleep(300);
+ ft6x06_write_reg(client, 0, FTS_WORKMODE_VALUE); /*return to normal mode */
+ msleep(300);
+
+ /*store CLB result OK */
+ return 0;
+}
+
+/*
+upgrade with *.i file
+*/
+int fts_ctpm_fw_upgrade_with_i_file(struct i2c_client *client)
+{
+ u8 *pbt_buf = NULL;
+ int i_ret;
+ int fw_len = sizeof(CTPM_FW);
+
+ /*judge the fw that will be upgraded
+ * if illegal, then stop upgrade and return.
+ */
+
+ if (fw_len < 8 || fw_len > 32 * 1024) {
+ dev_err(&client->dev, "%s:FW length error\n", __func__);
+ return -EIO;
+ }
+
+ /*FW upgrade */
+ pbt_buf = CTPM_FW;
+ /*call the upgrade function */
+
+ i_ret = fts_ctpm_fw_upgrade(client, pbt_buf, sizeof(CTPM_FW));
+ if (i_ret != 0)
+ dev_err(&client->dev, "%s:upgrade failed. err.\n",
+ __func__);
+
+ return i_ret;
+}
+
+u8 fts_ctpm_get_i_file_ver(void)
+{
+ u16 ui_sz;
+ ui_sz = sizeof(CTPM_FW);
+ if (ui_sz > 2)
+ return CTPM_FW[0x10a];
+
+ return 0x00; /*default value */
+}
+
+/*update project setting
+*only update these settings for COB project, or for some special case
+*/
+int fts_ctpm_update_project_setting(struct i2c_client *client)
+{
+ u8 uc_i2c_addr; /*I2C slave address (7 bit address)*/
+ u8 uc_io_voltage; /*IO Voltage 0---3.3v; 1----1.8v*/
+ u8 uc_panel_factory_id; /*TP panel factory ID*/
+ u8 buf[FTS_SETTING_BUF_LEN];
+ u8 reg_val[2] = {0};
+ u8 auc_i2c_write_buf[10] = {0};
+ u8 packet_buf[FTS_SETTING_BUF_LEN + 6];
+ u32 i = 0;
+ int i_ret;
+
+ uc_i2c_addr = client->addr;
+ uc_io_voltage = 0x0;
+ uc_panel_factory_id = 0x5a;
+
+
+ /*Step 1:Reset CTPM
+ *write 0xaa to register 0xfc
+ */
+ ft6x06_write_reg(client, 0xfc, 0xaa);
+ msleep(50);
+
+ /*write 0x55 to register 0xfc */
+ ft6x06_write_reg(client, 0xfc, 0x55);
+ msleep(30);
+
+ /*********Step 2:Enter upgrade mode *****/
+ auc_i2c_write_buf[0] = 0x55;
+ auc_i2c_write_buf[1] = 0xaa;
+ do {
+ i++;
+ i_ret = ft6x06_i2c_Write(client, auc_i2c_write_buf, 2);
+ msleep(5);
+ } while (i_ret <= 0 && i < 5);
+
+
+ /*********Step 3:check READ-ID***********************/
+ auc_i2c_write_buf[0] = 0x90;
+ auc_i2c_write_buf[1] = auc_i2c_write_buf[2] = auc_i2c_write_buf[3] =
+ 0x00;
+
+ ft6x06_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2);
+
+ if (reg_val[0] == 0x79 && reg_val[1] == 0x3)
+ dev_dbg(&client->dev, "[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",
+ reg_val[0], reg_val[1]);
+ else
+ return -EIO;
+
+ auc_i2c_write_buf[0] = 0xcd;
+ ft6x06_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1);
+ dev_dbg(&client->dev, "bootloader version = 0x%x\n", reg_val[0]);
+
+ /*--------- read current project setting ---------- */
+ /*set read start address */
+ buf[0] = 0x3;
+ buf[1] = 0x0;
+ buf[2] = 0x78;
+ buf[3] = 0x0;
+
+ ft6x06_i2c_Read(client, buf, 4, buf, FTS_SETTING_BUF_LEN);
+ dev_dbg(&client->dev, "[FTS] old setting: uc_i2c_addr = 0x%x,\
+ uc_io_voltage = %d, uc_panel_factory_id = 0x%x\n",
+ buf[0], buf[2], buf[4]);
+
+ /*--------- Step 4:erase project setting --------------*/
+ auc_i2c_write_buf[0] = 0x63;
+ ft6x06_i2c_Write(client, auc_i2c_write_buf, 1);
+ msleep(100);
+
+ /*---------- Set new settings ---------------*/
+ buf[0] = uc_i2c_addr;
+ buf[1] = ~uc_i2c_addr;
+ buf[2] = uc_io_voltage;
+ buf[3] = ~uc_io_voltage;
+ buf[4] = uc_panel_factory_id;
+ buf[5] = ~uc_panel_factory_id;
+ packet_buf[0] = 0xbf;
+ packet_buf[1] = 0x00;
+ packet_buf[2] = 0x78;
+ packet_buf[3] = 0x0;
+ packet_buf[4] = 0;
+ packet_buf[5] = FTS_SETTING_BUF_LEN;
+
+ for (i = 0; i < FTS_SETTING_BUF_LEN; i++)
+ packet_buf[6 + i] = buf[i];
+
+ ft6x06_i2c_Write(client, packet_buf, FTS_SETTING_BUF_LEN + 6);
+ msleep(100);
+
+ /********* reset the new FW***********************/
+ auc_i2c_write_buf[0] = 0x07;
+ ft6x06_i2c_Write(client, auc_i2c_write_buf, 1);
+
+ msleep(200);
+ return 0;
+}
+
+int fts_ctpm_auto_upgrade(struct i2c_client *client)
+{
+ u8 uc_host_fm_ver = FT6x06_REG_FW_VER;
+ u8 uc_tp_fm_ver;
+ int i_ret;
+
+ ft6x06_read_reg(client, FT6x06_REG_FW_VER, &uc_tp_fm_ver);
+ uc_host_fm_ver = fts_ctpm_get_i_file_ver();
+
+ if (/*the firmware in touch panel maybe corrupted */
+ uc_tp_fm_ver == FT6x06_REG_FW_VER ||
+ /*the firmware in host flash is new, need upgrade */
+ uc_tp_fm_ver < uc_host_fm_ver
+ ) {
+ msleep(100);
+ dev_dbg(&client->dev, "[FTS] uc_tp_fm_ver = 0x%x, uc_host_fm_ver = 0x%x\n",
+ uc_tp_fm_ver, uc_host_fm_ver);
+ i_ret = fts_ctpm_fw_upgrade_with_i_file(client);
+ if (i_ret == 0) {
+ msleep(300);
+ uc_host_fm_ver = fts_ctpm_get_i_file_ver();
+ dev_dbg(&client->dev, "[FTS] upgrade to new version 0x%x\n",
+ uc_host_fm_ver);
+ } else {
+ pr_err("[FTS] upgrade failed ret=%d.\n", i_ret);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+void delay_qt_ms(unsigned long w_ms)
+{
+ unsigned long i;
+ unsigned long j;
+
+ for (i = 0; i < w_ms; i++)
+ {
+ for (j = 0; j < 1000; j++)
+ {
+ udelay(1);
+ }
+ }
+}
+
+int fts_ctpm_fw_upgrade(struct i2c_client *client, u8 *pbt_buf,
+ u32 dw_lenth)
+{
+ u8 reg_val[2] = {0};
+ u32 i = 0;
+ u32 packet_number;
+ u32 j;
+ u32 temp;
+ u32 lenght;
+ u32 fw_length;
+ u8 packet_buf[FTS_PACKET_LENGTH + 6];
+ u8 auc_i2c_write_buf[10];
+ u8 bt_ecc;
+ int i_ret;
+
+
+ if(pbt_buf[0] != 0x02)
+ {
+ DBG("[FTS] FW first byte is not 0x02. so it is invalid \n");
+ return -1;
+ }
+
+ if(dw_lenth > 0x11f)
+ {
+ fw_length = ((u32)pbt_buf[0x100]<<8) + pbt_buf[0x101];
+ if(dw_lenth < fw_length)
+ {
+ DBG("[FTS] Fw length is invalid \n");
+ return -1;
+ }
+ }
+ else
+ {
+ DBG("[FTS] Fw length is invalid \n");
+ return -1;
+ }
+
+ //DBG("[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n", reg_val[0], reg_val[1]);
+
+ for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+ /*********Step 1:Reset CTPM *****/
+ /*write 0xaa to register 0xbc */
+
+ ft6x06_write_reg(client, 0xbc, FT_UPGRADE_AA);
+ msleep(FT6X06_UPGRADE_AA_DELAY);
+
+ /*write 0x55 to register 0xbc */
+ ft6x06_write_reg(client, 0xbc, FT_UPGRADE_55);
+
+ msleep(FT6X06_UPGRADE_55_DELAY);
+
+ /*********Step 2:Enter upgrade mode *****/
+ auc_i2c_write_buf[0] = FT_UPGRADE_55;
+ ft6x06_i2c_Write(client, auc_i2c_write_buf, 1);
+
+ auc_i2c_write_buf[0] = FT_UPGRADE_AA;
+ ft6x06_i2c_Write(client, auc_i2c_write_buf, 1);
+ msleep(FT6X06_UPGRADE_READID_DELAY);
+
+ /*********Step 3:check READ-ID***********************/
+ auc_i2c_write_buf[0] = 0x90;
+ auc_i2c_write_buf[1] = auc_i2c_write_buf[2] = auc_i2c_write_buf[3] =
+ 0x00;
+ reg_val[0] = 0x00;
+ reg_val[1] = 0x00;
+ ft6x06_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2);
+
+
+ if (reg_val[0] == FT6X06_UPGRADE_ID_1
+ && reg_val[1] == FT6X06_UPGRADE_ID_2) {
+ //dev_dbg(&client->dev, "[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",
+ //reg_val[0], reg_val[1]);
+ DBG("[FTS] Step 3: GET CTPM ID OK,ID1 = 0x%x,ID2 = 0x%x\n",
+ reg_val[0], reg_val[1]);
+ break;
+ } else {
+ dev_err(&client->dev, "[FTS] Step 3: GET CTPM ID FAIL,ID1 = 0x%x,ID2 = 0x%x\n",
+ reg_val[0], reg_val[1]);
+ }
+ }
+ if (i >= FTS_UPGRADE_LOOP)
+ return -EIO;
+
+ auc_i2c_write_buf[0] = 0x90;
+ auc_i2c_write_buf[1] = 0x00;
+ auc_i2c_write_buf[2] = 0x00;
+ auc_i2c_write_buf[3] = 0x00;
+ auc_i2c_write_buf[4] = 0x00;
+ ft6x06_i2c_Write(client, auc_i2c_write_buf, 5);
+
+ //auc_i2c_write_buf[0] = 0xcd;
+ //ft6x06_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1);
+
+
+ /*Step 4:erase app and panel paramenter area*/
+ DBG("Step 4:erase app and panel paramenter area\n");
+ auc_i2c_write_buf[0] = 0x61;
+ ft6x06_i2c_Write(client, auc_i2c_write_buf, 1); /*erase app area */
+ msleep(FT6X06_UPGRADE_EARSE_DELAY);
+
+ for(i = 0;i < 200;i++)
+ {
+ auc_i2c_write_buf[0] = 0x6a;
+ auc_i2c_write_buf[1] = 0x00;
+ auc_i2c_write_buf[2] = 0x00;
+ auc_i2c_write_buf[3] = 0x00;
+ reg_val[0] = 0x00;
+ reg_val[1] = 0x00;
+ ft6x06_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2);
+ if(0xb0 == reg_val[0] && 0x02 == reg_val[1])
+ {
+ DBG("[FTS] erase app finished \n");
+ break;
+ }
+ msleep(50);
+ }
+
+ /*********Step 5:write firmware(FW) to ctpm flash*********/
+ bt_ecc = 0;
+ DBG("Step 5:write firmware(FW) to ctpm flash\n");
+
+ dw_lenth = fw_length;
+ packet_number = (dw_lenth) / FTS_PACKET_LENGTH;
+ packet_buf[0] = 0xbf;
+ packet_buf[1] = 0x00;
+
+ for (j = 0; j < packet_number; j++) {
+ temp = j * FTS_PACKET_LENGTH;
+ packet_buf[2] = (u8) (temp >> 8);
+ packet_buf[3] = (u8) temp;
+ lenght = FTS_PACKET_LENGTH;
+ packet_buf[4] = (u8) (lenght >> 8);
+ packet_buf[5] = (u8) lenght;
+
+ for (i = 0; i < FTS_PACKET_LENGTH; i++) {
+ packet_buf[6 + i] = pbt_buf[j * FTS_PACKET_LENGTH + i];
+ bt_ecc ^= packet_buf[6 + i];
+ }
+
+ ft6x06_i2c_Write(client, packet_buf, FTS_PACKET_LENGTH + 6);
+
+ for(i = 0;i < 30;i++)
+ {
+ auc_i2c_write_buf[0] = 0x6a;
+ auc_i2c_write_buf[1] = 0x00;
+ auc_i2c_write_buf[2] = 0x00;
+ auc_i2c_write_buf[3] = 0x00;
+ reg_val[0] = 0x00;
+ reg_val[1] = 0x00;
+ ft6x06_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2);
+ if(0xb0 == (reg_val[0] & 0xf0) && (0x03 + (j % 0x0ffd)) == (((reg_val[0] & 0x0f) << 8) |reg_val[1]))
+ {
+ DBG("[FTS] write a block data finished \n");
+ break;
+ }
+ msleep(1);
+ }
+ //msleep(FTS_PACKET_LENGTH / 6 + 1);
+ //DBG("write bytes:0x%04x\n", (j+1) * FTS_PACKET_LENGTH);
+ //delay_qt_ms(FTS_PACKET_LENGTH / 6 + 1);
+ }
+
+ if ((dw_lenth) % FTS_PACKET_LENGTH > 0) {
+ temp = packet_number * FTS_PACKET_LENGTH;
+ packet_buf[2] = (u8) (temp >> 8);
+ packet_buf[3] = (u8) temp;
+ temp = (dw_lenth) % FTS_PACKET_LENGTH;
+ packet_buf[4] = (u8) (temp >> 8);
+ packet_buf[5] = (u8) temp;
+
+ for (i = 0; i < temp; i++) {
+ packet_buf[6 + i] = pbt_buf[packet_number * FTS_PACKET_LENGTH + i];
+ bt_ecc ^= packet_buf[6 + i];
+ }
+
+ ft6x06_i2c_Write(client, packet_buf, temp + 6);
+
+ for(i = 0;i < 30;i++)
+ {
+ auc_i2c_write_buf[0] = 0x6a;
+ auc_i2c_write_buf[1] = 0x00;
+ auc_i2c_write_buf[2] = 0x00;
+ auc_i2c_write_buf[3] = 0x00;
+ reg_val[0] = 0x00;
+ reg_val[1] = 0x00;
+ ft6x06_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2);
+ if(0xb0 == (reg_val[0] & 0xf0) && (0x03 + (j % 0x0ffd)) == (((reg_val[0] & 0x0f) << 8) |reg_val[1]))
+ {
+ DBG("[FTS] write a block data finished \n");
+ break;
+ }
+ msleep(1);
+ }
+ //msleep(20);
+ }
+
+
+ /*********Step 6: read out checksum***********************/
+ /*send the opration head */
+ DBG("Step 6: read out checksum\n");
+ auc_i2c_write_buf[0] = 0xcc;
+ ft6x06_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1);
+ if (reg_val[0] != bt_ecc) {
+ dev_err(&client->dev, "[FTS]--ecc error! FW=%02x bt_ecc=%02x\n",
+ reg_val[0],
+ bt_ecc);
+ return -EIO;
+ }
+
+ /*********Step 7: reset the new FW***********************/
+ DBG("Step 7: reset the new FW\n");
+ auc_i2c_write_buf[0] = 0x07;
+ ft6x06_i2c_Write(client, auc_i2c_write_buf, 1);
+ msleep(300); /*make sure CTP startup normally */
+
+ return 0;
+}
+
+/*sysfs debug*/
+
+/*
+*get firmware size
+
+@firmware_name:firmware name
+*note:the firmware default path is sdcard.
+ if you want to change the dir, please modify by yourself.
+*/
+static int ft6x06_GetFirmwareSize(char *firmware_name)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize = 0;
+ char filepath[128];
+ memset(filepath, 0, sizeof(filepath));
+
+ sprintf(filepath, "/sdcard/%s", firmware_name);
+
+ if (NULL == pfile)
+ pfile = filp_open(filepath, O_RDONLY, 0);
+
+ if (IS_ERR(pfile)) {
+ pr_err("error occured while opening file %s.\n", filepath);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ filp_close(pfile, NULL);
+ return fsize;
+}
+
+
+
+/*
+*read firmware buf for .bin file.
+
+@firmware_name: fireware name
+@firmware_buf: data buf of fireware
+
+note:the firmware default path is sdcard.
+ if you want to change the dir, please modify by yourself.
+*/
+static int ft6x06_ReadFirmware(char *firmware_name,
+ unsigned char *firmware_buf)
+{
+ struct file *pfile = NULL;
+ struct inode *inode;
+ unsigned long magic;
+ off_t fsize;
+ char filepath[128];
+ loff_t pos;
+ mm_segment_t old_fs;
+
+ memset(filepath, 0, sizeof(filepath));
+ sprintf(filepath, "/sdcard/%s", firmware_name);
+ if (NULL == pfile)
+ pfile = filp_open(filepath, O_RDONLY, 0);
+ if (IS_ERR(pfile)) {
+ pr_err("error occured while opening file %s.\n", filepath);
+ return -EIO;
+ }
+
+ inode = pfile->f_dentry->d_inode;
+ magic = inode->i_sb->s_magic;
+ fsize = inode->i_size;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ pos = 0;
+ vfs_read(pfile, firmware_buf, fsize, &pos);
+ filp_close(pfile, NULL);
+ set_fs(old_fs);
+
+ return 0;
+}
+
+
+
+/*
+upgrade with *.bin file
+*/
+
+int fts_ctpm_fw_upgrade_with_app_file(struct i2c_client *client,
+ char *firmware_name)
+{
+ u8 *pbt_buf = NULL;
+ int i_ret;
+ int fwsize = ft6x06_GetFirmwareSize(firmware_name);
+
+ if (fwsize <= 0) {
+ dev_err(&client->dev, "%s ERROR:Get firmware size failed\n",
+ __func__);
+ return -EIO;
+ }
+
+ if (fwsize < 8 || fwsize > 32 * 1024) {
+ dev_dbg(&client->dev, "%s:FW length error\n", __func__);
+ return -EIO;
+ }
+
+ /*=========FW upgrade========================*/
+ pbt_buf = kmalloc(fwsize + 1, GFP_ATOMIC);
+
+ if (ft6x06_ReadFirmware(firmware_name, pbt_buf)) {
+ dev_err(&client->dev, "%s() - ERROR: request_firmware failed\n",
+ __func__);
+ kfree(pbt_buf);
+ return -EIO;
+ }
+
+ /*call the upgrade function */
+ i_ret = fts_ctpm_fw_upgrade(client, pbt_buf, fwsize);
+ if (i_ret != 0)
+ dev_err(&client->dev, "%s() - ERROR:[FTS] upgrade failed..\n",
+ __func__);
+ //else
+ //fts_ctpm_auto_clb(client);
+ kfree(pbt_buf);
+
+ return i_ret;
+}
+
+static ssize_t ft6x06_tpfwver_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t num_read_chars = 0;
+ u8 fwver = 0;
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+
+ mutex_lock(&g_device_mutex);
+
+ if (ft6x06_read_reg(client, FT6x06_REG_FW_VER, &fwver) < 0)
+ num_read_chars = snprintf(buf, PAGE_SIZE,
+ "get tp fw version fail!\n");
+ else
+ num_read_chars = snprintf(buf, PAGE_SIZE, "%02X\n", fwver);
+
+ mutex_unlock(&g_device_mutex);
+
+ return num_read_chars;
+}
+
+static ssize_t ft6x06_tpfwver_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ /*place holder for future use*/
+ return -EPERM;
+}
+
+
+
+static ssize_t ft6x06_tprwreg_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ /*place holder for future use*/
+ return -EPERM;
+}
+
+static ssize_t ft6x06_tprwreg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ ssize_t num_read_chars = 0;
+ int retval;
+ long unsigned int wmreg = 0;
+ u8 regaddr = 0xff, regvalue = 0xff;
+ u8 valbuf[5] = {0};
+
+ memset(valbuf, 0, sizeof(valbuf));
+ mutex_lock(&g_device_mutex);
+ num_read_chars = count - 1;
+
+ if (num_read_chars != 2) {
+ if (num_read_chars != 4) {
+ pr_info("please input 2 or 4 character\n");
+ goto error_return;
+ }
+ }
+
+ memcpy(valbuf, buf, num_read_chars);
+ retval = strict_strtoul(valbuf, 16, &wmreg);
+
+ if (0 != retval) {
+ dev_err(&client->dev, "%s() - ERROR: Could not convert the "\
+ "given input to a number." \
+ "The given input was: \"%s\"\n",
+ __func__, buf);
+ goto error_return;
+ }
+
+ if (2 == num_read_chars) {
+ /*read register*/
+ regaddr = wmreg;
+ if (ft6x06_read_reg(client, regaddr, &regvalue) < 0)
+ dev_err(&client->dev, "Could not read the register(0x%02x)\n",
+ regaddr);
+ else
+ pr_info("the register(0x%02x) is 0x%02x\n",
+ regaddr, regvalue);
+ } else {
+ regaddr = wmreg >> 8;
+ regvalue = wmreg;
+ if (ft6x06_write_reg(client, regaddr, regvalue) < 0)
+ dev_err(&client->dev, "Could not write the register(0x%02x)\n",
+ regaddr);
+ else
+ dev_err(&client->dev, "Write 0x%02x into register(0x%02x) successful\n",
+ regvalue, regaddr);
+ }
+
+error_return:
+ mutex_unlock(&g_device_mutex);
+
+ return count;
+}
+
+static ssize_t ft6x06_fwupdate_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ /* place holder for future use */
+ return -EPERM;
+}
+
+/*upgrade from *.i*/
+static ssize_t ft6x06_fwupdate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ft6x06_ts_data *data = NULL;
+ u8 uc_host_fm_ver;
+ int i_ret;
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+
+ data = (struct ft6x06_ts_data *)i2c_get_clientdata(client);
+
+ mutex_lock(&g_device_mutex);
+
+ disable_irq(client->irq);
+ i_ret = fts_ctpm_fw_upgrade_with_i_file(client);
+ if (i_ret == 0) {
+ msleep(300);
+ uc_host_fm_ver = fts_ctpm_get_i_file_ver();
+ pr_info("%s [FTS] upgrade to new version 0x%x\n", __func__,
+ uc_host_fm_ver);
+ } else
+ dev_err(&client->dev, "%s ERROR:[FTS] upgrade failed.\n",
+ __func__);
+
+ enable_irq(client->irq);
+ mutex_unlock(&g_device_mutex);
+
+ return count;
+}
+
+static ssize_t ft6x06_fwupgradeapp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ /*place holder for future use*/
+ return -EPERM;
+}
+
+
+/*upgrade from app.bin*/
+static ssize_t ft6x06_fwupgradeapp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ char fwname[128];
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+
+ memset(fwname, 0, sizeof(fwname));
+ sprintf(fwname, "%s", buf);
+ fwname[count - 1] = '\0';
+
+ mutex_lock(&g_device_mutex);
+ disable_irq(client->irq);
+
+ fts_ctpm_fw_upgrade_with_app_file(client, fwname);
+
+ enable_irq(client->irq);
+ mutex_unlock(&g_device_mutex);
+
+ return count;
+}
+
+
+/*sysfs */
+/*get the fw version
+*example:cat ftstpfwver
+*/
+static DEVICE_ATTR(ftstpfwver, S_IRUGO | S_IWUSR, ft6x06_tpfwver_show,
+ ft6x06_tpfwver_store);
+
+/*upgrade from *.i
+*example: echo 1 > ftsfwupdate
+*/
+static DEVICE_ATTR(ftsfwupdate, S_IRUGO | S_IWUSR, ft6x06_fwupdate_show,
+ ft6x06_fwupdate_store);
+
+/*read and write register
+*read example: echo 88 > ftstprwreg ---read register 0x88
+*write example:echo 8807 > ftstprwreg ---write 0x07 into register 0x88
+*
+*note:the number of input must be 2 or 4.if it not enough,please fill in the 0.
+*/
+static DEVICE_ATTR(ftstprwreg, S_IRUGO | S_IWUSR, ft6x06_tprwreg_show,
+ ft6x06_tprwreg_store);
+
+
+/*upgrade from app.bin
+*example:echo "*_app.bin" > ftsfwupgradeapp
+*/
+static DEVICE_ATTR(ftsfwupgradeapp, S_IRUGO | S_IWUSR, ft6x06_fwupgradeapp_show,
+ ft6x06_fwupgradeapp_store);
+
+
+/*add your attr in here*/
+static struct attribute *ft6x06_attributes[] = {
+ &dev_attr_ftstpfwver.attr,
+ &dev_attr_ftsfwupdate.attr,
+ &dev_attr_ftstprwreg.attr,
+ &dev_attr_ftsfwupgradeapp.attr,
+ NULL
+};
+
+static struct attribute_group ft6x06_attribute_group = {
+ .attrs = ft6x06_attributes
+};
+
+/*create sysfs for debug*/
+int ft6x06_create_sysfs(struct i2c_client *client)
+{
+ int err;
+ err = sysfs_create_group(&client->dev.kobj, &ft6x06_attribute_group);
+ if (0 != err) {
+ dev_err(&client->dev,
+ "%s() - ERROR: sysfs_create_group() failed.\n",
+ __func__);
+ sysfs_remove_group(&client->dev.kobj, &ft6x06_attribute_group);
+ return -EIO;
+ } else {
+ mutex_init(&g_device_mutex);
+ pr_info("ft6x06:%s() - sysfs_create_group() succeeded.\n",
+ __func__);
+ }
+ return err;
+}
+
+void ft6x06_release_sysfs(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &ft6x06_attribute_group);
+ mutex_destroy(&g_device_mutex);
+}
+
+/*create apk debug channel*/
+#define PROC_UPGRADE 0
+#define PROC_READ_REGISTER 1
+#define PROC_WRITE_REGISTER 2
+#define PROC_AUTOCLB 4
+#define PROC_UPGRADE_INFO 5
+#define PROC_WRITE_DATA 6
+#define PROC_READ_DATA 7
+
+
+#define PROC_NAME "ft5x0x-debug"
+static unsigned char proc_operate_mode = PROC_UPGRADE;
+static struct proc_dir_entry *ft6x06_proc_entry;
+/*interface of write proc*/
+static int ft6x06_debug_write(struct file *filp,
+ const char __user *buff, unsigned long len, void *data)
+{
+ struct i2c_client *client = (struct i2c_client *)ft6x06_proc_entry->data;
+ unsigned char writebuf[FTS_PACKET_LENGTH];
+ int buflen = len;
+ int writelen = 0;
+ int ret = 0;
+
+ if (copy_from_user(&writebuf, buff, buflen)) {
+ dev_err(&client->dev, "%s:copy from user error\n", __func__);
+ return -EFAULT;
+ }
+ proc_operate_mode = writebuf[0];
+
+ switch (proc_operate_mode) {
+ case PROC_UPGRADE:
+ {
+ char upgrade_file_path[128];
+ memset(upgrade_file_path, 0, sizeof(upgrade_file_path));
+ sprintf(upgrade_file_path, "%s", writebuf + 1);
+ upgrade_file_path[buflen-1] = '\0';
+ DBG("%s\n", upgrade_file_path);
+ disable_irq(client->irq);
+
+ ret = fts_ctpm_fw_upgrade_with_app_file(client, upgrade_file_path);
+
+ enable_irq(client->irq);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s:upgrade failed.\n", __func__);
+ return ret;
+ }
+ }
+ break;
+ case PROC_READ_REGISTER:
+ writelen = 1;
+ ret = ft6x06_i2c_Write(client, writebuf + 1, writelen);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s:write iic error\n", __func__);
+ return ret;
+ }
+ break;
+ case PROC_WRITE_REGISTER:
+ writelen = 2;
+ ret = ft6x06_i2c_Write(client, writebuf + 1, writelen);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s:write iic error\n", __func__);
+ return ret;
+ }
+ break;
+ case PROC_AUTOCLB:
+ DBG("%s: autoclb\n", __func__);
+ fts_ctpm_auto_clb(client);
+ break;
+ case PROC_READ_DATA:
+ case PROC_WRITE_DATA:
+ writelen = len - 1;
+ ret = ft6x06_i2c_Write(client, writebuf + 1, writelen);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s:write iic error\n", __func__);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+
+ return len;
+}
+
+/*interface of read proc*/
+static int ft6x06_debug_read( char *page, char **start,
+ off_t off, int count, int *eof, void *data )
+{
+ struct i2c_client *client = (struct i2c_client *)ft6x06_proc_entry->data;
+ int ret = 0;
+ unsigned char buf[PAGE_SIZE];
+ int num_read_chars = 0;
+ int readlen = 0;
+ u8 regvalue = 0x00, regaddr = 0x00;
+
+ switch (proc_operate_mode) {
+ case PROC_UPGRADE:
+ /*after calling ft5x0x_debug_write to upgrade*/
+ regaddr = 0xA6;
+ ret = ft6x06_read_reg(client, regaddr, &regvalue);
+ if (ret < 0)
+ num_read_chars = sprintf(buf, "%s", "get fw version failed.\n");
+ else
+ num_read_chars = sprintf(buf, "current fw version:0x%02x\n", regvalue);
+ break;
+ case PROC_READ_REGISTER:
+ readlen = 1;
+ ret = ft6x06_i2c_Read(client, NULL, 0, buf, readlen);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s:read iic error\n", __func__);
+ return ret;
+ }
+ num_read_chars = 1;
+ break;
+ case PROC_READ_DATA:
+ readlen = count;
+ ret = ft6x06_i2c_Read(client, NULL, 0, buf, readlen);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s:read iic error\n", __func__);
+ return ret;
+ }
+
+ num_read_chars = readlen;
+ break;
+ case PROC_WRITE_DATA:
+ break;
+ default:
+ break;
+ }
+
+ memcpy(page, buf, num_read_chars);
+ return num_read_chars;
+}
+int ft6x06_create_apk_debug_channel(struct i2c_client * client)
+{
+ ft6x06_proc_entry = create_proc_entry(PROC_NAME, 0777, NULL);
+ if (NULL == ft6x06_proc_entry) {
+ dev_err(&client->dev, "Couldn't create proc entry!\n");
+ return -ENOMEM;
+ } else {
+ dev_info(&client->dev, "Create proc entry success!\n");
+ ft6x06_proc_entry->data = client;
+ ft6x06_proc_entry->write_proc = ft6x06_debug_write;
+ ft6x06_proc_entry->read_proc = ft6x06_debug_read;
+ }
+ return 0;
+}
+
+void ft6x06_release_apk_debug_channel(void)
+{
+ if (ft6x06_proc_entry)
+ remove_proc_entry(PROC_NAME, NULL);
+}
+
diff --git a/drivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.h b/drivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.h
new file mode 100755
index 00000000..e25675c0
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft6x06_ex_fun.h
@@ -0,0 +1,79 @@
+#ifndef __LINUX_FT6X06_EX_FUN_H__
+#define __LINUX_FT6X06_EX_FUN_H__
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/semaphore.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+
+#include <linux/syscalls.h>
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+
+
+#define FT_UPGRADE_AA 0xAA
+#define FT_UPGRADE_55 0x55
+
+
+//upgrade config of FT6X06
+/*
+#define FT6X06_UPGRADE_AA_DELAY 100
+#define FT6X06_UPGRADE_55_DELAY 10
+#define FT6X06_UPGRADE_ID_1 0x79
+#define FT6X06_UPGRADE_ID_2 0x08
+#define FT6X06_UPGRADE_READID_DELAY 10
+#define FT6X06_UPGRADE_EARSE_DELAY 2000
+*/
+
+/*upgrade config of FT6X36*/
+#define FT6X06_UPGRADE_AA_DELAY 10
+#define FT6X06_UPGRADE_55_DELAY 10
+#define FT6X06_UPGRADE_ID_1 0x79
+#define FT6X06_UPGRADE_ID_2 0x18
+#define FT6X06_UPGRADE_READID_DELAY 10
+#define FT6X06_UPGRADE_EARSE_DELAY 2000
+
+#define FTS_PACKET_LENGTH 128
+#define FTS_SETTING_BUF_LEN 128
+
+#define FTS_UPGRADE_LOOP 20
+
+#define FTS_FACTORYMODE_VALUE 0x40
+#define FTS_WORKMODE_VALUE 0x00
+
+//#define AUTO_CLB
+#define FTS_DBG
+#ifdef FTS_DBG
+#define DBG(fmt, args...) printk("[FTS]" fmt, ## args)
+#else
+#define DBG(fmt, args...) do{}while(0)
+#endif
+
+/*create sysfs for debug*/
+int ft6x06_create_sysfs(struct i2c_client * client);
+
+void ft6x06_release_sysfs(struct i2c_client * client);
+
+int ft6x06_create_apk_debug_channel(struct i2c_client *client);
+
+void ft6x06_release_apk_debug_channel(void);
+
+/*
+*ft6x06_write_reg- write register
+*@client: handle of i2c
+*@regaddr: register address
+*@regvalue: register value
+*
+*/
+int ft6x06_write_reg(struct i2c_client * client,u8 regaddr, u8 regvalue);
+
+int ft6x06_read_reg(struct i2c_client * client,u8 regaddr, u8 *regvalue);
+
+#endif
diff --git a/drivers/input/touchscreen/ft6x0x/ft6x06_ts.c b/drivers/input/touchscreen/ft6x0x/ft6x06_ts.c
new file mode 100755
index 00000000..56148177
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft6x06_ts.c
@@ -0,0 +1,511 @@
+/* drivers/input/touchscreen/ft5x06_ts.c
+ *
+ * FocalTech ft6x06 TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include "ft6x06_ts.h"
+//#include <linux/earlysuspend.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include <linux/kernel.h>
+#include <linux/semaphore.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/syscalls.h>
+#include <linux/unistd.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+
+//#define FTS_CTL_FACE_DETECT
+#define FTS_CTL_IIC
+#define SYSFS_DEBUG
+#define FTS_APK_DEBUG
+//#define FT6X06_DOWNLOAD
+
+#ifdef FTS_CTL_IIC
+#include "focaltech_ctl.h"
+#endif
+#ifdef FTS_CTL_FACE_DETECT
+#include "ft_psensor_drv.h"
+#endif
+#ifdef SYSFS_DEBUG
+#include "ft6x06_ex_fun.h"
+#endif
+
+#if 0
+struct ts_event {
+ u16 au16_x[CFG_MAX_TOUCH_POINTS]; /*x coordinate */
+ u16 au16_y[CFG_MAX_TOUCH_POINTS]; /*y coordinate */
+ u8 au8_touch_event[CFG_MAX_TOUCH_POINTS]; /*touch event:
+ 0 -- down; 1-- up; 2 -- contact */
+ u8 au8_finger_id[CFG_MAX_TOUCH_POINTS]; /*touch ID */
+ u16 pressure;
+ u8 touch_point;
+};
+
+struct ft6x06_ts_data {
+ unsigned int irq;
+ unsigned int x_max;
+ unsigned int y_max;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct ts_event event;
+ struct ft6x06_platform_data *pdata;
+#ifdef CONFIG_PM
+ struct early_suspend *early_suspend;
+#endif
+};
+
+#define FTS_POINT_UP 0x01
+#define FTS_POINT_DOWN 0x00
+#define FTS_POINT_CONTACT 0x02
+#endif
+
+/*
+*ft6x06_i2c_Read-read data and write data by i2c
+*@client: handle of i2c
+*@writebuf: Data that will be written to the slave
+*@writelen: How many bytes to write
+*@readbuf: Where to store data read from slave
+*@readlen: How many bytes to read
+*
+*Returns negative errno, else the number of messages executed
+*
+*
+*/
+int ft6x06_i2c_Read(struct i2c_client *client, char *writebuf,
+ int writelen, char *readbuf, int readlen)
+{
+ int ret;
+
+ if (writelen > 0) {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ dev_err(&client->dev, "f%s: i2c read error.\n",
+ __func__);
+ } else {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0)
+ dev_err(&client->dev, "%s:i2c read error.\n", __func__);
+ }
+ return ret;
+}
+/*write data by i2c*/
+int ft6x06_i2c_Write(struct i2c_client *client, char *writebuf, int writelen)
+{
+ int ret;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ };
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ dev_err(&client->dev, "%s i2c write error.\n", __func__);
+
+ return ret;
+}
+
+#if 0
+/*Read touch point information when the interrupt is asserted.*/
+static int ft6x06_read_Touchdata(struct ft6x06_ts_data *data)
+{
+ struct ts_event *event = &data->event;
+ u8 buf[POINT_READ_BUF] = { 0 };
+ int ret = -1;
+ int i = 0;
+ u8 pointid = FT_MAX_ID;
+
+ ret = ft6x06_i2c_Read(data->client, buf, 1, buf, POINT_READ_BUF);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "%s read touchdata failed.\n",
+ __func__);
+ return ret;
+ }
+ memset(event, 0, sizeof(struct ts_event));
+
+ //event->touch_point = buf[2] & 0x0F;
+
+ //event->touch_point = 0;
+
+ for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++)
+ {
+ pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+ if (pointid >= FT_MAX_ID)
+ break;
+ else
+ event->touch_point++;
+ event->au16_x[i] =
+ (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+ 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i];
+ event->au16_y[i] =
+ (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
+ 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i];
+ event->au8_touch_event[i] =
+ buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6;
+ event->au8_finger_id[i] =
+ (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+ }
+
+ //event->pressure = FT_PRESS;
+
+ return 0;
+}
+
+/*
+*report the point information
+*/
+static void ft6x06_report_value(struct ft6x06_ts_data *data)
+{
+ struct ts_event *event = &data->event;
+ int i = 0;
+ int up_point = 0;
+
+ for (i = 0; i < event->touch_point; i++)
+ {
+ input_mt_slot(data->input_dev, event->au8_finger_id[i]);
+
+ if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)
+ {
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,
+ true);
+ //input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
+ //event->au8_finger_id[i]);
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+ 0x3f);
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+ 0x05);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+ event->au16_x[i]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+ event->au16_y[i]);
+
+ }
+ else
+ {
+ up_point++;
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,
+ false);
+ }
+
+ }
+
+ if(event->touch_point == up_point)
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ else
+ input_report_key(data->input_dev, BTN_TOUCH, 1);
+
+ input_sync(data->input_dev);
+
+}
+
+/*The ft6x06 device will signal the host about TRIGGER_FALLING.
+*Processed when the interrupt is asserted.
+*/
+static irqreturn_t ft6x06_ts_interrupt(int irq, void *dev_id)
+{
+ struct ft6x06_ts_data *ft6x06_ts = dev_id;
+ int ret = 0;
+ disable_irq_nosync(ft6x06_ts->irq);
+
+ ret = ft6x06_read_Touchdata(ft6x06_ts);
+ if (ret == 0)
+ ft6x06_report_value(ft6x06_ts);
+
+ enable_irq(ft6x06_ts->irq);
+
+ //printk(KERN_WARNING "interrupt \n");
+
+ return IRQ_HANDLED;
+}
+
+static int ft6x06_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ft6x06_platform_data *pdata =
+ (struct ft6x06_platform_data *)client->dev.platform_data;
+ struct ft6x06_ts_data *ft6x06_ts;
+ struct input_dev *input_dev;
+ int err = 0;
+ unsigned char uc_reg_value;
+ unsigned char uc_reg_addr;
+
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ ft6x06_ts = kzalloc(sizeof(struct ft6x06_ts_data), GFP_KERNEL);
+
+ if (!ft6x06_ts) {
+ err = -ENOMEM;
+ goto exit_alloc_data_failed;
+ }
+
+ i2c_set_clientdata(client, ft6x06_ts);
+ ft6x06_ts->irq = client->irq;
+ ft6x06_ts->client = client;
+ ft6x06_ts->pdata = pdata;
+ ft6x06_ts->x_max = pdata->x_max - 1;
+ ft6x06_ts->y_max = pdata->y_max - 1;
+ ft6x06_ts->pdata->irq = ft6x06_ts->irq;
+ client->irq = ft6x06_ts->irq;
+ pr_info("irq = %d\n", client->irq);
+
+#ifdef CONFIG_PM
+ #if 0
+ err = gpio_request(pdata->reset, "ft6x06 reset");
+ if (err < 0) {
+ dev_err(&client->dev, "%s:failed to set gpio reset.\n",
+ __func__);
+ goto exit_request_reset;
+ }
+ #endif
+#endif
+
+ err = request_threaded_irq(client->irq, NULL, ft6x06_ts_interrupt,
+ IRQF_TRIGGER_FALLING, client->dev.driver->name,
+ ft6x06_ts);
+
+ if (err < 0) {
+ dev_err(&client->dev, "ft6x06_probe: request irq failed\n");
+ goto exit_irq_request_failed;
+ }
+ disable_irq(client->irq);
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ ft6x06_ts->input_dev = input_dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ input_mt_init_slots(input_dev, MT_MAX_TOUCH_POINTS);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, PRESS_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, ft6x06_ts->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, ft6x06_ts->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, PRESS_MAX, 0, 0);
+
+ input_dev->name = FT6X06_NAME;
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&client->dev,
+ "ft6x06_ts_probe: failed to register input device: %s\n",
+ dev_name(&client->dev));
+ goto exit_input_register_device_failed;
+ }
+ /*make sure CTP already finish startup process */
+ msleep(150);
+
+ /*get some register information */
+ uc_reg_addr = FT6x06_REG_FW_VER;
+ ft6x06_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1);
+ dev_dbg(&client->dev, "[FTS] Firmware version = 0x%x\n", uc_reg_value);
+
+ uc_reg_addr = FT6x06_REG_POINT_RATE;
+ ft6x06_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1);
+ dev_dbg(&client->dev, "[FTS] report rate is %dHz.\n",
+ uc_reg_value * 10);
+
+ uc_reg_addr = FT6x06_REG_THGROUP;
+ ft6x06_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1);
+ dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n",
+ uc_reg_value * 4);
+
+#ifdef SYSFS_DEBUG
+ ft6x06_create_sysfs(client);
+#endif
+
+#ifdef FTS_CTL_IIC
+ if (ft_rw_iic_drv_init(client) < 0)
+ dev_err(&client->dev, "%s:[FTS] create fts control iic driver failed\n",
+ __func__);
+#endif
+
+#ifdef FTS_APK_DEBUG
+ ft6x06_create_apk_debug_channel(client);
+#endif
+
+#ifdef FTS_CTL_FACE_DETECT
+ if (ft_psensor_drv_init(client) < 0)
+ dev_err(&client->dev, "%s:[FTS] create fts control psensor driver failed\n",
+ __func__);
+#endif
+
+ enable_irq(client->irq);
+ return 0;
+
+exit_input_register_device_failed:
+ input_free_device(input_dev);
+
+exit_input_dev_alloc_failed:
+ free_irq(client->irq, ft6x06_ts);
+#ifdef CONFIG_PM
+exit_request_reset:
+ gpio_free(ft6x06_ts->pdata->reset);
+#endif
+
+exit_irq_request_failed:
+ i2c_set_clientdata(client, NULL);
+ kfree(ft6x06_ts);
+
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ return err;
+}
+
+#ifdef CONFIG_PM
+static void ft6x06_ts_suspend(struct early_suspend *handler)
+{
+ struct ft6x06_ts_data *ts = container_of(handler, struct ft6x06_ts_data,
+ early_suspend);
+
+ dev_dbg(&ts->client->dev, "[FTS]ft6x06 suspend\n");
+ disable_irq(ts->pdata->irq);
+}
+
+static void ft6x06_ts_resume(struct early_suspend *handler)
+{
+ struct ft6x06_ts_data *ts = container_of(handler, struct ft6x06_ts_data,
+ early_suspend);
+
+ dev_dbg(&ts->client->dev, "[FTS]ft6x06 resume.\n");
+ gpio_set_value(ts->pdata->reset, 0);
+ msleep(20);
+ gpio_set_value(ts->pdata->reset, 1);
+ enable_irq(ts->pdata->irq);
+}
+#else
+#define ft6x06_ts_suspend NULL
+#define ft6x06_ts_resume NULL
+#endif
+
+static int __devexit ft6x06_ts_remove(struct i2c_client *client)
+{
+ struct ft6x06_ts_data *ft6x06_ts;
+ ft6x06_ts = i2c_get_clientdata(client);
+ input_unregister_device(ft6x06_ts->input_dev);
+ #ifdef CONFIG_PM
+ gpio_free(ft6x06_ts->pdata->reset);
+ #endif
+
+ #ifdef SYSFS_DEBUG
+ ft6x06_release_sysfs(client);
+ #endif
+ #ifdef FTS_CTL_IIC
+ ft_rw_iic_drv_exit();
+ #endif
+ #ifdef FTS_CTL_FACE_DETECT
+ ft_psensor_drv_exit();
+ #endif
+ free_irq(client->irq, ft6x06_ts);
+ kfree(ft6x06_ts);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+static const struct i2c_device_id ft6x06_ts_id[] = {
+ {FT6X06_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ft6x06_ts_id);
+
+static struct i2c_driver ft6x06_ts_driver = {
+ .probe = ft6x06_ts_probe,
+ .remove = __devexit_p(ft6x06_ts_remove),
+ .id_table = ft6x06_ts_id,
+ .suspend = ft6x06_ts_suspend,
+ .resume = ft6x06_ts_resume,
+ .driver = {
+ .name = FT6X06_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ft6x06_ts_init(void)
+{
+ int ret;
+ ret = i2c_add_driver(&ft6x06_ts_driver);
+ if (ret) {
+ printk(KERN_WARNING "Adding ft6x06 driver failed "
+ "(errno = %d)\n", ret);
+ } else {
+ pr_info("Successfully added driver %s\n",
+ ft6x06_ts_driver.driver.name);
+ }
+ return ret;
+}
+
+static void __exit ft6x06_ts_exit(void)
+{
+ i2c_del_driver(&ft6x06_ts_driver);
+}
+
+module_init(ft6x06_ts_init);
+module_exit(ft6x06_ts_exit);
+
+MODULE_AUTHOR("<luowj>");
+MODULE_DESCRIPTION("FocalTech ft6x06 TouchScreen driver");
+MODULE_LICENSE("GPL");
+
+#endif
diff --git a/drivers/input/touchscreen/ft6x0x/ft6x06_ts.h b/drivers/input/touchscreen/ft6x0x/ft6x06_ts.h
new file mode 100755
index 00000000..83859c05
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ft6x06_ts.h
@@ -0,0 +1,52 @@
+#ifndef __LINUX_FT6X06_TS_H__
+#define __LINUX_FT6X06_TS_H__
+
+/* -- dirver configure -- */
+#define CFG_MAX_TOUCH_POINTS 2
+#define MT_MAX_TOUCH_POINTS 9
+
+#define PRESS_MAX 0xFF
+#define FT_PRESS 0x7F
+
+#define Proximity_Max 32
+
+#define FT_FACE_DETECT_ON 0xc0
+#define FT_FACE_DETECT_OFF 0xe0
+
+#define FT_FACE_DETECT_ENABLE 1
+#define FT_FACE_DETECT_DISABLE 0
+#define FT_FACE_DETECT_REG 0xB0
+
+#define FT6X06_NAME "ft6x06_ts"
+
+#define FT_MAX_ID 0x0F
+#define FT_TOUCH_STEP 6
+#define FT_FACE_DETECT_POS 1
+#define FT_TOUCH_X_H_POS 3
+#define FT_TOUCH_X_L_POS 4
+#define FT_TOUCH_Y_H_POS 5
+#define FT_TOUCH_Y_L_POS 6
+#define FT_TOUCH_EVENT_POS 3
+#define FT_TOUCH_ID_POS 5
+
+#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
+
+/*register address*/
+#define FT6x06_REG_FW_VER 0xA6
+#define FT6x06_REG_POINT_RATE 0x88
+#define FT6x06_REG_THGROUP 0x80
+
+int ft6x06_i2c_Read(struct i2c_client *client, char *writebuf, int writelen,
+ char *readbuf, int readlen);
+int ft6x06_i2c_Write(struct i2c_client *client, char *writebuf, int writelen);
+
+/* The platform data for the Focaltech ft6x06 touchscreen driver */
+struct ft6x06_platform_data {
+ unsigned int x_max;
+ unsigned int y_max;
+ unsigned long irqflags;
+ unsigned int irq;
+ unsigned int reset;
+};
+
+#endif
diff --git a/drivers/input/touchscreen/ft6x0x/ini.c b/drivers/input/touchscreen/ft6x0x/ini.c
new file mode 100755
index 00000000..a4f8dc38
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ini.c
@@ -0,0 +1,406 @@
+#include <linux/string.h>
+#include <asm/unistd.h>
+#include <linux/slab.h>
+
+#include "ini.h"
+
+
+char CFG_SSL = '['; /* Ïî±êÖ¾·ûSection Symbol --¿É¸ù¾ÝÌØÊâÐèÒª½øÐж¨Òå¸ü¸Ä£¬Èç { }µÈ*/
+char CFG_SSR = ']'; /* Ïî±êÖ¾·ûSection Symbol --¿É¸ù¾ÝÌØÊâÐèÒª½øÐж¨Òå¸ü¸Ä£¬Èç { }µÈ*/
+char CFG_NIS = ':'; /* name Óë index Ö®¼äµÄ·Ö¸ô·û */
+char CFG_NTS = '#'; /* ×¢ÊÍ·û*/
+
+static char * ini_str_trim_r(char * buf);
+static char * ini_str_trim_l(char * buf);
+static int ini_file_get_line(char *filedata, char *buffer, int maxlen);
+static int ini_split_key_value(char *buf, char **key, char **val);
+static long atol(char *nptr);
+
+
+/*************************************************************
+Function: »ñµÃkeyµÄÖµ
+Input: char * filedata¡¡Îļþ£»char * section¡¡ÏîÖµ£»char * key¡¡¼üÖµ
+Output: char * value¡¡keyµÄÖµ
+Return: 0 SUCCESS
+ -1 δÕÒµ½section
+ -2 δÕÒµ½key
+ -10 Îļþ´ò¿ªÊ§°Ü
+ -12 ¶ÁÈ¡Îļþʧ°Ü
+ -14 Îļþ¸ñʽ´íÎó
+ -22 ³¬³ö»º³åÇø´óС
+Note:
+*************************************************************/
+int ini_get_key(char *filedata, char * section, char * key, char * value)
+{
+ //char buf1[MAX_CFG_BUF + 1], buf2[MAX_CFG_BUF + 1];
+ char *buf1, *buf2;
+ char *key_ptr, *val_ptr;
+ int n, ret;
+ int dataoff = 0;
+
+ *value='\0';
+
+ buf1 = kzalloc(MAX_CFG_BUF + 1, GFP_KERNEL);
+ if(!buf1){
+ printk("buf1: mem alloc failed.\n");
+ return -ENOMEM;
+ }
+ buf2 = kzalloc(MAX_CFG_BUF + 1, GFP_KERNEL);
+ if(!buf2){
+ printk("buf2: mem alloc failed.\n");
+ kfree(buf1);
+ return -ENOMEM;
+ }
+
+ while(1) { /* ËÑÕÒÏîsection */
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf1, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto r_cfg_end;
+ ret = CFG_SECTION_NOT_FOUND;
+ if(n < 0)
+ goto r_cfg_end; /* Îļþβ£¬Î´·¢ÏÖ */
+
+ n = strlen(ini_str_trim_l(ini_str_trim_r(buf1)));
+ if(n == 0 || buf1[0] == CFG_NTS)
+ continue; /* ¿ÕÐÐ »ò ×¢ÊÍÐÐ */
+
+ ret = CFG_ERR_FILE_FORMAT;
+ if(n > 2 && ((buf1[0] == CFG_SSL && buf1[n-1] != CFG_SSR)))
+ goto r_cfg_end;
+ if(buf1[0] == CFG_SSL) {
+ buf1[n-1] = 0x00;
+ if(strcmp(buf1+1, section) == 0)
+ break; /* ÕÒµ½Ïîsection */
+ }
+ }
+
+ while(1){ /* ËÑÕÒkey */
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf1, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto r_cfg_end;
+ ret = CFG_KEY_NOT_FOUND;
+ if(n < 0)
+ goto r_cfg_end;/* Îļþβ£¬Î´·¢ÏÖkey */
+
+ n = strlen(ini_str_trim_l(ini_str_trim_r(buf1)));
+ if(n == 0 || buf1[0] == CFG_NTS)
+ continue; /* ¿ÕÐÐ »ò ×¢ÊÍÐÐ */
+ ret = CFG_KEY_NOT_FOUND;
+ if(buf1[0] == CFG_SSL)
+ goto r_cfg_end;
+ if(buf1[n-1] == '+') { /* Óö+ºÅ±íʾÏÂÒ»ÐмÌÐø */
+ buf1[n-1] = 0x00;
+ while(1) {
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf2, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto r_cfg_end;
+ if(n < 0)
+ break;/* Îļþ½áÊø */
+
+ n = strlen(ini_str_trim_r(buf2));
+ ret = CFG_ERR_EXCEED_BUF_SIZE;
+ if(n > 0 && buf2[n-1] == '+'){/* Óö+ºÅ±íʾÏÂÒ»ÐмÌÐø */
+ buf2[n-1] = 0x00;
+ if( (strlen(buf1) + strlen(buf2)) > MAX_CFG_BUF)
+ goto r_cfg_end;
+ strcat(buf1, buf2);
+ continue;
+ }
+ if(strlen(buf1) + strlen(buf2) > MAX_CFG_BUF)
+ goto r_cfg_end;
+ strcat(buf1, buf2);
+ break;
+ }
+ }
+ ret = CFG_ERR_FILE_FORMAT;
+ if(ini_split_key_value(buf1, &key_ptr, &val_ptr) != 1)
+ goto r_cfg_end;
+ ini_str_trim_l(ini_str_trim_r(key_ptr));
+ if(strcmp(key_ptr, key) != 0)
+ continue; /* ºÍkeyÖµ²»Æ¥Åä */
+ strcpy(value, val_ptr);
+ break;
+ }
+ ret = CFG_OK;
+r_cfg_end:
+ //if(fp != NULL) fclose(fp);
+ kfree(buf1);
+ kfree(buf2);
+ return ret;
+}
+/*************************************************************
+Function: »ñµÃËùÓÐsection
+Input: char *filename¡¡Îļþ,int max ×î´ó¿É·µ»ØµÄsectionµÄ¸öÊý
+Output: char *sections[]¡¡´æ·ÅsectionÃû×Ö
+Return: ·µ»Øsection¸öÊý¡£Èô³ö´í£¬·µ»Ø¸ºÊý¡£
+ -10 Îļþ´ò¿ª³ö´í
+ -12 Îļþ¶ÁÈ¡´íÎó
+ -14 Îļþ¸ñʽ´íÎó
+Note:
+*************************************************************/
+int ini_get_sections(char *filedata, unsigned char * sections[], int max)
+{
+ //FILE *fp;
+ char buf1[MAX_CFG_BUF + 1];
+ int n, n_sections = 0, ret;
+ int dataoff = 0;
+
+// if((fp = fopen(filename, "rb")) == NULL)
+// return CFG_ERR_OPEN_FILE;
+
+ while(1) {/*ËÑÕÒÏîsection */
+ ret = CFG_ERR_READ_FILE;
+ n = ini_file_get_line(filedata+dataoff, buf1, MAX_CFG_BUF);
+ dataoff += n;
+ if(n < -1)
+ goto cfg_scts_end;
+ if(n < 0)
+ break;/* Îļþβ */
+ n = strlen(ini_str_trim_l(ini_str_trim_r(buf1)));
+ if(n == 0 || buf1[0] == CFG_NTS)
+ continue; /* ¿ÕÐÐ »ò ×¢ÊÍÐÐ */
+ ret = CFG_ERR_FILE_FORMAT;
+ if(n > 2 && ((buf1[0] == CFG_SSL && buf1[n-1] != CFG_SSR)))
+ goto cfg_scts_end;
+ if(buf1[0] == CFG_SSL) {
+ if (max!=0){
+ buf1[n-1] = 0x00;
+ strcpy((char *)sections[n_sections], buf1+1);
+ if (n_sections>=max)
+ break; /* ³¬¹ý¿É·µ»Ø×î´ó¸öÊý */
+ }
+ n_sections++;
+ }
+
+ }
+ ret = n_sections;
+cfg_scts_end:
+// if(fp != NULL)
+// fclose(fp);
+ return ret;
+}
+
+
+/*************************************************************
+Function: È¥³ý×Ö·û´®ÓұߵĿÕ×Ö·û
+Input: char * buf ×Ö·û´®Ö¸Õë
+Output:
+Return: ×Ö·û´®Ö¸Õë
+Note:
+*************************************************************/
+static char * ini_str_trim_r(char * buf)
+{
+ int len,i;
+ char tmp[128];
+
+ memset(tmp, 0, sizeof(tmp));
+ len = strlen(buf);
+// tmp = (char *)malloc(len);
+
+ memset(tmp,0x00,len);
+ for(i = 0;i < len;i++) {
+ if (buf[i] !=' ')
+ break;
+ }
+ if (i < len) {
+ strncpy(tmp,(buf+i),(len-i));
+ }
+ strncpy(buf,tmp,len);
+// free(tmp);
+ return buf;
+}
+
+/*************************************************************
+Function: È¥³ý×Ö·û´®×ó±ßµÄ¿Õ×Ö·û
+Input: char * buf ×Ö·û´®Ö¸Õë
+Output:
+Return: ×Ö·û´®Ö¸Õë
+Note:
+*************************************************************/
+static char * ini_str_trim_l(char * buf)
+{
+ int len,i;
+ char tmp[128];
+
+ memset(tmp, 0, sizeof(tmp));
+ len = strlen(buf);
+ //tmp = (char *)malloc(len);
+
+ memset(tmp,0x00,len);
+
+ for(i = 0;i < len;i++) {
+ if (buf[len-i-1] !=' ')
+ break;
+ }
+ if (i < len) {
+ strncpy(tmp,buf,len-i);
+ }
+ strncpy(buf,tmp,len);
+ //free(tmp);
+ return buf;
+}
+/*************************************************************
+Function: ´ÓÎļþÖжÁÈ¡Ò»ÐÐ
+Input: FILE *fp Îļþ¾ä±ú£»int maxlen »º³åÇø×î´ó³¤¶È
+Output: char *buffer Ò»ÐÐ×Ö·û´®
+Return: >0 ʵ¼Ê¶ÁµÄ³¤¶È
+ -1 Îļþ½áÊø
+ -2 ¶ÁÎļþ³ö´í
+Note:
+*************************************************************/
+static int ini_file_get_line(char *filedata, char *buffer, int maxlen)
+{
+ int i, j;
+ char ch1;
+
+ for(i=0, j=0; i<maxlen; j++) {
+ ch1 = filedata[j];
+ if(ch1 == '\n' || ch1 == 0x00)
+ break; /* »»ÐÐ */
+ if(ch1 == '\f' || ch1 == 0x1A) { /* '\f':»»Ò³·ûÒ²ËãÓÐЧ×Ö·û */
+ buffer[i++] = ch1;
+ break;
+ }
+ if(ch1 != '\r') buffer[i++] = ch1; /* ºöÂԻسµ·û */
+ }
+ buffer[i] = '\0';
+ return i+2;
+}
+/*************************************************************
+Function: ·ÖÀëkeyºÍvalue
+ key=val
+ jack = liaoyuewang
+ | | |
+ k1 k2 i
+Input: char *buf
+Output: char **key, char **val
+Return: 1 --- ok
+ 0 --- blank line
+ -1 --- no key, "= val"
+ -2 --- only key, no '='
+Note:
+*************************************************************/
+static int ini_split_key_value(char *buf, char **key, char **val)
+{
+ int i, k1, k2, n;
+
+ if((n = strlen((char *)buf)) < 1)
+ return 0;
+ for(i = 0; i < n; i++)
+ if(buf[i] != ' ' && buf[i] != '\t')
+ break;
+
+ if(i >= n)
+ return 0;
+
+ if(buf[i] == '=')
+ return -1;
+
+ k1 = i;
+ for(i++; i < n; i++)
+ if(buf[i] == '=')
+ break;
+
+ if(i >= n)
+ return -2;
+ k2 = i;
+
+ for(i++; i < n; i++)
+ if(buf[i] != ' ' && buf[i] != '\t')
+ break;
+
+ buf[k2] = '\0';
+
+ *key = buf + k1;
+ *val = buf + i;
+ return 1;
+}
+
+int my_atoi(const char *str)
+{
+ int result = 0;
+ int signal = 1; /* ĬÈÏΪÕýÊý */
+ if((*str>='0'&&*str<='9')||*str=='-'||*str=='+') {
+ if(*str=='-'||*str=='+') {
+ if(*str=='-')
+ signal = -1; /*ÊäÈ븺Êý*/
+ str++;
+ }
+ }
+ else
+ return 0;
+ /*¿ªÊ¼×ª»»*/
+ while(*str>='0' && *str<='9')
+ result = result*10 + (*str++ - '0' );
+
+ return signal*result;
+}
+
+int isspace(int x)
+{
+ if(x==' '||x=='\t'||x=='\n'||x=='\f'||x=='\b'||x=='\r')
+ return 1;
+ else
+ return 0;
+}
+
+int isdigit(int x)
+{
+ if(x<='9' && x>='0')
+ return 1;
+ else
+ return 0;
+
+}
+
+static long atol(char *nptr)
+{
+ int c; /* current char */
+ long total; /* current total */
+ int sign; /* if ''-'', then negative, otherwise positive */
+ /* skip whitespace */
+ while ( isspace((int)(unsigned char)*nptr) )
+ ++nptr;
+ c = (int)(unsigned char)*nptr++;
+ sign = c; /* save sign indication */
+ if (c == '-' || c == '+')
+ c = (int)(unsigned char)*nptr++; /* skip sign */
+ total = 0;
+ while (isdigit(c)) {
+ total = 10 * total + (c - '0'); /* accumulate digit */
+ c = (int)(unsigned char)*nptr++; /* get next char */
+ }
+ if (sign == '-')
+ return -total;
+ else
+ return total; /* return result, negated if necessary */
+}
+/***
+*int atoi(char *nptr) - Convert string to long
+*
+*Purpose:
+* Converts ASCII string pointed to by nptr to binary.
+* Overflow is not detected. Because of this, we can just use
+* atol().
+*
+*Entry:
+* nptr = ptr to string to convert
+*
+*Exit:
+* return int value of the string
+*
+*Exceptions:
+* None - overflow is not detected.
+*
+*******************************************************************************/
+int atoi(char *nptr)
+{
+ return (int)atol(nptr);
+}
+
diff --git a/drivers/input/touchscreen/ft6x0x/ini.h b/drivers/input/touchscreen/ft6x0x/ini.h
new file mode 100755
index 00000000..72434b53
--- /dev/null
+++ b/drivers/input/touchscreen/ft6x0x/ini.h
@@ -0,0 +1,43 @@
+#ifndef INI_H
+#define INI_H
+
+#define MAX_CFG_BUF 512
+#define SUCCESS 0
+/* return value */
+#define CFG_OK SUCCESS
+#define CFG_SECTION_NOT_FOUND -1
+#define CFG_KEY_NOT_FOUND -2
+#define CFG_ERR -10
+
+#define CFG_ERR_OPEN_FILE -10
+#define CFG_ERR_CREATE_FILE -11
+#define CFG_ERR_READ_FILE -12
+#define CFG_ERR_WRITE_FILE -13
+#define CFG_ERR_FILE_FORMAT -14
+
+
+#define CFG_ERR_EXCEED_BUF_SIZE -22
+
+#define COPYF_OK SUCCESS
+#define COPYF_ERR_OPEN_FILE -10
+#define COPYF_ERR_CREATE_FILE -11
+#define COPYF_ERR_READ_FILE -12
+#define COPYF_ERR_WRITE_FILE -13
+
+
+struct ini_key_location {
+ int ini_section_line_no;
+ int ini_key_line_no;
+ int ini_key_lines;
+};
+
+
+int ini_get_key(char *filedata, char * section, char * key, char * value);
+int ini_get_sections(char *filedata, unsigned char * sections[], int max);
+
+int ini_split_section(char *section, char **name, char **index);
+//int ini_join_section(char **section, char *name, char *index);
+
+int atoi(char *nptr);
+
+#endif
diff --git a/drivers/input/touchscreen/fujitsu_ts.c b/drivers/input/touchscreen/fujitsu_ts.c
new file mode 100644
index 00000000..80b21800
--- /dev/null
+++ b/drivers/input/touchscreen/fujitsu_ts.c
@@ -0,0 +1,189 @@
+/*
+ * Fujitsu serial touchscreen driver
+ *
+ * Copyright (c) Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Fujitsu serial touchscreen driver"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define FUJITSU_LENGTH 5
+
+/*
+ * Per-touchscreen data.
+ */
+struct fujitsu {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[FUJITSU_LENGTH];
+ char phys[32];
+};
+
+/*
+ * Decode serial data (5 bytes per packet)
+ * First byte
+ * 1 C 0 0 R S S S
+ * Where C is 1 while in calibration mode (which we don't use)
+ * R is 1 when no coordinate corection was done.
+ * S are button state
+ */
+static irqreturn_t fujitsu_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct fujitsu *fujitsu = serio_get_drvdata(serio);
+ struct input_dev *dev = fujitsu->dev;
+
+ if (fujitsu->idx == 0) {
+ /* resync skip until start of frame */
+ if ((data & 0xf0) != 0x80)
+ return IRQ_HANDLED;
+ } else {
+ /* resync skip garbage */
+ if (data & 0x80) {
+ fujitsu->idx = 0;
+ return IRQ_HANDLED;
+ }
+ }
+
+ fujitsu->data[fujitsu->idx++] = data;
+ if (fujitsu->idx == FUJITSU_LENGTH) {
+ input_report_abs(dev, ABS_X,
+ (fujitsu->data[2] << 7) | fujitsu->data[1]);
+ input_report_abs(dev, ABS_Y,
+ (fujitsu->data[4] << 7) | fujitsu->data[3]);
+ input_report_key(dev, BTN_TOUCH,
+ (fujitsu->data[0] & 0x03) != 2);
+ input_sync(dev);
+ fujitsu->idx = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * fujitsu_disconnect() is the opposite of fujitsu_connect()
+ */
+static void fujitsu_disconnect(struct serio *serio)
+{
+ struct fujitsu *fujitsu = serio_get_drvdata(serio);
+
+ input_get_device(fujitsu->dev);
+ input_unregister_device(fujitsu->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(fujitsu->dev);
+ kfree(fujitsu);
+}
+
+/*
+ * fujitsu_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Fujitsu protocol and registers it
+ * as input device.
+ */
+static int fujitsu_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct fujitsu *fujitsu;
+ struct input_dev *input_dev;
+ int err;
+
+ fujitsu = kzalloc(sizeof(struct fujitsu), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!fujitsu || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ fujitsu->serio = serio;
+ fujitsu->dev = input_dev;
+ snprintf(fujitsu->phys, sizeof(fujitsu->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Fujitsu Serial Touchscreen";
+ input_dev->phys = fujitsu->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_FUJITSU;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 4096, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 4096, 0, 0);
+ serio_set_drvdata(serio, fujitsu);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(fujitsu->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3:
+ serio_close(serio);
+ fail2:
+ serio_set_drvdata(serio, NULL);
+ fail1:
+ input_free_device(input_dev);
+ kfree(fujitsu);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+static struct serio_device_id fujitsu_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_FUJITSU,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, fujitsu_serio_ids);
+
+static struct serio_driver fujitsu_drv = {
+ .driver = {
+ .name = "fujitsu_ts",
+ },
+ .description = DRIVER_DESC,
+ .id_table = fujitsu_serio_ids,
+ .interrupt = fujitsu_interrupt,
+ .connect = fujitsu_connect,
+ .disconnect = fujitsu_disconnect,
+};
+
+static int __init fujitsu_init(void)
+{
+ return serio_register_driver(&fujitsu_drv);
+}
+
+static void __exit fujitsu_exit(void)
+{
+ serio_unregister_driver(&fujitsu_drv);
+}
+
+module_init(fujitsu_init);
+module_exit(fujitsu_exit);
diff --git a/drivers/input/touchscreen/gsl1680_ts/Kconfig b/drivers/input/touchscreen/gsl1680_ts/Kconfig
new file mode 100755
index 00000000..0f47c8bc
--- /dev/null
+++ b/drivers/input/touchscreen/gsl1680_ts/Kconfig
@@ -0,0 +1,16 @@
+#
+# GSL1680 capacity touch screen driver configuration
+#
+config TOUCHSCREEN_GSL1680
+ tristate "ilead GSL1680 I2C Touchscreen Input Driver Support"
+ depends on ARCH_WMT
+ default y
+ help
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_gsl1680.
+
diff --git a/drivers/input/touchscreen/gsl1680_ts/Makefile b/drivers/input/touchscreen/gsl1680_ts/Makefile
new file mode 100755
index 00000000..372a0fce
--- /dev/null
+++ b/drivers/input/touchscreen/gsl1680_ts/Makefile
@@ -0,0 +1,33 @@
+#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_gsl1680
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := gslX680.o wmt_ts.o gsl_point_id.b
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
diff --git a/drivers/input/touchscreen/gsl1680_ts/gslX680.c b/drivers/input/touchscreen/gsl1680_ts/gslX680.c
new file mode 100755
index 00000000..bcb252a5
--- /dev/null
+++ b/drivers/input/touchscreen/gsl1680_ts/gslX680.c
@@ -0,0 +1,1416 @@
+/*
+ * drivers/input/touchscreen/gslX680.c
+ *
+ * Copyright (c) 2012 Shanghai Basewin
+ * Guan Yuwei<guanyuwei@basewin.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+//#include <mach/gpio.h>
+//#include <mach/gpio_data.h>
+#include <linux/jiffies.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#include <linux/pm_runtime.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/input/mt.h>
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/async.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/wakelock.h>
+
+
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+
+
+//#include <asm/irq.h>
+//#include <asm/io.h>
+
+//#include <mach/irqs.h>
+//#include <mach/system.h>
+//#include <mach/hardware.h>
+#include "gslX680.h"
+#include "wmt_ts.h"
+#include "../../../video/backlight/wmt_bl.h"
+
+//#define GSL_DEBUG
+//#define GSL_TIMER
+//#define REPORT_DATA_ANDROID_4_0
+
+#define HAVE_TOUCH_KEY
+
+#define SCREEN_MAX_X 480
+#define SCREEN_MAX_Y 800
+
+
+#define GSLX680_I2C_NAME "touch_gslX680"
+#define GSLX680_I2C_ADDR 0x40
+#define IRQ_PORT INT_GPIO_0
+
+#define GSL_DATA_REG 0x80
+#define GSL_STATUS_REG 0xe0
+#define GSL_PAGE_REG 0xf0
+
+#define PRESS_MAX 255
+#define MAX_FINGERS 5
+#define MAX_CONTACTS 10
+#define DMA_TRANS_LEN 0x20
+
+#ifdef GSL_NOID_VERSION
+int gsl_noid_ver = 0;
+unsigned int gsl_config_data_id[512] = {0};
+#endif
+
+#ifdef HAVE_TOUCH_KEY
+static u16 key = 0;
+static int key_state_flag = 0;
+struct key_data {
+ u16 key;
+ u16 x_min;
+ u16 x_max;
+ u16 y_min;
+ u16 y_max;
+};
+
+const u16 key_array[]={
+ KEY_BACK,
+ KEY_HOME,
+ KEY_MENU,
+ KEY_SEARCH,
+ };
+#define MAX_KEY_NUM (sizeof(key_array)/sizeof(key_array[0]))
+
+struct key_data gsl_key_data[MAX_KEY_NUM] = {
+ {KEY_BACK, 2048, 2048, 2048, 2048},
+ {KEY_HOME, 2048, 2048, 2048, 2048},
+ {KEY_MENU, 2048, 2048, 2048, 2048},
+ {KEY_SEARCH, 2048, 2048, 2048, 2048},
+};
+#endif
+
+struct gsl_ts_data {
+ u8 x_index;
+ u8 y_index;
+ u8 z_index;
+ u8 id_index;
+ u8 touch_index;
+ u8 data_reg;
+ u8 status_reg;
+ u8 data_size;
+ u8 touch_bytes;
+ u8 update_data;
+ u8 touch_meta_data;
+ u8 finger_size;
+};
+
+static struct gsl_ts_data devices[] = {
+ {
+ .x_index = 6,
+ .y_index = 4,
+ .z_index = 5,
+ .id_index = 7,
+ .data_reg = GSL_DATA_REG,
+ .status_reg = GSL_STATUS_REG,
+ .update_data = 0x4,
+ .touch_bytes = 4,
+ .touch_meta_data = 4,
+ .finger_size = 70,
+ },
+};
+
+struct gsl_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct work_struct work;
+ struct workqueue_struct *wq;
+ struct gsl_ts_data *dd;
+ u8 *touch_data;
+ u8 device_id;
+ u8 prev_touches;
+ bool is_suspended;
+ bool int_pending;
+ struct mutex sus_lock;
+// uint32_t gpio_irq;
+ int irq;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+#ifdef GSL_TIMER
+ struct timer_list gsl_timer;
+#endif
+
+ struct workqueue_struct *timeout_queue;
+ struct delayed_work timeout_work;
+ struct mutex timeout_mutex;
+ int timeout_count;
+};
+
+#define DELAY_TIMEOUT 1000
+#define DELAY_TIMEOUT_MAX 3
+
+
+
+struct gsl_ts *l_ts=NULL;
+
+static u32 id_sign[MAX_CONTACTS+1] = {0};
+static u8 id_state_flag[MAX_CONTACTS+1] = {0};
+static u8 id_state_old_flag[MAX_CONTACTS+1] = {0};
+static u16 x_old[MAX_CONTACTS+1] = {0};
+static u16 y_old[MAX_CONTACTS+1] = {0};
+static u16 x_new = 0;
+static u16 y_new = 0;
+
+static struct fw_data* GSLX680_FW = NULL;
+static int l_fwlen = 0;
+static struct task_struct *resume_download_task;
+static struct wake_lock downloadWakeLock;
+static int is_delay = 0;
+
+extern int tp_led_gpio;
+extern int tp_led_gpio_active;
+
+extern int sel_reg_bit;
+extern int sel_reg_active;
+
+
+extern int register_bl_notifier(struct notifier_block *nb);
+
+extern int unregister_bl_notifier(struct notifier_block *nb);
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+////////////////////////////////////////////////////////////////////
+static int wmt_get_fwdata(void);
+
+////////////////////////////////////////////////////////////////////
+static int gslX680_chip_init(void)
+{
+ //gpio_set_status(PAD_GPIOA_6, gpio_status_out);
+ //gpio_out(PAD_GPIOA_6, 1);
+ // shutdown pin
+ wmt_rst_output(1);
+ // irq pin
+ //gpio_set_status(PAD_GPIOA_16, gpio_status_in);
+ //gpio_irq_set(PAD_GPIOA_16, GPIO_IRQ(INT_GPIO_0-INT_GPIO_0, GPIO_IRQ_RISING));
+ wmt_set_gpirq(IRQ_TYPE_EDGE_RISING);//GIRQ_FALLING);
+ wmt_disable_gpirq();
+ msleep(20);
+ return 0;
+}
+
+static int gslX680_shutdown_low(void)
+{
+ //gpio_set_status(PAD_GPIOA_6, gpio_status_out);
+ //gpio_out(PAD_GPIOA_6, 0);
+ wmt_rst_output(0);
+ return 0;
+}
+
+static int gslX680_shutdown_high(void)
+{
+ //gpio_set_status(PAD_GPIOA_6, gpio_status_out);
+ //gpio_out(PAD_GPIOA_6, 1);
+ wmt_rst_output(1);
+ return 0;
+}
+
+static inline u16 join_bytes(u8 a, u8 b)
+{
+ u16 ab = 0;
+ ab = ab | a;
+ ab = ab << 8 | b;
+ return ab;
+}
+
+#if 0
+static u32 gsl_read_interface(struct i2c_client *client, u8 reg, u8 *buf, u32 num)
+{
+ struct i2c_msg xfer_msg[2];
+
+ xfer_msg[0].addr = client->addr;
+ xfer_msg[0].len = 1;
+ xfer_msg[0].flags = client->flags & I2C_M_TEN;
+ xfer_msg[0].buf = &reg;
+
+ xfer_msg[1].addr = client->addr;
+ xfer_msg[1].len = num;
+ xfer_msg[1].flags |= I2C_M_RD;
+ xfer_msg[1].buf = buf;
+
+ if (reg < 0x80) {
+ i2c_transfer(client->adapter, xfer_msg, ARRAY_SIZE(xfer_msg));
+ msleep(5);
+ }
+
+ return i2c_transfer(client->adapter, xfer_msg, ARRAY_SIZE(xfer_msg)) == ARRAY_SIZE(xfer_msg) ? 0 : -EFAULT;
+}
+#endif
+
+static u32 gsl_write_interface(struct i2c_client *client, const u8 reg, u8 *buf, u32 num)
+{
+ struct i2c_msg xfer_msg[1];
+
+ buf[0] = reg;
+
+ xfer_msg[0].addr = client->addr;
+ xfer_msg[0].len = num + 1;
+ xfer_msg[0].flags = client->flags & I2C_M_TEN;
+ xfer_msg[0].buf = buf;
+
+ return i2c_transfer(client->adapter, xfer_msg, 1) == 1 ? 0 : -EFAULT;
+}
+
+static __inline__ void fw2buf(u8 *buf, const u32 *fw)
+{
+ u32 *u32_buf = (int *)buf;
+ *u32_buf = *fw;
+}
+
+static int wmt_get_fwdata(void)
+{
+ char fwname[128];
+
+ // get the firmware file name
+ memset(fwname,0,sizeof(fwname));
+ wmt_ts_get_firmwfilename(fwname);
+ // load the data into GSLX680_FW
+ l_fwlen = read_firmwfile(fwname, &GSLX680_FW, gsl_config_data_id);
+ return ((l_fwlen>0)?0:-1);
+}
+
+static void gsl_load_fw(struct i2c_client *client)
+{
+ u8 buf[DMA_TRANS_LEN*4 + 1] = {0};
+ u8 send_flag = 1;
+ u8 *cur = buf + 1;
+ u32 source_line = 0;
+ u32 source_len = l_fwlen;//ARRAY_SIZE(GSLX680_FW);
+
+ printk("=============gsl_load_fw start==============\n");
+
+ for (source_line = 0; source_line < source_len; source_line++)
+ {
+ /* init page trans, set the page val */
+ if (GSL_PAGE_REG == GSLX680_FW[source_line].offset)
+ {
+ fw2buf(cur, &GSLX680_FW[source_line].val);
+ gsl_write_interface(client, GSL_PAGE_REG, buf, 4);
+ send_flag = 1;
+ }
+ else
+ {
+ if (1 == send_flag % (DMA_TRANS_LEN < 0x20 ? DMA_TRANS_LEN : 0x20))
+ buf[0] = (u8)GSLX680_FW[source_line].offset;
+
+ fw2buf(cur, &GSLX680_FW[source_line].val);
+ cur += 4;
+
+ if (0 == send_flag % (DMA_TRANS_LEN < 0x20 ? DMA_TRANS_LEN : 0x20))
+ {
+ gsl_write_interface(client, buf[0], buf, cur - buf - 1);
+ cur = buf + 1;
+ }
+
+ send_flag++;
+ }
+ }
+
+ printk("=============gsl_load_fw end==============\n");
+
+}
+
+
+static int gsl_ts_write(struct i2c_client *client, u8 addr, u8 *pdata, int datalen)
+{
+ int ret = 0;
+ u8 tmp_buf[128];
+ unsigned int bytelen = 0;
+ if (datalen > 125)
+ {
+ dbg("%s too big datalen = %d!\n", __func__, datalen);
+ return -1;
+ }
+
+ tmp_buf[0] = addr;
+ bytelen++;
+
+ if (datalen != 0 && pdata != NULL)
+ {
+ memcpy(&tmp_buf[bytelen], pdata, datalen);
+ bytelen += datalen;
+ }
+
+ ret = i2c_master_send(client, tmp_buf, bytelen);
+ return ret;
+}
+
+static int gsl_ts_read(struct i2c_client *client, u8 addr, u8 *pdata, unsigned int datalen)
+{
+ int ret = 0;
+
+ if (datalen > 126)
+ {
+ dbg("%s too big datalen = %d!\n", __func__, datalen);
+ return -1;
+ }
+
+ ret = gsl_ts_write(client, addr, NULL, 0);
+ if (ret < 0)
+ {
+ dbg("%s set data address fail!\n", __func__);
+ return ret;
+ }
+
+ return i2c_master_recv(client, pdata, datalen);
+}
+
+#if 0
+static void test_i2c(struct i2c_client *client)
+{
+ u8 read_buf = 0;
+ u8 write_buf = 0x12;
+ int ret;
+ ret = gsl_ts_read( client, 0xf0, &read_buf, sizeof(read_buf) );
+ if (ret < 0)
+ {
+ pr_info("I2C transfer error!\n");
+ }
+ else
+ {
+ pr_info("I read reg 0xf0 is %x\n", read_buf);
+ }
+ msleep(10);
+
+ ret = gsl_ts_write(client, 0xf0, &write_buf, sizeof(write_buf));
+ if (ret < 0)
+ {
+ pr_info("I2C transfer error!\n");
+ }
+ else
+ {
+ pr_info("I write reg 0xf0 0x12\n");
+ }
+ msleep(10);
+
+ ret = gsl_ts_read( client, 0xf0, &read_buf, sizeof(read_buf) );
+ if (ret < 0 )
+ {
+ pr_info("I2C transfer error!\n");
+ }
+ else
+ {
+ pr_info("I read reg 0xf0 is 0x%x\n", read_buf);
+ }
+ msleep(10);
+
+}
+#endif
+
+static int test_i2c(struct i2c_client *client)
+{
+ u8 read_buf = 0;
+ u8 write_buf = 0x12;
+ int ret, rc = 1;
+
+ ret = gsl_ts_read( client, 0xf0, &read_buf, sizeof(read_buf) );
+ if (ret < 0)
+ rc --;
+ else
+ dbg("I read reg 0xf0 is %x\n", read_buf);
+
+ msleep(2);
+ ret = gsl_ts_write(client, 0xf0, &write_buf, sizeof(write_buf));
+ if(ret >= 0 )
+ dbg("I write reg 0xf0 0x12\n");
+
+ msleep(2);
+ ret = gsl_ts_read( client, 0xf0, &read_buf, sizeof(read_buf) );
+ if(ret < 0 )
+ rc --;
+ else
+ dbg("I read reg 0xf0 is 0x%x\n", read_buf);
+
+ return rc;
+}
+
+static void startup_chip(struct i2c_client *client)
+{
+ u8 tmp = 0x00;
+#ifdef GSL_NOID_VERSION
+ if (gsl_noid_ver)
+ gsl_DataInit(gsl_config_data_id);
+#endif
+ gsl_ts_write(client, 0xe0, &tmp, 1);
+ msleep(10);
+}
+
+static void reset_chip(struct i2c_client *client)
+{
+ u8 buf[4] = {0x00};
+ u8 tmp = 0x88;
+ gsl_ts_write(client, 0xe0, &tmp, sizeof(tmp));
+ msleep(10);
+
+ tmp = 0x04;
+ gsl_ts_write(client, 0xe4, &tmp, sizeof(tmp));
+ msleep(10);
+
+ gsl_ts_write(client, 0xbc, buf, sizeof(buf));
+ msleep(10);
+}
+
+static void clr_reg(struct i2c_client *client)
+{
+ u8 write_buf[4] = {0};
+
+ write_buf[0] = 0x88;
+ gsl_ts_write(client, 0xe0, &write_buf[0], 1);
+ msleep(20);
+ write_buf[0] = 0x03;
+ gsl_ts_write(client, 0x80, &write_buf[0], 1);
+ msleep(5);
+ write_buf[0] = 0x04;
+ gsl_ts_write(client, 0xe4, &write_buf[0], 1);
+ msleep(5);
+ write_buf[0] = 0x00;
+ gsl_ts_write(client, 0xe0, &write_buf[0], 1);
+ msleep(20);
+}
+
+static void init_chip(struct i2c_client *client)
+{
+ int rc;
+
+ gslX680_shutdown_low();
+ msleep(20);
+ gslX680_shutdown_high();
+ msleep(20);
+ rc = test_i2c(client);
+ if(rc < 0)
+ {
+ printk("------gslX680 test_i2c error------\n");
+ return;
+ }
+ clr_reg(client);
+ reset_chip(client);
+ gsl_load_fw(client);
+ startup_chip(client);
+ reset_chip(client);
+ startup_chip(client);
+}
+
+static void check_mem_data(struct i2c_client *client)
+{
+ /*char write_buf;
+ char read_buf[4] = {0};
+
+ msleep(30);
+ write_buf = 0x00;
+ gsl_ts_write(client,0xf0, &write_buf, sizeof(write_buf));
+ gsl_ts_read(client,0x00, read_buf, sizeof(read_buf));
+ gsl_ts_read(client,0x00, read_buf, sizeof(read_buf));
+ if (read_buf[3] != 0x1 || read_buf[2] != 0 || read_buf[1] != 0 || read_buf[0] != 0)
+ {
+ dbg("!!!!!!!!!!!page: %x offset: %x val: %x %x %x %x\n",0x0, 0x0, read_buf[3], read_buf[2], read_buf[1], read_buf[0]);
+ init_chip(client);
+ }*/
+
+ u8 read_buf[4] = {0};
+
+ msleep(30);
+ gsl_ts_read(client,0xb0, read_buf, sizeof(read_buf));
+
+ if (read_buf[3] != 0x5a || read_buf[2] != 0x5a || read_buf[1] != 0x5a || read_buf[0] != 0x5a)
+ {
+ printk("#########check mem read 0xb0 = %x %x %x %x #########\n", read_buf[3], read_buf[2], read_buf[1], read_buf[0]);
+ init_chip(client);
+ }
+
+}
+
+static void record_point(u16 x, u16 y , u8 id)
+{
+ u16 x_err =0;
+ u16 y_err =0;
+
+ id_sign[id]=id_sign[id]+1;
+
+ if(id_sign[id]==1){
+ x_old[id]=x;
+ y_old[id]=y;
+ }
+
+ x = (x_old[id] + x)/2;
+ y = (y_old[id] + y)/2;
+
+ if(x>x_old[id]){
+ x_err=x -x_old[id];
+ }
+ else{
+ x_err=x_old[id]-x;
+ }
+
+ if(y>y_old[id]){
+ y_err=y -y_old[id];
+ }
+ else{
+ y_err=y_old[id]-y;
+ }
+
+ if( (x_err > 3 && y_err > 1) || (x_err > 1 && y_err > 3) ){
+ x_new = x; x_old[id] = x;
+ y_new = y; y_old[id] = y;
+ }
+ else{
+ if(x_err > 3){
+ x_new = x; x_old[id] = x;
+ }
+ else
+ x_new = x_old[id];
+ if(y_err> 3){
+ y_new = y; y_old[id] = y;
+ }
+ else
+ y_new = y_old[id];
+ }
+
+ if(id_sign[id]==1){
+ x_new= x_old[id];
+ y_new= y_old[id];
+ }
+
+}
+
+void wmt_set_keypos(int index,int xmin,int xmax,int ymin,int ymax)
+{
+ gsl_key_data[index].x_min = xmin;
+ gsl_key_data[index].x_max = xmax;
+ gsl_key_data[index].y_min = ymin;
+ gsl_key_data[index].y_max = ymax;
+}
+
+#ifdef HAVE_TOUCH_KEY
+static void report_key(struct gsl_ts *ts, u16 x, u16 y)
+{
+ u16 i = 0;
+
+ for(i = 0; i < MAX_KEY_NUM; i++)
+ {
+ if((gsl_key_data[i].x_min <= x) && (x <= gsl_key_data[i].x_max)&&(gsl_key_data[i].y_min <= y) && (y <= gsl_key_data[i].y_max))
+ {
+ key = gsl_key_data[i].key;
+ input_report_key(ts->input, key, 1);
+ input_sync(ts->input);
+ key_state_flag = 1;
+ dbg("rport key:%d\n",key);
+
+ if( tp_led_gpio >= 0 ){
+ gpio_set_value(tp_led_gpio,tp_led_gpio_active);
+
+ mutex_lock(&ts->timeout_mutex);
+ if( ts->timeout_count < 0 ){
+ queue_delayed_work(ts->timeout_queue, &ts->timeout_work, msecs_to_jiffies(DELAY_TIMEOUT));
+ }
+ ts->timeout_count = DELAY_TIMEOUT_MAX;
+ mutex_unlock(&ts->timeout_mutex);
+ }
+ break;
+ }
+ }
+}
+#endif
+
+static void report_data(struct gsl_ts *ts, u16 x, u16 y, u8 pressure, u8 id)
+{
+ //swap(x, y);
+ int tx,ty;
+ int keyx,keyy;
+
+ dbg("#####id=%d,x=%d,y=%d######\n",id,x,y);
+
+
+ tx = x;
+ ty = y;
+ keyx = x;
+ keyy = y;
+
+ if( (x>=wmt_ts_get_resolvX()&&x>=wmt_ts_get_resolvY())
+ || (y>= wmt_ts_get_resolvX()&&y>= wmt_ts_get_resolvY()))
+ {
+ #ifdef HAVE_TOUCH_KEY
+ if (wmt_ts_if_tskey())
+ {
+ report_key(ts,keyx,keyy);
+ }
+ #endif
+ return;
+ }
+
+
+ if (wmt_ts_get_xaxis()==1)
+ {
+ tx = y;
+ ty = x;
+ }
+ if (wmt_ts_get_xdir()==-1)
+ {
+ tx = wmt_ts_get_resolvX() - tx - 1;
+ }
+ if (wmt_ts_get_ydir()==-1)
+ {
+ ty = wmt_ts_get_resolvY() - ty - 1;
+ }
+ /*if ((tx < 0) || (tx >= wmt_ts_get_resolvX()) ||
+ (ty < 0) || (ty >= wmt_ts_get_resolvY()))
+ {
+ dbg("Invalid point(%d,%d)\n");
+ return;
+ }*/
+ x = tx;
+ y = ty;
+
+
+ if (wmt_ts_get_lcdexchg()) {
+ int tmp;
+ tmp = x;
+ x = y;
+ y = wmt_ts_get_resolvX() - tmp;
+ }
+
+ dbg("rpt%d(%d,%d)\n",id,x,y);
+#ifdef REPORT_DATA_ANDROID_4_0
+ input_mt_slot(ts->input, id);
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
+ //input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, pressure);
+ input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+ //input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, 1);
+#else
+ //add for cross finger 2013-1-10
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
+ //input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, pressure);
+ input_report_abs(ts->input, ABS_MT_POSITION_X,x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+ //input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(ts->input);
+#endif
+}
+
+static void process_gslX680_data(struct gsl_ts *ts)
+{
+ u8 id, touches;
+ u16 x, y;
+ int i = 0;
+#ifdef GSL_NOID_VERSION
+ u32 tmp1;
+ u8 buf[4] = {0};
+ struct gsl_touch_info cinfo;
+#endif
+ touches = ts->touch_data[ts->dd->touch_index];
+
+#ifdef GSL_NOID_VERSION
+ if (gsl_noid_ver) {
+ cinfo.finger_num = touches;
+ dbg("tp-gsl finger_num = %d\n",cinfo.finger_num);
+ for(i = 0; i < (touches < MAX_CONTACTS ? touches : MAX_CONTACTS); i ++) {
+ cinfo.x[i] = join_bytes( ( ts->touch_data[ts->dd->x_index + 4 * i + 1] & 0xf),
+ ts->touch_data[ts->dd->x_index + 4 * i]);
+ cinfo.y[i] = join_bytes(ts->touch_data[ts->dd->y_index + 4 * i + 1],
+ ts->touch_data[ts->dd->y_index + 4 * i ]);
+ cinfo.id[i] = ((ts->touch_data[ts->dd->x_index + 4 * i + 1] & 0xf0)>>4);
+ dbg("tp-gsl x = %d y = %d \n",cinfo.x[i],cinfo.y[i]);
+ }
+ cinfo.finger_num=(ts->touch_data[3]<<24)|(ts->touch_data[2]<<16)
+ |(ts->touch_data[1]<<8)|(ts->touch_data[0]);
+ gsl_alg_id_main(&cinfo);
+ tmp1=gsl_mask_tiaoping();
+ dbg("[tp-gsl] tmp1=%x\n",tmp1);
+ if(tmp1>0&&tmp1<0xffffffff) {
+ buf[0]=0xa;buf[1]=0;buf[2]=0;buf[3]=0;
+ gsl_ts_write(ts->client,0xf0,buf,4);
+ buf[0]=(u8)(tmp1 & 0xff);
+ buf[1]=(u8)((tmp1>>8) & 0xff);
+ buf[2]=(u8)((tmp1>>16) & 0xff);
+ buf[3]=(u8)((tmp1>>24) & 0xff);
+ dbg("tmp1=%08x,buf[0]=%02x,buf[1]=%02x,buf[2]=%02x,buf[3]=%02x\n",
+ tmp1,buf[0],buf[1],buf[2],buf[3]);
+ gsl_ts_write(ts->client,0x8,buf,4);
+ }
+ touches = cinfo.finger_num;
+ }
+#endif
+
+ for(i=1;i<=MAX_CONTACTS;i++)
+ {
+ if(touches == 0)
+ id_sign[i] = 0;
+ id_state_flag[i] = 0;
+ }
+ for(i= 0;i < (touches > MAX_FINGERS ? MAX_FINGERS : touches);i ++)
+ {
+ #ifdef GSL_NOID_VERSION
+ if (gsl_noid_ver) {
+ id = cinfo.id[i];
+ x = cinfo.x[i];
+ y = cinfo.y[i];
+ }
+ else {
+ x = join_bytes( ( ts->touch_data[ts->dd->x_index + 4 * i + 1] & 0xf),
+ ts->touch_data[ts->dd->x_index + 4 * i]);
+ y = join_bytes(ts->touch_data[ts->dd->y_index + 4 * i + 1],
+ ts->touch_data[ts->dd->y_index + 4 * i ]);
+ id = ts->touch_data[ts->dd->id_index + 4 * i] >> 4;
+ }
+ #endif
+
+ if(1 <=id && id <= MAX_CONTACTS)
+ {
+ dbg("raw%d(%d,%d)\n", id, x, y);
+ record_point(x, y , id);
+ dbg("new%d(%d,%d)\n", id, x_new, y_new);
+ report_data(ts, x_new, y_new, 10, id);
+ id_state_flag[id] = 1;
+ }
+ }
+ for(i=1;i<=MAX_CONTACTS;i++)
+ {
+ if( (0 == touches) || ((0 != id_state_old_flag[i]) && (0 == id_state_flag[i])) )
+ {
+ #ifdef REPORT_DATA_ANDROID_4_0
+ input_mt_slot(ts->input, i);
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, false);
+ #endif
+ id_sign[i]=0;
+ }
+ id_state_old_flag[i] = id_state_flag[i];
+ }
+#ifndef REPORT_DATA_ANDROID_4_0
+ if(0 == touches)
+ {
+ //add 2013-1-10 cross fingers
+ //input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1);
+ input_report_key(ts->input, BTN_TOUCH, 0);
+ //**********************
+ input_mt_sync(ts->input);
+ #ifdef HAVE_TOUCH_KEY
+ if (wmt_ts_if_tskey())
+ {
+ if(key_state_flag)
+ {
+ input_report_key(ts->input, key, 0);
+ input_sync(ts->input);
+ key_state_flag = 0;
+ }
+ }
+ #endif
+ }
+#endif
+ input_sync(ts->input);
+ ts->prev_touches = touches;
+}
+
+
+static void gsl_ts_xy_worker(struct work_struct *work)
+{
+ int rc;
+ u8 read_buf[4] = {0};
+
+ struct gsl_ts *ts = container_of(work, struct gsl_ts,work);
+
+ dbg("---gsl_ts_xy_worker---\n");
+
+ if (ts->is_suspended == true) {
+ dev_dbg(&ts->client->dev, "TS is supended\n");
+ ts->int_pending = true;
+ goto schedule;
+ }
+
+ /* read data from DATA_REG */
+ rc = gsl_ts_read(ts->client, 0x80, ts->touch_data, ts->dd->data_size);
+ dbg("---touches: %d ---\n",ts->touch_data[0]);
+
+ if (rc < 0)
+ {
+ dev_err(&ts->client->dev, "read failed\n");
+ goto schedule;
+ }
+
+ if (ts->touch_data[ts->dd->touch_index] == 0xff) {
+ goto schedule;
+ }
+
+ rc = gsl_ts_read( ts->client, 0xbc, read_buf, sizeof(read_buf));
+ if (rc < 0)
+ {
+ dev_err(&ts->client->dev, "read 0xbc failed\n");
+ goto schedule;
+ }
+ dbg("//////// reg %x : %x %x %x %x\n",0xbc, read_buf[3], read_buf[2], read_buf[1], read_buf[0]);
+
+ if (read_buf[3] == 0 && read_buf[2] == 0 && read_buf[1] == 0 && read_buf[0] == 0)
+ {
+ process_gslX680_data(ts);
+ }
+ else
+ {
+ reset_chip(ts->client);
+ startup_chip(ts->client);
+ }
+
+schedule:
+ //enable_irq(ts->irq);
+ wmt_enable_gpirq();
+
+}
+
+static irqreturn_t gsl_ts_irq(int irq, void *dev_id)
+{
+ struct gsl_ts *ts = dev_id;
+
+ if (wmt_is_tsint())
+ {
+ wmt_clr_int();
+ if (wmt_is_tsirq_enable() && ts->is_suspended == false)
+ {
+ wmt_disable_gpirq();
+ dbg("begin..\n");
+ //if (!work_pending(&l_tsdata.pen_event_work))
+ {
+ queue_work(ts->wq, &ts->work);
+ }
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+
+
+ /*disable_irq_nosync(ts->irq);
+
+ if (!work_pending(&ts->work))
+ {
+ queue_work(ts->wq, &ts->work);
+ }
+
+ return IRQ_HANDLED;*/
+
+}
+
+#ifdef GSL_TIMER
+static void gsl_timer_handle(unsigned long data)
+{
+ struct gsl_ts *ts = (struct gsl_ts *)data;
+
+#ifdef GSL_DEBUG
+ dbg("----------------gsl_timer_handle-----------------\n");
+#endif
+
+ disable_irq_nosync(ts->irq);
+ check_mem_data(ts->client);
+ ts->gsl_timer.expires = jiffies + 3 * HZ;
+ add_timer(&ts->gsl_timer);
+ enable_irq(ts->irq);
+
+}
+#endif
+
+static int gsl_ts_init_ts(struct i2c_client *client, struct gsl_ts *ts)
+{
+ struct input_dev *input_device;
+ int i;
+ int rc = 0;
+
+ dbg("[GSLX680] Enter %s\n", __func__);
+
+
+ ts->dd = &devices[ts->device_id];
+
+ if (ts->device_id == 0) {
+ ts->dd->data_size = MAX_FINGERS * ts->dd->touch_bytes + ts->dd->touch_meta_data;
+ ts->dd->touch_index = 0;
+ }
+
+ ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+ if (!ts->touch_data) {
+ pr_err("%s: Unable to allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ ts->prev_touches = 0;
+
+ input_device = input_allocate_device();
+ if (!input_device) {
+ rc = -ENOMEM;
+ goto error_alloc_dev;
+ }
+
+ ts->input = input_device;
+ input_device->name = GSLX680_I2C_NAME;
+ input_device->id.bustype = BUS_I2C;
+ input_device->dev.parent = &client->dev;
+ input_set_drvdata(input_device, ts);
+
+#ifdef REPORT_DATA_ANDROID_4_0
+ __set_bit(EV_ABS, input_device->evbit);
+ __set_bit(EV_KEY, input_device->evbit);
+ __set_bit(EV_REP, input_device->evbit);
+ __set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+ input_mt_init_slots(input_device, (MAX_CONTACTS+1));
+#else
+ //input_set_abs_params(input_device,ABS_MT_TRACKING_ID, 0, (MAX_CONTACTS+1), 0, 0);
+ set_bit(EV_ABS, input_device->evbit);
+ set_bit(EV_KEY, input_device->evbit);
+ __set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+#endif
+
+ input_device->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+#ifdef HAVE_TOUCH_KEY
+ //input_device->evbit[0] = BIT_MASK(EV_KEY);
+ if (wmt_ts_if_tskey())
+ {
+ for (i = 0; i < MAX_KEY_NUM; i++)
+ set_bit(key_array[i], input_device->keybit);
+ }
+#endif
+
+ set_bit(ABS_MT_POSITION_X, input_device->absbit);
+ set_bit(ABS_MT_POSITION_Y, input_device->absbit);
+ //set_bit(ABS_MT_TOUCH_MAJOR, input_device->absbit);
+ //set_bit(ABS_MT_WIDTH_MAJOR, input_device->absbit);
+ //****************add 2013-1-10
+ set_bit(BTN_TOUCH, input_device->keybit);
+ set_bit(ABS_MT_TRACKING_ID, input_device->absbit);
+
+ dbg("regsister:x=%d,y=%d\n",wmt_ts_get_resolvX(),wmt_ts_get_resolvY());
+ if (wmt_ts_get_lcdexchg()) {
+ input_set_abs_params(input_device,ABS_MT_POSITION_X, 0, wmt_ts_get_resolvY(), 0, 0);
+ input_set_abs_params(input_device,ABS_MT_POSITION_Y, 0, wmt_ts_get_resolvX(), 0, 0);
+ } else {
+ input_set_abs_params(input_device,ABS_MT_POSITION_X, 0, wmt_ts_get_resolvX(), 0, 0);
+ input_set_abs_params(input_device,ABS_MT_POSITION_Y, 0, wmt_ts_get_resolvY(), 0, 0);
+ }
+ //input_set_abs_params(input_device,ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
+ //input_set_abs_params(input_device,ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
+
+ //client->irq = IRQ_PORT,
+ //ts->irq = client->irq;
+
+ ts->wq = create_singlethread_workqueue("kworkqueue_ts");
+ if (!ts->wq) {
+ dev_err(&client->dev, "Could not create workqueue\n");
+ goto error_wq_create;
+ }
+ flush_workqueue(ts->wq);
+
+ INIT_WORK(&ts->work, gsl_ts_xy_worker);
+
+ rc = input_register_device(input_device);
+ if (rc)
+ goto error_unreg_device;
+
+
+
+ return 0;
+
+error_unreg_device:
+ destroy_workqueue(ts->wq);
+error_wq_create:
+ input_free_device(input_device);
+error_alloc_dev:
+ kfree(ts->touch_data);
+ return rc;
+}
+
+static int gsl_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct gsl_ts *ts = l_ts; //dev_get_drvdata(dev);
+ //int rc = 0;
+
+ printk("I'am in gsl_ts_suspend() start\n");
+ ts->is_suspended = true;
+
+#ifdef GSL_TIMER
+ printk( "gsl_ts_suspend () : delete gsl_timer\n");
+
+ del_timer(&ts->gsl_timer);
+#endif
+ //disable_irq_nosync(ts->irq);
+ wmt_disable_gpirq();
+
+ reset_chip(ts->client);
+ gslX680_shutdown_low();
+ msleep(10);
+
+ return 0;
+}
+
+int resume_download_thread(void *arg)
+{
+ wake_lock(&downloadWakeLock);
+ gslX680_chip_init();
+ gslX680_shutdown_high();
+ msleep(20);
+ reset_chip(l_ts->client);
+ startup_chip(l_ts->client);
+ //check_mem_data(l_ts->client);
+ init_chip(l_ts->client);
+
+ l_ts->is_suspended = false;
+ if (!earlysus_en)
+ wmt_enable_gpirq();
+ wake_unlock(&downloadWakeLock);
+ return 0;
+}
+
+static int gsl_ts_resume(struct platform_device *pdev)
+{
+
+ gpio_direction_output(tp_led_gpio, !tp_led_gpio_active);
+#ifdef GSL_TIMER
+ dbg( "gsl_ts_resume () : add gsl_timer\n");
+
+ init_timer(&ts->gsl_timer);
+ ts->gsl_timer.expires = jiffies + 3 * HZ;
+ ts->gsl_timer.function = &gsl_timer_handle;
+ ts->gsl_timer.data = (unsigned long)ts;
+ add_timer(&ts->gsl_timer);
+#endif
+ if (is_delay) {
+ resume_download_task = kthread_create(resume_download_thread, NULL , "resume_download");
+ if(IS_ERR(resume_download_task)) {
+ errlog("cread thread failed\n");
+ }
+ wake_up_process(resume_download_task);
+ } else
+ resume_download_thread(NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void gsl_ts_early_suspend(struct early_suspend *h)
+{
+ //struct gsl_ts *ts = container_of(h, struct gsl_ts, early_suspend);
+ dbg("[GSL1680] Enter %s\n", __func__);
+ //gsl_ts_suspend(&ts->client->dev);
+ wmt_disable_gpirq();
+}
+
+static void gsl_ts_late_resume(struct early_suspend *h)
+{
+ //struct gsl_ts *ts = container_of(h, struct gsl_ts, early_suspend);
+ dbg("[GSL1680] Enter %s\n", __func__);
+ //gsl_ts_resume(&ts->client->dev);
+ wmt_enable_gpirq();
+}
+#endif
+
+static void check_Backlight_delay(void)
+{
+ int ret;
+ int len = 7;
+ char retval[8];
+
+ ret = wmt_getsyspara("wmt.backlight.delay", retval, &len);
+ if(ret) {
+ dbg("Read wmt.backlight.delay Failed.\n");
+ is_delay = 0;
+ } else
+ is_delay = 1;
+}
+
+static void timeout_func(struct work_struct *work){
+ struct gsl_ts *ts =
+ container_of(work, struct gsl_ts, timeout_work.work);
+ int button_up = -1;
+
+ mutex_lock(&ts->timeout_mutex);
+ button_up = --ts->timeout_count;
+ mutex_unlock(&ts->timeout_mutex);
+
+ if( button_up < 0){
+ gpio_set_value(tp_led_gpio,!tp_led_gpio_active);
+ }else{
+ queue_delayed_work(ts->timeout_queue, &ts->timeout_work, msecs_to_jiffies(DELAY_TIMEOUT));
+ }
+
+}
+
+
+static int gsl_ts_probe(struct i2c_client *client)
+{
+ struct gsl_ts *ts;
+ int rc;
+
+ dbg("GSLX680 Enter %s\n", __func__);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C functionality not supported\n");
+ return -ENODEV;
+ }
+ if (wmt_get_fwdata())
+ {
+ errlog("Failed to load the firmware data!\n");
+ return -1;
+ }
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts) {
+ rc = -ENOMEM;
+ goto error_kfree_fw;
+ }
+ dbg("==kzalloc success=\n");
+ l_ts = ts;
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+ ts->device_id = 0;//id->driver_data;
+
+ ts->is_suspended = false;
+ ts->int_pending = false;
+ wake_lock_init(&downloadWakeLock, WAKE_LOCK_SUSPEND, "resume_download");
+ mutex_init(&ts->sus_lock);
+
+ rc = gsl_ts_init_ts(client, ts);
+ if (rc < 0) {
+ dev_err(&client->dev, "GSLX680 init failed\n");
+ goto error_mutex_destroy;
+ }
+
+ gslX680_chip_init();
+ init_chip(ts->client);
+ check_mem_data(ts->client);
+
+ ts->irq = wmt_get_tsirqnum();
+ rc= request_irq(wmt_get_tsirqnum(), gsl_ts_irq, IRQF_SHARED, client->name, ts);
+ if (rc < 0) {
+ dbg( "gsl_probe: request irq failed\n");
+ goto error_req_irq_fail;
+ }
+
+ if( tp_led_gpio >= 0 ){
+ if(gpio_request(tp_led_gpio, "tp_led_gpio") >= 0){
+ wmt_gpio_setpull(tp_led_gpio, tp_led_gpio_active ? WMT_GPIO_PULL_UP : WMT_GPIO_PULL_DOWN);
+ gpio_direction_output(tp_led_gpio, !tp_led_gpio_active);
+ #if 0
+ errlog("sel_reg_bit:%d sel_reg_active:%d\n",sel_reg_bit,sel_reg_active);
+ if(sel_reg_bit >= 0 && sel_reg_active >= 0){
+ if(sel_reg_active == 0){
+ REG32_VAL(__GPIO_BASE+PIN_SHARING_SEL_OFFSET) &= ~(1<<sel_reg_bit);
+ }else {
+ REG32_VAL(__GPIO_BASE+PIN_SHARING_SEL_OFFSET) |= (1<<sel_reg_bit);
+ }
+ }
+ #endif
+ }else{
+ errlog(" touch panel led gpio request failed !! \n");
+ goto error_req_irq_fail;
+ }
+
+ mutex_init(&ts->timeout_mutex);
+ ts->timeout_count = -1;
+ ts->timeout_queue= create_singlethread_workqueue("timeout_queue");
+ INIT_DELAYED_WORK(&ts->timeout_work,timeout_func);
+
+ }
+
+
+
+
+#ifdef GSL_TIMER
+ dbg( "gsl_ts_probe () : add gsl_timer\n");
+
+ init_timer(&ts->gsl_timer);
+ ts->gsl_timer.expires = jiffies + 3 * HZ; //¶¨Ê±3 ÃëÖÓ
+ ts->gsl_timer.function = &gsl_timer_handle;
+ ts->gsl_timer.data = (unsigned long)ts;
+ add_timer(&ts->gsl_timer);
+#endif
+
+ /* create debug attribute */
+ //rc = device_create_file(&ts->input->dev, &dev_attr_debug_enable);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = gsl_ts_early_suspend;
+ ts->early_suspend.resume = gsl_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ dbg("[GSLX680] End %s\n", __func__);
+ wmt_enable_gpirq();
+
+ check_Backlight_delay();
+ return 0;
+
+
+
+error_req_irq_fail:
+ free_irq(ts->irq, ts);
+
+error_mutex_destroy:
+ mutex_destroy(&ts->sus_lock);
+ wake_lock_destroy(&downloadWakeLock);
+ input_free_device(ts->input);
+ kfree(ts);
+error_kfree_fw:
+ kfree(GSLX680_FW);
+ return rc;
+}
+
+static int gsl_ts_remove(struct i2c_client *client)
+{
+
+
+ struct gsl_ts *ts = i2c_get_clientdata(client);
+ dbg("==gsl_ts_remove=\n");
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+
+ //device_init_wakeup(&client->dev, 0);
+ cancel_work_sync(&ts->work);
+
+ if( tp_led_gpio >= 0 ){
+ mutex_destroy(&ts->timeout_mutex);
+ gpio_free(tp_led_gpio);
+ tp_led_gpio = -1;
+ tp_led_gpio_active = -1;
+ }
+
+ free_irq(ts->irq, ts);
+ destroy_workqueue(ts->wq);
+ input_unregister_device(ts->input);
+ mutex_destroy(&ts->sus_lock);
+ wake_lock_destroy(&downloadWakeLock);
+
+ //device_remove_file(&ts->input->dev, &dev_attr_debug_enable);
+
+ if(GSLX680_FW){
+ kfree(GSLX680_FW);
+ GSLX680_FW = NULL;
+ }
+
+ kfree(ts->touch_data);
+ kfree(ts);
+
+ return 0;
+}
+
+/*
+static const struct i2c_device_id gsl_ts_id[] = {
+ {GSLX680_I2C_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, gsl_ts_id);
+
+
+static struct i2c_driver gsl_ts_driver = {
+ .driver = {
+ .name = GSLX680_I2C_NAME,
+ .owner = THIS_MODULE,
+ },
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = gsl_ts_suspend,
+ .resume = gsl_ts_resume,
+#endif
+ .probe = gsl_ts_probe,
+ .remove = __devexit_p(gsl_ts_remove),
+ .id_table = gsl_ts_id,
+};
+*/
+static int wmt_wakeup_bl_notify(struct notifier_block *nb, unsigned long event,
+ void *dummy)
+{
+ //printk("get notify\n");
+ switch (event) {
+ case BL_CLOSE:
+ l_ts->is_suspended = true;
+ //printk("\nclose backlight\n\n");
+ //printk("disable irq\n\n");
+ wmt_disable_gpirq();
+ break;
+ case BL_OPEN:
+ l_ts->is_suspended = false;
+ //printk("\nopen backlight\n\n");
+ //printk("enable irq\n\n");
+ wmt_enable_gpirq();
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wmt_bl_notify = {
+ .notifier_call = wmt_wakeup_bl_notify,
+};
+
+static int gsl_ts_init(void)
+{
+ int ret = 0;
+ ret = gsl_ts_probe(ts_get_i2c_client());
+ if (ret)
+ {
+ dbg("Can't load gsl1680 ts driver!\n");
+ }
+ //dbg("ret=%d\n",ret);
+ if (earlysus_en)
+ register_bl_notifier(&wmt_bl_notify);
+ return ret;
+}
+static void gsl_ts_exit(void)
+{
+ dbg("==gsl_ts_exit==\n");
+ //i2c_del_driver(&gsl_ts_driver);
+ gsl_ts_remove(ts_get_i2c_client());
+ if (earlysus_en)
+ unregister_bl_notifier(&wmt_bl_notify);
+ return;
+}
+
+struct wmtts_device gslx680_tsdev = {
+ .driver_name = WMT_TS_I2C_NAME,
+ .ts_id = "GSL1680",
+ .init = gsl_ts_init,
+ .exit = gsl_ts_exit,
+ .suspend = gsl_ts_suspend,
+ .resume = gsl_ts_resume,
+};
+
+
+//module_init(gsl_ts_init);
+//module_exit(gsl_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GSLX680 touchscreen controller driver");
+MODULE_AUTHOR("Guan Yuwei, guanyuwei@basewin.com");
+MODULE_ALIAS("platform:gsl_ts");
diff --git a/drivers/input/touchscreen/gsl1680_ts/gslX680.h b/drivers/input/touchscreen/gsl1680_ts/gslX680.h
new file mode 100755
index 00000000..c146127c
--- /dev/null
+++ b/drivers/input/touchscreen/gsl1680_ts/gslX680.h
@@ -0,0 +1,35 @@
+#ifndef _GSLX680_H_
+#define _GSLX680_H_
+
+
+
+#define GSL_NOID_VERSION
+#ifdef GSL_NOID_VERSION
+struct gsl_touch_info
+{
+ int x[10];
+ int y[10];
+ int id[10];
+ int finger_num;
+};
+extern unsigned int gsl_mask_tiaoping(void);
+extern unsigned int gsl_version_id(void);
+extern void gsl_alg_id_main(struct gsl_touch_info *cinfo);
+extern void gsl_DataInit(int *ret);
+
+extern int gsl_noid_ver;
+extern unsigned int gsl_config_data_id[512];
+#endif
+
+struct fw_data
+{
+ //u32 offset : 8;
+ //u32 : 0;
+ u32 offset;
+ u32 val;
+};
+
+extern void wmt_set_keypos(int index,int xmin,int xmax,int ymin,int ymax);
+
+
+#endif
diff --git a/drivers/input/touchscreen/gsl1680_ts/gsl_point_id.b b/drivers/input/touchscreen/gsl1680_ts/gsl_point_id.b
new file mode 100755
index 00000000..f25fccd3
--- /dev/null
+++ b/drivers/input/touchscreen/gsl1680_ts/gsl_point_id.b
Binary files differ
diff --git a/drivers/input/touchscreen/gsl1680_ts/wmt_ts.c b/drivers/input/touchscreen/gsl1680_ts/wmt_ts.c
new file mode 100755
index 00000000..e0ddf9e7
--- /dev/null
+++ b/drivers/input/touchscreen/gsl1680_ts/wmt_ts.c
@@ -0,0 +1,1102 @@
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+//#include <asm/semaphore.h>
+#include <linux/proc_fs.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+
+#include "gslX680.h"
+#include "wmt_ts.h"
+
+/////////////////////////////////////////////////////////////////
+
+// commands for ui
+#define TS_IOC_MAGIC 't'
+
+#define TS_IOCTL_CAL_START _IO(TS_IOC_MAGIC, 1)
+#define TS_IOCTL_CAL_DONE _IOW(TS_IOC_MAGIC, 2, int*)
+#define TS_IOCTL_GET_RAWDATA _IOR(TS_IOC_MAGIC, 3, int*)
+#define TS_IOCTL_CAL_QUIT _IOW(TS_IOC_MAGIC, 4, int*)
+#define TS_IOCTL_AUTO_CALIBRATION _IOW(TS_IOC_MAGIC, 5, int*)
+#define TS_IOC_MAXNR 5
+
+//
+#define TS_MAJOR 11
+#define TS_DRIVER_NAME "wmtts_touch"
+#define TS_NAME "wmtts"
+#define WMTTS_PROC_NAME "wmtts_config"
+
+#define EXT_GPIO0 0
+#define EXT_GPIO1 1
+#define EXT_GPIO2 2
+#define EXT_GPIO3 3
+#define EXT_GPIO4 4
+#define EXT_GPIO5 5
+#define EXT_GPIO6 6
+#define EXT_GPIO7 7
+
+typedef struct {
+ int a1;
+ int b1;
+ int c1;
+ int a2;
+ int b2;
+ int c2;
+ int delta;
+}CALIBRATION_PARAMETER, *PCALIBRATION_PARAMETER;
+
+
+static int irq_gpio;
+static int rst_gpio;
+static int panelres_x;
+static int panelres_y;
+static int lcd_exchg = 0;
+static DECLARE_WAIT_QUEUE_HEAD(queue);
+static CALIBRATION_PARAMETER g_CalcParam;
+static TS_EVENT g_evLast;
+static struct mutex cal_mutex;
+static DECLARE_WAIT_QUEUE_HEAD(ts_penup_wait_queue);
+
+static struct class* l_dev_class = NULL;
+static struct device *l_clsdevice = NULL;
+extern struct wmtts_device gslx680_tsdev;
+static struct wmtts_device* l_tsdev = &gslx680_tsdev;
+struct proc_dir_entry* l_tsproc = NULL;
+static struct i2c_client *l_client=NULL;
+static int l_penup = 0; // 1-pen up,0-pen down
+static int l_iftskey = -1; // 1:have ts key
+int earlysus_en = 0;
+
+int tp_led_gpio;
+int tp_led_gpio_active;
+
+int sel_reg_bit;
+int sel_reg_active;
+
+
+struct tp_infor
+{
+ //enum tp_type type;
+ char name[64];
+ int i2caddr;
+ int xaxis; //0: x,1: x swap with y
+ int xdir; // 1: positive,-1: revert
+ int ydir; // 1: positive,-1: revert
+ int finger_num;
+};
+
+static int l_tpindex = -1;
+static struct tp_infor l_tpinfor[1];
+
+
+/////////////////////////////////////////////////////
+// function declare
+/////////////////////////////////////////////////////
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data );
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+///////////////////////////////////////////////////////////////////////
+void TouchPanelCalibrateAPoint(
+ int UncalX, //@PARM The uncalibrated X coordinate
+ int UncalY, //@PARM The uncalibrated Y coordinate
+ int *pCalX, //@PARM The calibrated X coordinate
+ int *pCalY //@PARM The calibrated Y coordinate
+ )
+{
+ int x, y;
+ mutex_lock(&cal_mutex);
+ x = (g_CalcParam.a1 * UncalX + g_CalcParam.b1 * UncalY +
+ g_CalcParam.c1) / g_CalcParam.delta;
+ y = (g_CalcParam.a2 * UncalX + g_CalcParam.b2 * UncalY +
+ g_CalcParam.c2) / g_CalcParam.delta;
+
+//klog("afer(%d,%d)(%d,%d)\n", x,y,panelres_x,panelres_y);
+ if ( x < 0 )
+ x = 0;
+
+ if ( y < 0 )
+ y = 0;
+ if (x >= panelres_x)
+ x = panelres_x-1;
+ if (y >= panelres_y)
+ y = panelres_y-1;
+
+ *pCalX = x;
+ *pCalY = y;
+ mutex_unlock(&cal_mutex);
+ return;
+}
+
+int wmt_ts_if_tskey(void)
+{
+ return ((l_iftskey==1) ? 1: 0);
+}
+
+int wmt_ts_get_firmwfilename(char* fname)
+{
+ sprintf(fname,"%s_fw.tpf",l_tpinfor[l_tpindex].name);
+ return 0;
+}
+
+
+static struct device* get_tp_device(void){
+ if(l_client == NULL){
+ errlog("l_client is NULL\n");
+ }
+ return &l_client->dev;
+}
+
+
+unsigned int wmt_ts_get_xaxis(void)
+{
+ return l_tpinfor[l_tpindex].xaxis;
+}
+
+unsigned int wmt_ts_get_xdir(void)
+{
+ return l_tpinfor[l_tpindex].xdir;
+}
+
+unsigned int wmt_ts_get_ydir(void)
+{
+ return l_tpinfor[l_tpindex].ydir;
+}
+
+static int parse_firmwarefile(const char* filedata, struct fw_data** firmarr, unsigned int* id_config)
+{
+ char endflag[]="/* End flag */";
+ const char *p = filedata;
+ u32 val[2];
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ const char *s = NULL;
+ int hex = 0;
+
+#ifdef GSL_NOID_VERSION
+ if (gsl_noid_ver) {
+ while (*p != '{') p++;
+ p++;
+ s = p;
+ while (*s != '}') {
+ if (*s == '0' && *(s+1) == 'x') {
+ hex = 1;
+ break;
+ }
+ s++;
+ }
+ while (*p != '}') {
+ sscanf(p,hex ? "%x" : "%u,",&(id_config[k++]));
+ p = strchr(p,',');p++;
+ }
+ }
+#endif
+
+ // the first {
+ while (*p!='{') p++;
+ p++;
+ s = p;
+ // calculate the number of array
+ while (strncmp(p,endflag,strlen(endflag)))
+ {
+ if (*p=='{')
+ {
+ i++;
+ }
+ p++;
+ };
+ dbg("the number of arry:0x%x\n", i);
+ // alloc the memory for array
+ *firmarr = kzalloc(sizeof(struct fw_data)*i, GFP_KERNEL);
+ // parse the value of array
+ p = s;
+ j = 0;
+ while (strncmp(p,endflag,strlen(endflag)))
+ {
+ if (*p=='{')
+ {
+ memset(val,0,sizeof(val));
+ sscanf(p,"{%x,%x}",val,val+1);
+ (*firmarr)[j].offset = val[0]&0x00FF;
+ (*firmarr)[j].val= val[1];
+ //dbg("data%x{%x,%x}\n",j,(*firmarr)[j].offset,(*firmarr)[j].val);
+ j++;
+ }
+ //p = strchr(p,'}');
+ p++;
+ if (j>=i-2)
+ {
+ dbg("%s",p);
+ }
+
+ };
+ if (i != j)
+ {
+ errlog("Error parsing file(the number of arry not match)!\n");
+ errlog("i=0x%x,j=0x%x\n", i,j);
+ kfree(*firmarr);
+ return -1;
+ };
+ dbg("paring firmware file end.\n");
+ return i;
+}
+
+
+
+//filepath:the path of firmware file;
+//firmdata:store the data from firmware file;
+//id_config:config data for some ICs whitch have no id;
+//return:the lengthen of firmware data,negative-parsing error.
+int read_firmwfile(char* filepath, struct fw_data** firmdata, unsigned int* id_config)
+{
+ const u8 *data = NULL;
+ int i = 0;
+ int ret = -1;
+ const struct firmware* tpfirmware = NULL;
+
+ klog("ts firmware file:%s\n",filepath);
+ ret = request_firmware(&tpfirmware, filepath, get_tp_device());
+ if (ret < 0) {
+ errlog("Failed load tp firmware: %s ret=%d\n", filepath,ret);
+ goto err_end;
+ }
+ data = tpfirmware->data;
+
+ i = parse_firmwarefile(data,firmdata,id_config);
+
+ if (i <= 0){
+ dbg("Failed to pare firmware file!\n");
+ ret = -1;
+ goto error_parse_fw;
+ }
+ dbg("firmware arry len=0x%x\n", i);
+ ret = i;
+ dbg("success to read firmware file!\n");;
+error_parse_fw:
+ if(tpfirmware){
+ release_firmware(tpfirmware);
+ tpfirmware = NULL;
+ }
+err_end:
+ return ret;
+}
+
+
+
+ int wmt_ts_get_gpionum(void)
+{
+ return irq_gpio;
+}
+
+int wmt_ts_get_resetgpnum(void)
+{
+ return rst_gpio;
+}
+
+int wmt_ts_get_lcdexchg(void)
+{
+ return lcd_exchg;
+}
+
+int wmt_ts_get_resolvX(void)
+{
+ return panelres_x;
+}
+
+int wmt_ts_get_resolvY(void)
+{
+ return panelres_y;
+}
+
+//up:1-pen up,0-pen down
+void wmt_ts_set_penup(int up)
+{
+ l_penup = up;
+}
+
+//
+int wmt_ts_wait_penup(void)
+{
+ int ret = wait_event_interruptible(
+ ts_penup_wait_queue,
+ (1==l_penup));
+ return ret;
+}
+
+// return:1-pen up,0-pen dwon
+int wmt_ts_ispenup(void)
+{
+ return l_penup;
+}
+
+
+void wmt_ts_wakeup_penup(void)
+{
+ wake_up(&ts_penup_wait_queue);
+}
+
+int wmt_is_tsirq_enable(void)
+{
+ int val = 0;
+ int num = irq_gpio;
+
+ if(num > 11)
+ return 0;
+
+ if(num<4)
+ val = REG32_VAL(__GPIO_BASE+0x0300) & (1<<(num*8+7));
+ else if(num >= 4 && num < 8)
+ val = REG32_VAL(__GPIO_BASE+0x0304) & (1<<((num-4)*8+7));
+ else
+ val = REG32_VAL(__GPIO_BASE+0x0308) & (1<<((num-8)*8+7));
+
+ return val?1:0;
+
+}
+
+int wmt_is_tsint(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return 0;
+ }
+ return (REG32_VAL(__GPIO_BASE+0x0360) & (1<<num)) ? 1: 0;
+}
+
+void wmt_clr_int(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return;
+ }
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num;
+}
+
+void wmt_tsreset_init(void)
+{
+ int num = rst_gpio;
+
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num);//&= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<num); // out low
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<num); //output enable
+ msleep(10);
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<num); // out high
+}
+
+// enable:0-disable,1-enable
+void wmt_enable_rst_pull(int enable)
+{
+ if (enable)
+ {
+ REG32_VAL(__GPIO_BASE+0x0480) |= (1<<rst_gpio); //enable pull up/down
+ } else {
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<rst_gpio); //disable pull up/down
+ }
+}
+
+// up:0-pull down,1-pull up
+void wmt_set_rst_pull(int up)
+{
+ if (up)
+ {
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<rst_gpio); //pull up
+ } else {
+ REG32_VAL(__GPIO_BASE+0x04c0) &= ~(1<<rst_gpio); //pull down
+ }
+}
+
+// high:0-low level,1-high level
+void wmt_rst_output(int high)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ if (high)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<rst_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<rst_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<rst_gpio); //set output
+}
+
+void wmt_rst_input(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<rst_gpio); //set input
+}
+
+void wmt_set_intasgp(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<irq_gpio); //enable gpio
+}
+
+// val:1--high,0-low
+void wmt_intgp_out(int val)
+{
+ if (val)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<irq_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<irq_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<irq_gpio); //set output
+}
+
+void wmt_ts_set_irqinput(void)
+{
+ int num = irq_gpio;
+
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+}
+
+unsigned int wmt_ts_irqinval(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<irq_gpio); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<irq_gpio); //set input
+ return REG32_VAL(__GPIO_BASE+0x0000)&(1<<irq_gpio);
+}
+
+int wmt_set_gpirq(int type)
+{
+ int shift;
+ int offset;
+ unsigned long reg;
+ int num = irq_gpio;
+
+ if(num >11)
+ return -1;
+ //if (num > 9)
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; // gpio10,11 as gpio
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull down
+ REG32_VAL(__GPIO_BASE+0x0480) |= (1<<num); //enable pull up/down
+
+ //set gpio irq triger type
+ if(num < 4){//[0,3]
+ shift = num;
+ offset = 0x0300;
+ }else if(num >= 4 && num < 8){//[4,7]
+ shift = num-4;
+ offset = 0x0304;
+ }else{// [8,11]
+ shift = num-8;
+ offset = 0x0308;
+ }
+
+ reg = REG32_VAL(__GPIO_BASE + offset);
+
+ switch(type){
+ case IRQ_TYPE_LEVEL_LOW:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ default://both edge
+ reg |= (1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+
+ }
+ //reg |= 1<<(shift*8+7);//enable interrupt
+ reg &= ~(1<<(shift*8+7)); //disable int
+
+ REG32_VAL(__GPIO_BASE + offset) = reg;
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num; //clear interrupt status
+ msleep(5);
+ return 0;
+}
+
+int wmt_enable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) |= 1<<(num*8+7); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) |= 1<<((num-4)*8+7); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) |= 1<<((num-8)*8+7); //enable interrupt
+
+ return 0;
+}
+
+int wmt_disable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) &= ~(1<<(num*8+7)); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) &= ~(1<<((num-4)*8+7)); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) &= ~(1<<((num-8)*8+7)); //enable interrupt
+
+ return 0;
+}
+
+
+int wmt_get_tsirqnum(void)
+{
+ return IRQ_GPIO;
+}
+
+
+int wmt_ts_set_rawcoord(unsigned short x, unsigned short y)
+{
+ g_evLast.x = x;
+ g_evLast.y = y;
+ //dbg("raw(%d,%d)*\n", x, y);
+ return 0;
+}
+
+static void wmt_ts_platform_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device wmt_ts_plt_device = {
+ .name = TS_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = wmt_ts_platform_release,
+ },
+// .num_resources = ARRAY_SIZE(wm9715_ts_resources),
+// .resource = wm9715_ts_resources,
+};
+
+static int wmt_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dbg("ts suspend....\n");
+ if (l_tsdev->suspend != NULL)
+ {
+ return l_tsdev->suspend(pdev, state);
+ }
+ return 0;
+}
+static int wmt_ts_resume(struct platform_device *pdev)
+{
+ dbg("ts resume....\n");
+ if (l_tsdev->resume != NULL)
+ {
+ return l_tsdev->resume(pdev);
+ }
+ klog("...\n");
+ return 0;
+}
+
+static int wmt_ts_probe(struct platform_device *pdev)
+{
+ l_tsproc= create_proc_entry(WMTTS_PROC_NAME, 0666, NULL/*&proc_root*/);
+ if (l_tsproc != NULL)
+ {
+ l_tsproc->read_proc = ts_readproc;
+ l_tsproc->write_proc = ts_writeproc;
+ }
+
+ if (l_tsdev->probe != NULL)
+ return l_tsdev->probe(pdev);
+ else
+ return 0;
+}
+
+static int wmt_ts_remove(struct platform_device *pdev)
+{
+ if (l_tsproc != NULL)
+ {
+ remove_proc_entry(WMTTS_PROC_NAME, NULL);
+ l_tsproc = NULL;
+ }
+
+ if (l_tsdev->remove != NULL)
+ return l_tsdev->remove(pdev);
+ else
+ return 0;
+}
+
+static struct platform_driver wmt_ts_plt_driver = {
+ .driver = {
+ .name = TS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = wmt_ts_probe,
+ .remove = wmt_ts_remove,
+ .suspend = wmt_ts_suspend,
+ .resume = wmt_ts_resume,
+};
+
+static int wmt_ts_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+
+ klog("wmt ts driver opening...\n");
+
+ //ts_clear();
+ //try_module_get(THIS_MODULE);
+
+ return ret;
+}
+
+static int wmt_ts_close(struct inode *inode, struct file *filp)
+{
+ klog("wmt ts driver closing...\n");
+ //ts_clear();
+ //module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static unsigned int wmt_ts_poll(struct file *filp, struct poll_table_struct *wait)
+{
+#if 0
+ poll_wait(filp, &queue, wait);
+ if ( head != tail )
+ return (POLLIN | POLLRDNORM);
+#endif
+ return 0;
+}
+
+static long wmt_ts_ioctl(/*struct inode * node,*/ struct file *dev, unsigned int cmd, unsigned long arg)
+{
+ int nBuff[7];
+ char env_val[96]={0};
+ //dbg("wmt_ts_ioctl(node=0x%p, dev=0x%p, cmd=0x%08x, arg=0x%08lx)\n", node, dev, cmd, arg);
+
+ if (_IOC_TYPE(cmd) != TS_IOC_MAGIC){
+ dbg("CMD ERROR!");
+ return -ENOTTY;
+ }
+
+ if (_IOC_NR(cmd) > TS_IOC_MAXNR){
+ dbg("NO SUCH IO CMD!\n");
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+
+ case TS_IOCTL_CAL_DONE:
+ klog("wmt_ts_ioctl: TS_IOCTL_CAL_DONE\n");
+ copy_from_user(nBuff, (unsigned int*)arg, 7*sizeof(int));
+
+ mutex_lock(&cal_mutex);
+ g_CalcParam.a1 = nBuff[0];
+ g_CalcParam.b1 = nBuff[1];
+ g_CalcParam.c1 = nBuff[2];
+ g_CalcParam.a2 = nBuff[3];
+ g_CalcParam.b2 = nBuff[4];
+ g_CalcParam.c2 = nBuff[5];
+ g_CalcParam.delta = nBuff[6];
+
+ if(g_CalcParam.delta == 0)
+ g_CalcParam.delta =1;//avoid divide by zero
+
+ mutex_unlock(&cal_mutex);
+
+ sprintf(env_val,"%d %d %d %d %d %d %d",nBuff[0],nBuff[1],nBuff[2],nBuff[3],nBuff[4],nBuff[5],nBuff[6]);
+ wmt_setsyspara("wmt.io.ts.2dcal", env_val);
+ klog("Tsc calibrate done data: [%s]\n",env_val);
+
+ return 0;
+
+
+ case TS_IOCTL_GET_RAWDATA:
+ // wait for point up
+ dbg("test wait_penup\n");
+ //l_tsdev->wait_penup(&gt811_tsdev);
+ klog("wmt_ts_ioctl: TS_IOCTL_GET_RAWDATA\n");
+ wmt_ts_wait_penup();
+
+ nBuff[0] = g_evLast.x;
+ nBuff[1] = g_evLast.y;
+ copy_to_user((unsigned int*)arg, nBuff, 2*sizeof(int));
+ klog("raw data: x=%d, y=%d\n", nBuff[0], nBuff[1]);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t wmt_ts_read(struct file *filp, char *buf, size_t count, loff_t *l)
+{
+ // read firmware file
+
+ return 0;
+}
+
+
+static struct file_operations wmt_ts_fops = {
+ .read = wmt_ts_read,
+ .poll = wmt_ts_poll,
+ .unlocked_ioctl = wmt_ts_ioctl,
+ .open = wmt_ts_open,
+ .release = wmt_ts_close,
+};
+
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data )
+{
+ int calibrate = 0;
+ int val = 0;
+
+ if (sscanf(buffer, "calibrate=%d\n", &calibrate))
+ {
+ if (1 == calibrate)
+ {
+ if((l_tsdev->capacitance_calibrate != NULL) &&
+ (0 == l_tsdev->capacitance_calibrate()))
+ {
+ printk(KERN_ALERT "%s calibration successfully!\n", l_tsdev->ts_id);
+ } else {
+ printk(KERN_ALERT "%s calibration failed!\n", l_tsdev->ts_id);
+ }
+ }
+ } else if (sscanf(buffer, "reset=%d\n", &val))
+ {
+
+ }
+ return count;
+}
+
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len = sprintf(page,
+ "echo calibrate=1 > /proc/wmtts_config to calibrate ts.\n");
+ return len;
+}
+
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 127, i = 0;
+ char retval[128] = {0},*p=NULL,*s=NULL;
+ int Enable=0;
+ int keypos[4][4];
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ errlog("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ memset(l_tpinfor,0,sizeof(l_tpinfor[0]));
+ p = retval;
+ sscanf(p,"%d:", &Enable);
+ p = strchr(p,':');p++;
+ s = strchr(p,':');
+ strncpy(l_tpinfor[0].name,p, (s-p));
+ p = s+1;
+ dbg("ts_name=%s\n", l_tpinfor[0].name);
+
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &irq_gpio,&panelres_x,&panelres_y,&rst_gpio,
+ &(l_tpinfor[0].xaxis),&(l_tpinfor[0].xdir),&(l_tpinfor[0].ydir),
+ &(l_tpinfor[0].finger_num),
+ &(l_tpinfor[0].i2caddr),&gsl_noid_ver);
+ if (ret < 9)
+ {
+ dbg("Wrong format ts u-boot param(%d)!\n",ret);
+ return -ENODEV;
+ }
+ //check touch enable
+ if(Enable == 0){
+ errlog("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ /*if (strstr(l_tpinfor[0].name, l_tsdev->ts_id) == NULL)
+ {
+ dbg("Can't find %s!\n", l_tsdev->ts_id);
+ return -ENODEV;
+ }*/
+ l_tpindex = 0;
+
+ klog("p.x = %d, p.y = %d, gpio=%d, resetgpio=%d,xaxis=%d,xdir=%d,ydri=%d,i2caddr=%d,gsl_noid_ver=%d\n",
+ panelres_x, panelres_y, irq_gpio, rst_gpio,
+ l_tpinfor[0].xaxis,l_tpinfor[0].xdir,l_tpinfor[0].ydir,
+ l_tpinfor[0].i2caddr, gsl_noid_ver);
+
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.tskey", retval, &len);
+ if(ret)
+ {
+ l_iftskey = 0;
+ } else {
+ // get touch key
+ p = retval;
+ sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", \
+ keypos[0],keypos[0]+1,keypos[0]+2,keypos[0]+3, \
+ keypos[1],keypos[1]+1,keypos[1]+2,keypos[1]+3, \
+ keypos[2],keypos[2]+1,keypos[2]+2,keypos[2]+3, \
+ keypos[3],keypos[3]+1,keypos[3]+2,keypos[3]+3);
+ for (i = 0; i < 4; i++)
+ {
+ wmt_set_keypos(i,keypos[i][0],keypos[i][1],keypos[i][2],keypos[i][3]);
+ dbg("%d:%d,%d,%d,%d\n",i,keypos[i][0],keypos[i][1],keypos[i][2],keypos[i][3]);
+ };
+ l_iftskey = 1;
+ }
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.touch.earlysus", retval, &len);
+ if(!ret) {
+ p = retval;
+ sscanf(p, "%d", &earlysus_en);
+ }
+
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ lcd_exchg = 1;
+ }
+
+ //wmt.tpkey.led 141:1
+ tp_led_gpio = -1;
+ tp_led_gpio_active = -1;
+ sel_reg_bit = -1;
+ sel_reg_active = -1;
+ ret = wmt_getsyspara("wmt.tpkey.led", retval, &len);
+
+ errlog("wmt.tpkey.led: %s \n",retval);
+
+ if (!ret) {
+ int tmp[4] = {0};
+ p = retval;
+ ret = sscanf(p, "%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3]);
+ if(ret == 2){
+ tp_led_gpio = tmp[0];
+ tp_led_gpio_active = tmp[1];
+ }
+ if(ret == 4){
+ tp_led_gpio = tmp[0];
+ tp_led_gpio_active = tmp[1];
+ sel_reg_bit = tmp[2];
+ sel_reg_active = tmp[3];
+ }
+ errlog("tp_led_gpio:%d tp_led_gpio_active:%d sel_reg_bit:%d sel_reg_active:%d \n"
+ ,tp_led_gpio,tp_led_gpio_active,sel_reg_bit,sel_reg_active);
+ }
+
+/*
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.ts.2dcal", retval, &len);
+ if(ret){
+ errlog("Read env wmt.io.ts.2dcal Failed.\n ");
+ //return -EIO;
+ }
+ i = 0;
+ while(i < sizeof(retval)){
+ if(retval[i]==' ' || retval[i]==',' || retval[i]==':')
+ retval[i] = '\0';
+ i++;
+ }
+
+ i = 0;
+ p = retval;
+ while(i<7 && p < (retval + sizeof(retval))){
+ if(*p == '\0')
+ p++;
+ else{
+ sscanf(p,"%d",&nBuff[i]);
+ //printk("%d\n",nBuff[i]);
+ p=p+strlen(p);
+ i++;
+ }
+ }
+ //sscanf(retval,"%d %d %d %d %d %d %d %d",&nBuff[0],&nBuff[1],&nBuff[2],&nBuff[3],&nBuff[4],&nBuff[5],&nBuff[6]);
+ printk("Tsc calibrate init data: [%d %d %d %d %d %d %d]\n",nBuff[0],nBuff[1],nBuff[2],nBuff[3],nBuff[4],nBuff[5],nBuff[6]);
+
+ g_CalcParam.a1 = nBuff[0];
+ g_CalcParam.b1 = nBuff[1];
+ g_CalcParam.c1 = nBuff[2];
+ g_CalcParam.a2 = nBuff[3];
+ g_CalcParam.b2 = nBuff[4];
+ g_CalcParam.c2 = nBuff[5];
+ g_CalcParam.delta = nBuff[6];
+
+ if(g_CalcParam.delta == 0)
+ g_CalcParam.delta =1;//avoid divide by zero
+*/
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = WMT_TS_I2C_NAME,
+ .flags = 0x00,
+ //.addr = WMT_TS_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+
+ ts_i2c_board_info.addr = l_tpinfor[0].i2caddr;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(1);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+struct i2c_client* ts_get_i2c_client(void)
+{
+ return l_client;
+}
+
+static int __init wmt_ts_init(void)
+{
+ int ret = 0;
+
+ if(wmt_check_touch_env())
+ return -ENODEV;
+
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+ mutex_init(&cal_mutex);
+
+ if (l_tsdev->init() < 0){
+ dbg("Errors to init %s ts IC!!!\n", l_tsdev->ts_id);
+ ret = -1;
+ goto err_init;
+ }
+ // Create device node
+ if (register_chrdev (TS_MAJOR, TS_NAME, &wmt_ts_fops)) {
+ printk (KERN_ERR "wmt touch: unable to get major %d\n", TS_MAJOR);
+ return -EIO;
+ }
+
+ l_dev_class = class_create(THIS_MODULE, TS_NAME);
+ if (IS_ERR(l_dev_class)){
+ ret = PTR_ERR(l_dev_class);
+ printk(KERN_ERR "Can't class_create touch device !!\n");
+ return ret;
+ }
+ l_clsdevice = device_create(l_dev_class, NULL, MKDEV(TS_MAJOR, 0), NULL, TS_NAME);
+ if (IS_ERR(l_clsdevice)){
+ ret = PTR_ERR(l_clsdevice);
+ printk(KERN_ERR "Failed to create device %s !!!",TS_NAME);
+ return ret;
+ }
+
+ // register device and driver of platform
+ ret = platform_device_register(&wmt_ts_plt_device);
+ if(ret){
+ errlog("wmt ts plat device register failed!\n");
+ return ret;
+ }
+ ret = platform_driver_register(&wmt_ts_plt_driver);
+ if(ret){
+ errlog("can not register platform_driver_register\n");
+ platform_device_unregister(&wmt_ts_plt_device);
+ return ret;
+ }
+
+ klog("%s driver init ok!\n",l_tsdev->ts_id);
+ return 0;
+err_init:
+ ts_i2c_unregister_device();
+ return ret;
+}
+
+static void __exit wmt_ts_exit(void)
+{
+ dbg("%s\n",__FUNCTION__);
+
+ l_tsdev->exit();
+ platform_driver_unregister(&wmt_ts_plt_driver);
+ platform_device_unregister(&wmt_ts_plt_device);
+ device_destroy(l_dev_class, MKDEV(TS_MAJOR, 0));
+ unregister_chrdev(TS_MAJOR, TS_NAME);
+ class_destroy(l_dev_class);
+ mutex_destroy(&cal_mutex);
+ ts_i2c_unregister_device();
+}
+
+
+module_init(wmt_ts_init);
+module_exit(wmt_ts_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/gsl1680_ts/wmt_ts.h b/drivers/input/touchscreen/gsl1680_ts/wmt_ts.h
new file mode 100755
index 00000000..ea764fd4
--- /dev/null
+++ b/drivers/input/touchscreen/gsl1680_ts/wmt_ts.h
@@ -0,0 +1,117 @@
+
+#ifndef WMT_TSH_201010191758
+#define WMT_TSH_201010191758
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <mach/hardware.h>
+
+
+//#define DEBUG_WMT_TS // kinseyli
+#ifdef DEBUG_WMT_TS
+#undef dbg
+#define dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args)
+
+//#define dbg(fmt, args...) if (kpadall_isrundbg()) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args)
+
+#else
+#define dbg(fmt, args...)
+#endif
+
+#undef errlog
+#undef klog
+#define errlog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+#define klog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+
+#define WMT_TS_I2C_NAME "gsl1680-ts"
+//#define WMT_TS_I2C_ADDR 0x15
+
+#define PIN_SHARING_SEL_OFFSET 0x200
+
+
+#define FW_BINARYFILE_SIZE 0x8000
+
+extern int earlysus_en;
+
+//////////////////////////////data type///////////////////////////
+typedef struct {
+ short pressure;
+ short x;
+ short y;
+ //short millisecs;
+} TS_EVENT;
+
+struct wmtts_device
+{
+ //data
+ char* driver_name;
+ char* ts_id;
+ //function
+ int (*init)(void);
+ int (*probe)(struct platform_device *platdev);
+ int (*remove)(struct platform_device *pdev);
+ void (*exit)(void);
+ int (*suspend)(struct platform_device *pdev, pm_message_t state);
+ int (*resume)(struct platform_device *pdev);
+ int (*capacitance_calibrate)(void);
+ int (*wait_penup)(struct wmtts_device*tsdev); // waiting untill penup
+ int penup; // 0--pendown;1--penup
+
+};
+
+
+//////////////////////////function interface/////////////////////////
+extern int wmt_ts_get_gpionum(void);
+extern int wmt_ts_iscalibrating(void);
+extern int wmt_ts_get_resolvX(void);
+extern int wmt_ts_get_resolvY(void);
+extern int wmt_ts_set_rawcoord(unsigned short x, unsigned short y);
+extern int wmt_set_gpirq(int type);
+extern int wmt_get_tsirqnum(void);
+extern int wmt_disable_gpirq(void);
+extern int wmt_enable_gpirq(void);
+extern int wmt_is_tsirq_enable(void);
+extern int wmt_is_tsint(void);
+extern void wmt_clr_int(void);
+extern void wmt_tsreset_init(void);
+extern int wmt_ts_get_resetgpnum(void);
+extern int wmt_ts_get_lcdexchg(void);
+extern void wmt_enable_rst_pull(int enable);
+extern void wmt_set_rst_pull(int up);
+extern void wmt_rst_output(int high);
+extern void wmt_rst_input(void);
+extern void wmt_set_intasgp(void);
+extern void wmt_intgp_out(int val);
+extern void wmt_ts_set_irqinput(void);
+extern unsigned int wmt_ts_irqinval(void);
+extern void wmt_ts_set_penup(int up);
+extern int wmt_ts_wait_penup(void);
+extern void wmt_ts_wakeup_penup(void);
+extern struct i2c_client* ts_get_i2c_client(void);
+extern int wmt_ts_ispenup(void);
+extern unsigned int wmt_ts_get_xaxis(void);
+extern unsigned int wmt_ts_get_xdir(void);
+extern unsigned int wmt_ts_get_ydir(void);
+extern int wmt_ts_if_tskey(void);
+
+extern void TouchPanelCalibrateAPoint(
+ int UncalX, //@PARM The uncalibrated X coordinate
+ int UncalY, //@PARM The uncalibrated Y coordinate
+ int *pCalX, //@PARM The calibrated X coordinate
+ int *pCalY //@PARM The calibrated Y coordinate
+ );
+
+//filepath:the path of firmware file;
+//firmdata:store the data from firmware file;
+//return:the lengthen of firmware data,negative-parsing error.
+extern int read_firmwfile(char* filepath, struct fw_data** firmdata, unsigned int* id_config);
+extern int wmt_ts_get_firmwfilename(char* fname);
+
+#endif
+
+
+
diff --git a/drivers/input/touchscreen/gt9xx_ts/Kconfig b/drivers/input/touchscreen/gt9xx_ts/Kconfig
new file mode 100755
index 00000000..d5d6a394
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx_ts/Kconfig
@@ -0,0 +1,11 @@
+config TOUCHSCREEN_GT9XX
+ tristate "GT9XX Capacity Touchscreen Device Support"
+ default y
+ depends on ARCH_WMT
+ ---help---
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+ If unsure, say N.
+ To compile this driver as a module, choose M here: the
+ module will be called gt9xx.
+
diff --git a/drivers/input/touchscreen/gt9xx_ts/Makefile b/drivers/input/touchscreen/gt9xx_ts/Makefile
new file mode 100755
index 00000000..642b1271
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx_ts/Makefile
@@ -0,0 +1,34 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_gt9xx
+
+#obj-$(CONFIG_TOUCHSCREEN_FT5X0X) := $(MY_MODULE_NAME).o
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := gt9xx.o gt9xx_update.o goodix_tool.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ @rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ @rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
+
diff --git a/drivers/input/touchscreen/gt9xx_ts/goodix_tool.c b/drivers/input/touchscreen/gt9xx_ts/goodix_tool.c
new file mode 100755
index 00000000..3dfe4e1d
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx_ts/goodix_tool.c
@@ -0,0 +1,615 @@
+/* drivers/input/touchscreen/goodix_tool.c
+ *
+ * 2010 - 2012 Goodix Technology.
+ *
+ * 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 a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ * Version:1.6
+ * V1.0:2012/05/01,create file.
+ * V1.2:2012/06/08,modify some warning.
+ * V1.4:2012/08/28,modified to support GT9XX
+ * V1.6:new proc name
+ */
+
+#include "gt9xx.h"
+
+#define DATA_LENGTH_UINT 512
+#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8*))
+static char procname[20] = {0};
+
+#define UPDATE_FUNCTIONS
+
+#ifdef UPDATE_FUNCTIONS
+extern s32 gup_enter_update_mode(struct i2c_client *client);
+extern void gup_leave_update_mode(void);
+extern s32 gup_update_proc(void *dir);
+#endif
+
+extern void gtp_irq_disable(struct goodix_ts_data *);
+extern void gtp_irq_enable(struct goodix_ts_data *);
+
+#pragma pack(1)
+typedef struct{
+ u8 wr; //write read flag£¬0:R 1:W 2:PID 3:
+ u8 flag; //0:no need flag/int 1: need flag 2:need int
+ u8 flag_addr[2]; //flag address
+ u8 flag_val; //flag val
+ u8 flag_relation; //flag_val:flag 0:not equal 1:equal 2:> 3:<
+ u16 circle; //polling cycle
+ u8 times; //plling times
+ u8 retry; //I2C retry times
+ u16 delay; //delay befor read or after write
+ u16 data_len; //data length
+ u8 addr_len; //address length
+ u8 addr[2]; //address
+ u8 res[3]; //reserved
+ u8* data; //data pointer
+}st_cmd_head;
+#pragma pack()
+st_cmd_head cmd_head;
+
+static struct i2c_client *gt_client = NULL;
+
+static struct proc_dir_entry *goodix_proc_entry;
+
+static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data);
+static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data );
+static s32 (*tool_i2c_read)(u8 *, u16);
+static s32 (*tool_i2c_write)(u8 *, u16);
+
+#if GTP_ESD_PROTECT
+extern void gtp_esd_switch(struct i2c_client *, s32);
+#endif
+s32 DATA_LENGTH = 0;
+s8 IC_TYPE[16] = {0};
+
+static void tool_set_proc_name(char * procname)
+{
+ char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May",
+ "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ char date[20] = {0};
+ char month[4] = {0};
+ int i = 0, n_month = 1, n_day = 0, n_year = 0;
+
+ sprintf(date, "%s", __DATE__);
+
+ //GTP_DEBUG("compile date: %s", date);
+
+ sscanf(date, "%s %d %d", month, &n_day, &n_year);
+
+ for (i = 0; i < 12; ++i)
+ {
+ if (!memcmp(months[i], month, 3))
+ {
+ n_month = i+1;
+ break;
+ }
+ }
+
+ sprintf(procname, "gmnode%04d%02d%02d", n_year, n_month, n_day);
+
+ //GTP_DEBUG("procname = %s", procname);
+}
+
+
+static s32 tool_i2c_read_no_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ s32 i = 0;
+ struct i2c_msg msgs[2];
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = gt_client->addr;
+ msgs[0].len = cmd_head.addr_len;
+ msgs[0].buf = &buf[0];
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = gt_client->addr;
+ msgs[1].len = len;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+
+ for (i = 0; i < cmd_head.retry; i++)
+ {
+ ret=i2c_transfer(gt_client->adapter, msgs, 2);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+ return ret;
+}
+
+static s32 tool_i2c_write_no_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ s32 i = 0;
+ struct i2c_msg msg;
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = gt_client->addr;
+ msg.len = len;
+ msg.buf = buf;
+
+ for (i = 0; i < cmd_head.retry; i++)
+ {
+ ret=i2c_transfer(gt_client->adapter, &msg, 1);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+ return ret;
+}
+
+static s32 tool_i2c_read_with_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ u8 pre[2] = {0x0f, 0xff};
+ u8 end[2] = {0x80, 0x00};
+
+ tool_i2c_write_no_extra(pre, 2);
+ ret = tool_i2c_read_no_extra(buf, len);
+ tool_i2c_write_no_extra(end, 2);
+
+ return ret;
+}
+
+static s32 tool_i2c_write_with_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ u8 pre[2] = {0x0f, 0xff};
+ u8 end[2] = {0x80, 0x00};
+
+ tool_i2c_write_no_extra(pre, 2);
+ ret = tool_i2c_write_no_extra(buf, len);
+ tool_i2c_write_no_extra(end, 2);
+
+ return ret;
+}
+
+static void register_i2c_func(void)
+{
+// if (!strncmp(IC_TYPE, "GT818", 5) || !strncmp(IC_TYPE, "GT816", 5)
+// || !strncmp(IC_TYPE, "GT811", 5) || !strncmp(IC_TYPE, "GT818F", 6)
+// || !strncmp(IC_TYPE, "GT827", 5) || !strncmp(IC_TYPE,"GT828", 5)
+// || !strncmp(IC_TYPE, "GT813", 5))
+ if (strncmp(IC_TYPE, "GT8110", 6) && strncmp(IC_TYPE, "GT8105", 6)
+ && strncmp(IC_TYPE, "GT801", 5) && strncmp(IC_TYPE, "GT800", 5)
+ && strncmp(IC_TYPE, "GT801PLUS", 9) && strncmp(IC_TYPE, "GT811", 5)
+ && strncmp(IC_TYPE, "GTxxx", 5))
+ {
+ tool_i2c_read = tool_i2c_read_with_extra;
+ tool_i2c_write = tool_i2c_write_with_extra;
+ GTP_DEBUG("I2C function: with pre and end cmd!");
+ }
+ else
+ {
+ tool_i2c_read = tool_i2c_read_no_extra;
+ tool_i2c_write = tool_i2c_write_no_extra;
+ GTP_INFO("I2C function: without pre and end cmd!");
+ }
+}
+
+static void unregister_i2c_func(void)
+{
+ tool_i2c_read = NULL;
+ tool_i2c_write = NULL;
+ GTP_INFO("I2C function: unregister i2c transfer function!");
+}
+
+
+s32 init_wr_node(struct i2c_client *client)
+{
+ s32 i;
+
+ gt_client = client;
+ memset(&cmd_head, 0, sizeof(cmd_head));
+ cmd_head.data = NULL;
+
+ i = 5;
+ while ((!cmd_head.data) && i)
+ {
+ cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
+ if (NULL != cmd_head.data)
+ {
+ break;
+ }
+ i--;
+ }
+ if (i)
+ {
+ DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH;
+ GTP_INFO("Applied memory size:%d.", DATA_LENGTH);
+ }
+ else
+ {
+ GTP_ERROR("Apply for memory failed.");
+ return FAIL;
+ }
+
+ cmd_head.addr_len = 2;
+ cmd_head.retry = 5;
+
+ register_i2c_func();
+
+ tool_set_proc_name(procname);
+ goodix_proc_entry = create_proc_entry(procname, 0666, NULL);
+ if (goodix_proc_entry == NULL)
+ {
+ GTP_ERROR("Couldn't create proc entry!");
+ return FAIL;
+ }
+ else
+ {
+ GTP_INFO("Create proc entry success!");
+ goodix_proc_entry->write_proc = goodix_tool_write;
+ goodix_proc_entry->read_proc = goodix_tool_read;
+ }
+
+ return SUCCESS;
+}
+
+void uninit_wr_node(void)
+{
+ kfree(cmd_head.data);
+ cmd_head.data = NULL;
+ unregister_i2c_func();
+ remove_proc_entry(procname, NULL);
+}
+
+static u8 relation(u8 src, u8 dst, u8 rlt)
+{
+ u8 ret = 0;
+
+ switch (rlt)
+ {
+ case 0:
+ ret = (src != dst) ? true : false;
+ break;
+
+ case 1:
+ ret = (src == dst) ? true : false;
+ GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.", src, dst, (s32)ret);
+ break;
+
+ case 2:
+ ret = (src > dst) ? true : false;
+ break;
+
+ case 3:
+ ret = (src < dst) ? true : false;
+ break;
+
+ case 4:
+ ret = (src & dst) ? true : false;
+ break;
+
+ case 5:
+ ret = (!(src | dst)) ? true : false;
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Comfirm function.
+Input:
+ None.
+Output:
+ Return write length.
+********************************************************/
+static u8 comfirm(void)
+{
+ s32 i = 0;
+ u8 buf[32];
+
+// memcpy(&buf[GTP_ADDR_LENGTH - cmd_head.addr_len], &cmd_head.flag_addr, cmd_head.addr_len);
+// memcpy(buf, &cmd_head.flag_addr, cmd_head.addr_len);//Modified by Scott, 2012-02-17
+ memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
+
+ for (i = 0; i < cmd_head.times; i++)
+ {
+ if (tool_i2c_read(buf, 1) <= 0)
+ {
+ GTP_ERROR("Read flag data failed!");
+ return FAIL;
+ }
+ if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val, cmd_head.flag_relation))
+ {
+ GTP_DEBUG("value at flag addr:0x%02x.", buf[GTP_ADDR_LENGTH]);
+ GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val);
+ break;
+ }
+
+ msleep(cmd_head.circle);
+ }
+
+ if (i >= cmd_head.times)
+ {
+ GTP_ERROR("Didn't get the flag to continue!");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+/*******************************************************
+Function:
+ Goodix tool write function.
+Input:
+ standard proc write function param.
+Output:
+ Return write length.
+********************************************************/
+static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data)
+{
+ s32 ret = 0;
+ GTP_DEBUG_FUNC();
+ GTP_DEBUG_ARRAY((u8*)buff, len);
+
+ ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+
+ GTP_DEBUG("wr :0x%02x.", cmd_head.wr);
+ GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
+ GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0], cmd_head.flag_addr[1]);
+ GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val);
+ GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation);
+ GTP_DEBUG("circle :%d.", (s32)cmd_head.circle);
+ GTP_DEBUG("times :%d.", (s32)cmd_head.times);
+ GTP_DEBUG("retry :%d.", (s32)cmd_head.retry);
+ GTP_DEBUG("delay :%d.", (s32)cmd_head.delay);
+ GTP_DEBUG("data len:%d.", (s32)cmd_head.data_len);
+ GTP_DEBUG("addr len:%d.", (s32)cmd_head.addr_len);
+ GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
+ GTP_DEBUG("len:%d.", (s32)len);
+ GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]);
+
+ if (1 == cmd_head.wr)
+ {
+ // copy_from_user(&cmd_head.data[cmd_head.addr_len], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+ memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], cmd_head.addr, cmd_head.addr_len);
+
+ GTP_DEBUG_ARRAY(cmd_head.data, cmd_head.data_len + cmd_head.addr_len);
+ GTP_DEBUG_ARRAY((u8*)&buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+
+ if (1 == cmd_head.flag)
+ {
+ if (FAIL == comfirm())
+ {
+ GTP_ERROR("[WRITE]Comfirm fail!");
+ return FAIL;
+ }
+ }
+ else if (2 == cmd_head.flag)
+ {
+ //Need interrupt!
+ }
+ if (tool_i2c_write(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
+ cmd_head.data_len + cmd_head.addr_len) <= 0)
+ {
+ GTP_ERROR("[WRITE]Write data failed!");
+ return FAIL;
+ }
+
+ GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],cmd_head.data_len + cmd_head.addr_len);
+ if (cmd_head.delay)
+ {
+ msleep(cmd_head.delay);
+ }
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (3 == cmd_head.wr) //Write ic type
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+ memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
+
+ register_i2c_func();
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (5 == cmd_head.wr)
+ {
+ //memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (7 == cmd_head.wr)//disable irq!
+ {
+ gtp_irq_disable(i2c_get_clientdata(gt_client));
+
+ #if GTP_ESD_PROTECT
+ gtp_esd_switch(gt_client, SWITCH_OFF);
+ #endif
+ return CMD_HEAD_LENGTH;
+ }
+ else if (9 == cmd_head.wr) //enable irq!
+ {
+ gtp_irq_enable(i2c_get_clientdata(gt_client));
+
+ #if GTP_ESD_PROTECT
+ gtp_esd_switch(gt_client, SWITCH_ON);
+ #endif
+ return CMD_HEAD_LENGTH;
+ }
+ else if(17 == cmd_head.wr)
+ {
+ struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_DEBUG("copy_from_user failed.");
+ }
+ if(cmd_head.data[GTP_ADDR_LENGTH])
+ {
+ GTP_DEBUG("gtp enter rawdiff.");
+ ts->gtp_rawdiff_mode = true;
+ }
+ else
+ {
+ ts->gtp_rawdiff_mode = false;
+ GTP_DEBUG("gtp leave rawdiff.");
+ }
+ return CMD_HEAD_LENGTH;
+ }
+#ifdef UPDATE_FUNCTIONS
+ else if (11 == cmd_head.wr)//Enter update mode!
+ {
+ if (FAIL == gup_enter_update_mode(gt_client))
+ {
+ return FAIL;
+ }
+ }
+ else if (13 == cmd_head.wr)//Leave update mode!
+ {
+ gup_leave_update_mode();
+ }
+ else if (15 == cmd_head.wr) //Update firmware!
+ {
+ show_len = 0;
+ total_len = 0;
+ memset(cmd_head.data, 0, cmd_head.data_len + 1);
+ memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+
+ if (FAIL == gup_update_proc((void*)cmd_head.data))
+ {
+ return FAIL;
+ }
+ }
+#endif
+
+ return CMD_HEAD_LENGTH;
+}
+
+/*******************************************************
+Function:
+ Goodix tool read function.
+Input:
+ standard proc read function param.
+Output:
+ Return read length.
+********************************************************/
+static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data )
+{
+ GTP_DEBUG_FUNC();
+
+ if (cmd_head.wr % 2)
+ {
+ return FAIL;
+ }
+ else if (!cmd_head.wr)
+ {
+ u16 len = 0;
+ s16 data_len = 0;
+ u16 loc = 0;
+
+ if (1 == cmd_head.flag)
+ {
+ if (FAIL == comfirm())
+ {
+ GTP_ERROR("[READ]Comfirm fail!");
+ return FAIL;
+ }
+ }
+ else if (2 == cmd_head.flag)
+ {
+ //Need interrupt!
+ }
+
+ memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len);
+
+ GTP_DEBUG("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0], cmd_head.data[1]);
+ GTP_DEBUG("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
+
+ if (cmd_head.delay)
+ {
+ msleep(cmd_head.delay);
+ }
+
+ data_len = cmd_head.data_len;
+ while(data_len > 0)
+ {
+ if (data_len > DATA_LENGTH)
+ {
+ len = DATA_LENGTH;
+ }
+ else
+ {
+ len = data_len;
+ }
+ data_len -= DATA_LENGTH;
+
+ if (tool_i2c_read(cmd_head.data, len) <= 0)
+ {
+ GTP_ERROR("[READ]Read data failed!");
+ return FAIL;
+ }
+ memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH], len);
+ loc += len;
+
+ GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len);
+ GTP_DEBUG_ARRAY(page, len);
+ }
+ }
+ else if (2 == cmd_head.wr)
+ {
+ // memcpy(page, "gt8", cmd_head.data_len);
+ // memcpy(page, "GT818", 5);
+ // page[5] = 0;
+
+ GTP_DEBUG("Return ic type:%s len:%d.", page, (s32)cmd_head.data_len);
+ return cmd_head.data_len;
+ //return sizeof(IC_TYPE_NAME);
+ }
+ else if (4 == cmd_head.wr)
+ {
+ page[0] = show_len >> 8;
+ page[1] = show_len & 0xff;
+ page[2] = total_len >> 8;
+ page[3] = total_len & 0xff;
+
+ return cmd_head.data_len;
+ }
+ else if (6 == cmd_head.wr)
+ {
+ //Read error code!
+ }
+ else if (8 == cmd_head.wr) //Read driver version
+ {
+ // memcpy(page, GTP_DRIVER_VERSION, strlen(GTP_DRIVER_VERSION));
+ s32 tmp_len;
+ tmp_len = strlen(GTP_DRIVER_VERSION);
+ memcpy(page, GTP_DRIVER_VERSION, tmp_len);
+ page[tmp_len] = 0;
+ }
+
+ return cmd_head.data_len;
+}
diff --git a/drivers/input/touchscreen/gt9xx_ts/gt9xx.c b/drivers/input/touchscreen/gt9xx_ts/gt9xx.c
new file mode 100755
index 00000000..cc02513e
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx_ts/gt9xx.c
@@ -0,0 +1,2163 @@
+/* drivers/input/touchscreen/gt9xx.c
+ *
+ * 2010 - 2013 Goodix Technology.
+ *
+ * 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 a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ * Version: 1.8
+ * Authors: andrew@goodix.com, meta@goodix.com
+ * Release Date: 2013/04/25
+ * Revision record:
+ * V1.0:
+ * first Release. By Andrew, 2012/08/31
+ * V1.2:
+ * modify gtp_reset_guitar,slot report,tracking_id & 0x0F. By Andrew, 2012/10/15
+ * V1.4:
+ * modify gt9xx_update.c. By Andrew, 2012/12/12
+ * V1.6:
+ * 1. new heartbeat/esd_protect mechanism(add external watchdog)
+ * 2. doze mode, sliding wakeup
+ * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5)
+ * 3. config length verification
+ * 4. names & comments
+ * By Meta, 2013/03/11
+ * V1.8:
+ * 1. pen/stylus identification
+ * 2. read double check & fixed config support
+ * 2. new esd & slide wakeup optimization
+ * By Meta, 2013/06/08
+ */
+
+#include <linux/irq.h>
+#include <linux/firmware.h>
+#include "gt9xx.h"
+
+#if GTP_ICS_SLOT_REPORT
+ #include <linux/input/mt.h>
+#endif
+
+static const char *goodix_ts_name = "Goodix Capacitive TouchScreen";
+static struct workqueue_struct *goodix_wq;
+struct goodix_ts_data *l_ts;
+int l_suspend = 0;
+struct i2c_client * i2c_connect_client = NULL;
+u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]
+ = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
+
+#if GTP_HAVE_TOUCH_KEY
+ static const u16 touch_key_array[] = GTP_KEY_TAB;
+ #define GTP_MAX_KEY_NUM (sizeof(touch_key_array)/sizeof(touch_key_array[0]))
+
+#if GTP_DEBUG_ON
+ static const int key_codes[] = {KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH};
+ static const char *key_names[] = {"Key_Home", "Key_Back", "Key_Menu", "Key_Search"};
+#endif
+
+#endif
+
+static s8 gtp_i2c_test(struct i2c_client *client);
+void gtp_reset_guitar(struct i2c_client *client, s32 ms);
+void gtp_int_sync(s32 ms);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void goodix_ts_early_suspend(struct early_suspend *h);
+static void goodix_ts_late_resume(struct early_suspend *h);
+#endif
+
+#if GTP_CREATE_WR_NODE
+extern s32 init_wr_node(struct i2c_client*);
+extern void uninit_wr_node(void);
+#endif
+
+#if GTP_AUTO_UPDATE
+extern u8 gup_init_update_proc(struct goodix_ts_data *);
+#endif
+
+#if GTP_ESD_PROTECT
+static struct delayed_work gtp_esd_check_work;
+static struct workqueue_struct * gtp_esd_check_workqueue = NULL;
+static void gtp_esd_check_func(struct work_struct *);
+static s32 gtp_init_ext_watchdog(struct i2c_client *client);
+void gtp_esd_switch(struct goodix_ts_data *, s32);
+#endif
+
+
+#if GTP_SLIDE_WAKEUP
+typedef enum
+{
+ DOZE_DISABLED = 0,
+ DOZE_ENABLED = 1,
+ DOZE_WAKEUP = 2,
+}DOZE_T;
+static DOZE_T doze_status = DOZE_DISABLED;
+static s8 gtp_enter_doze(struct goodix_ts_data *ts);
+#endif
+
+static u8 chip_gt9xxs = 0; // true if ic is gt9xxs, like gt915s
+u8 grp_cfg_version = 0;
+
+/*******************************************************
+Function:
+ Read data from the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: read start address.
+ buf[2~len-1]: read data buffer.
+ len: GTP_ADDR_LENGTH + read bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 2: succeed, otherwise: failed
+*********************************************************/
+s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
+{
+ struct i2c_msg msgs[2];
+ s32 ret=-1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ //msgs[0].flags = !I2C_M_RD;
+ msgs[0].flags = 0 | I2C_M_NOSTART;
+ msgs[0].addr = client->addr;
+ msgs[0].len = GTP_ADDR_LENGTH;
+ msgs[0].buf = &buf[0];
+ //msgs[0].scl_rate = 300 * 1000; // for Rockchip
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = client->addr;
+ msgs[1].len = len - GTP_ADDR_LENGTH;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+ //msgs[1].scl_rate = 300 * 1000;
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+ if((retries >= 5))
+ {
+ #if GTP_SLIDE_WAKEUP
+ // reset chip would quit doze mode
+ if (DOZE_ENABLED == doze_status)
+ {
+ return ret;
+ }
+ #endif
+ GTP_DEBUG("I2C communication timeout, resetting chip...");
+ gtp_reset_guitar(client, 10);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Write data to the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: write start address.
+ buf[2~len-1]: data buffer
+ len: GTP_ADDR_LENGTH + write bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 1: succeed, otherwise: failed
+*********************************************************/
+s32 gtp_i2c_write(struct i2c_client *client,u8 *buf,s32 len)
+{
+ struct i2c_msg msg;
+ s32 ret = -1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = len;
+ msg.buf = buf;
+ //msg.scl_rate = 300 * 1000; // for Rockchip
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)break;
+ retries++;
+ }
+ if((retries >= 5))
+ {
+ #if GTP_SLIDE_WAKEUP
+ if (DOZE_ENABLED == doze_status)
+ {
+ return ret;
+ }
+ #endif
+ GTP_DEBUG("I2C communication timeout, resetting chip...");
+ gtp_reset_guitar(client, 10);
+ }
+ return ret;
+}
+/*******************************************************
+Function:
+ i2c read twice, compare the results
+Input:
+ client: i2c device
+ addr: operate address
+ rxbuf: read data to store, if compare successful
+ len: bytes to read
+Output:
+ FAIL: read failed
+ SUCCESS: read successful
+*********************************************************/
+s32 gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr, u8 *rxbuf, int len)
+{
+ u8 buf[16] = {0};
+ u8 confirm_buf[16] = {0};
+ u8 retry = 0;
+
+ while (retry++ < 3)
+ {
+ memset(buf, 0xAA, 16);
+ buf[0] = (u8)(addr >> 8);
+ buf[1] = (u8)(addr & 0xFF);
+ gtp_i2c_read(client, buf, len + 2);
+
+ memset(confirm_buf, 0xAB, 16);
+ confirm_buf[0] = (u8)(addr >> 8);
+ confirm_buf[1] = (u8)(addr & 0xFF);
+ gtp_i2c_read(client, confirm_buf, len + 2);
+
+ if (!memcmp(buf, confirm_buf, len+2))
+ {
+ break;
+ }
+ }
+ if (retry < 3)
+ {
+ memcpy(rxbuf, confirm_buf+2, len);
+ return SUCCESS;
+ }
+ else
+ {
+ GTP_ERROR("i2c read 0x%04X, %d bytes, double check failed!", addr, len);
+ return FAIL;
+ }
+}
+
+/*******************************************************
+Function:
+ Send config.
+Input:
+ client: i2c device.
+Output:
+ result of i2c write operation.
+ 1: succeed, otherwise: failed
+*********************************************************/
+s32 gtp_send_cfg(struct goodix_ts_data * ts)
+{
+ s32 ret = 2;
+
+#if GTP_DRIVER_SEND_CFG
+ s32 retry = 0;
+
+ if (ts->fixed_cfg)
+ {
+ GTP_INFO("Ic fixed config, no config sent!");
+ return 2;
+ }
+ GTP_INFO("driver send config");
+ for (retry = 0; retry < 5; retry++)
+ {
+ ret = gtp_i2c_write(ts->client, config , GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+#endif
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Disable irq function
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+void gtp_irq_disable(struct goodix_ts_data *ts)
+{
+ //unsigned long irqflags;
+
+ GTP_DEBUG_FUNC();
+
+ //spin_lock_irqsave(&ts->irq_lock, irqflags);
+ if (!ts->irq_is_disable)
+ {
+ ts->irq_is_disable = 1;
+ //disable_irq_nosync(ts->client->irq);
+ wmt_gpio_mask_irq(ts->irq_gpio);
+ }
+ //spin_unlock_irqrestore(&ts->irq_lock, irqflags);
+}
+
+/*******************************************************
+Function:
+ Enable irq function
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+void gtp_irq_enable(struct goodix_ts_data *ts)
+{
+ //unsigned long irqflags = 0;
+
+ GTP_DEBUG_FUNC();
+
+ //spin_lock_irqsave(&ts->irq_lock, irqflags);
+ if (ts->irq_is_disable)
+ {
+ //enable_irq(ts->client->irq);
+ wmt_gpio_unmask_irq(ts->irq_gpio);
+ ts->irq_is_disable = 0;
+ }
+ //spin_unlock_irqrestore(&ts->irq_lock, irqflags);
+}
+
+
+/*******************************************************
+Function:
+ Report touch point event
+Input:
+ ts: goodix i2c_client private data
+ id: trackId
+ x: input x coordinate
+ y: input y coordinate
+ w: input pressure
+Output:
+ None.
+*********************************************************/
+static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w)
+{
+ s32 px = 0, py = 0;
+#if GTP_CHANGE_X2Y
+ GTP_SWAP(x, y);
+#endif
+
+ if (ts->swap) {
+ px = y;
+ py = x;
+ } else {
+ px = x;
+ py = y;
+ }
+ if (ts->xdir == -1)
+ px = ts->abs_x_max - px;
+ if (ts->ydir == -1)
+ py = ts->abs_y_max - py;
+
+ if (ts->lcd_exchg) {
+ int tmp;
+ tmp = px;
+ px = py;
+ py = ts->abs_x_max - tmp;
+ }
+
+#if GTP_ICS_SLOT_REPORT
+ input_mt_slot(ts->input_dev, id);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+#else
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, px);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, py);
+ //input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
+ //input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+ input_mt_sync(ts->input_dev);
+#endif
+
+ GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, px, py, w);
+}
+
+/*******************************************************
+Function:
+ Report touch release event
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+static void gtp_touch_up(struct goodix_ts_data* ts, s32 id)
+{
+#if GTP_ICS_SLOT_REPORT
+ input_mt_slot(ts->input_dev, id);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
+ GTP_DEBUG("Touch id[%2d] release!", id);
+#else
+ //input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ //input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
+ input_mt_sync(ts->input_dev);
+#endif
+}
+
+
+/*******************************************************
+Function:
+ Goodix touchscreen work function
+Input:
+ work: work struct of goodix_workqueue
+Output:
+ None.
+*********************************************************/
+static void goodix_ts_work_func(struct work_struct *work)
+{
+ u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
+ u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
+ u8 touch_num = 0;
+ u8 finger = 0;
+ static u16 pre_touch = 0;
+ static u8 pre_key = 0;
+#if GTP_WITH_PEN
+ static u8 pre_pen = 0;
+#endif
+ u8 key_value = 0;
+ u8* coor_data = NULL;
+ s32 input_x = 0;
+ s32 input_y = 0;
+ s32 input_w = 0;
+ s32 id = 0;
+ s32 i = 0;
+ s32 ret = -1;
+ struct goodix_ts_data *ts = NULL;
+
+#if GTP_SLIDE_WAKEUP
+ u8 doze_buf[3] = {0x81, 0x4B};
+#endif
+
+ GTP_DEBUG_FUNC();
+ ts = container_of(work, struct goodix_ts_data, work);
+ if (ts->enter_update)
+ {
+ return;
+ }
+#if GTP_SLIDE_WAKEUP
+ if (DOZE_ENABLED == doze_status)
+ {
+ ret = gtp_i2c_read(i2c_connect_client, doze_buf, 3);
+ GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
+ if (ret > 0)
+ {
+ if (doze_buf[2] == 0xAA)
+ {
+ GTP_INFO("Slide(0xAA) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ // clear 0x814B
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(i2c_connect_client, doze_buf, 3);
+ }
+ else if (doze_buf[2] == 0xBB)
+ {
+ GTP_INFO("Slide(0xBB) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ // clear 0x814B
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(i2c_connect_client, doze_buf, 3);
+ }
+ else if (0xC0 == (doze_buf[2] & 0xC0))
+ {
+ GTP_INFO("double click to light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ // clear 0x814B
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(i2c_connect_client, doze_buf, 3);
+ }
+ else
+ {
+ gtp_enter_doze(ts);
+ }
+ }
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+ return;
+ }
+#endif
+
+ ret = gtp_i2c_read(ts->client, point_data, 12);
+ if (ret < 0)
+ {
+ GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
+ goto exit_work_func;
+ }
+
+ finger = point_data[GTP_ADDR_LENGTH];
+ if((finger & 0x80) == 0)
+ {
+ goto exit_work_func;
+ }
+
+ touch_num = finger & 0x0f;
+ if (touch_num > GTP_MAX_TOUCH)
+ {
+ goto exit_work_func;
+ }
+
+ if (touch_num > 1)
+ {
+ u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
+
+ ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
+ memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
+ }
+
+#if GTP_HAVE_TOUCH_KEY
+ key_value = point_data[3 + 8 * touch_num];
+
+ if(key_value || pre_key)
+ {
+ for (i = 0; i < GTP_MAX_KEY_NUM; i++)
+ {
+ #if GTP_DEBUG_ON
+ for (ret = 0; ret < 4; ++ret)
+ {
+ if (key_codes[ret] == touch_key_array[i])
+ {
+ GTP_DEBUG("Key: %s %s", key_names[ret], (key_value & (0x01 << i)) ? "Down" : "Up");
+ break;
+ }
+ }
+ #endif
+ input_report_key(ts->input_dev, touch_key_array[i], key_value & (0x01<<i));
+ }
+ touch_num = 0;
+ pre_touch = 0;
+ }
+#endif
+ pre_key = key_value;
+
+ GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);
+
+#if GTP_ICS_SLOT_REPORT
+
+#if GTP_WITH_PEN
+ if (pre_pen && (touch_num == 0))
+ {
+ GTP_DEBUG("Pen touch UP(Slot)!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
+ pre_pen = 0;
+ }
+#endif
+ if (pre_touch || touch_num)
+ {
+ s32 pos = 0;
+ u16 touch_index = 0;
+
+ coor_data = &point_data[3];
+
+ if(touch_num)
+ {
+ id = coor_data[pos] & 0x0F;
+
+ #if GTP_WITH_PEN
+ id = coor_data[pos];
+ if ((id == 128))
+ {
+ GTP_DEBUG("Pen touch DOWN(Slot)!");
+ input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
+ input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
+ input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);
+
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 1);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 5);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+ GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]", input_x, input_y, input_w);
+ pre_pen = 1;
+ pre_touch = 0;
+ }
+ #endif
+
+ touch_index |= (0x01<<id);
+ }
+
+ GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",id, touch_index,pre_touch);
+ for (i = 0; i < GTP_MAX_TOUCH; i++)
+ {
+ #if GTP_WITH_PEN
+ if (pre_pen == 1)
+ {
+ break;
+ }
+ #endif
+
+ if (touch_index & (0x01<<i))
+ {
+ input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
+ input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
+ input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);
+
+ gtp_touch_down(ts, id, input_x, input_y, input_w);
+ pre_touch |= 0x01 << i;
+
+ pos += 8;
+ id = coor_data[pos] & 0x0F;
+ touch_index |= (0x01<<id);
+ }
+ else
+ {
+ gtp_touch_up(ts, i);
+ pre_touch &= ~(0x01 << i);
+ }
+ }
+ }
+#else
+ //input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value));
+ if (touch_num)
+ {
+ for (i = 0; i < touch_num; i++)
+ {
+ coor_data = &point_data[i * 8 + 3];
+
+ id = coor_data[0]; // & 0x0F;
+ input_x = coor_data[1] | (coor_data[2] << 8);
+ input_y = coor_data[3] | (coor_data[4] << 8);
+ input_w = coor_data[5] | (coor_data[6] << 8);
+
+ #if GTP_WITH_PEN
+ if (id == 128)
+ {
+ GTP_DEBUG("Pen touch DOWN!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 1);
+ pre_pen = 1;
+ id = 0;
+ }
+ #endif
+
+ gtp_touch_down(ts, id, input_x, input_y, input_w);
+ }
+ }
+ else if (pre_touch)
+ {
+ #if GTP_WITH_PEN
+ if (pre_pen == 1)
+ {
+ GTP_DEBUG("Pen touch UP!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
+ pre_pen = 0;
+ }
+ #endif
+
+ GTP_DEBUG("Touch Release!");
+ gtp_touch_up(ts, 0);
+ }
+
+ pre_touch = touch_num;
+#endif
+
+ input_sync(ts->input_dev);
+
+exit_work_func:
+ if(!ts->gtp_rawdiff_mode)
+ {
+ ret = gtp_i2c_write(ts->client, end_cmd, 3);
+ if (ret < 0)
+ {
+ GTP_INFO("I2C write end_cmd error!");
+ }
+ }
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+}
+
+/*******************************************************
+Function:
+ Timer interrupt service routine for polling mode.
+Input:
+ timer: timer struct pointer
+Output:
+ Timer work mode.
+ HRTIMER_NORESTART: no restart mode
+*********************************************************/
+static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
+{
+ struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer);
+
+ GTP_DEBUG_FUNC();
+
+ queue_work(goodix_wq, &ts->work);
+ hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME+6)*1000000), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+/*******************************************************
+Function:
+ External interrupt service routine for interrupt mode.
+Input:
+ irq: interrupt number.
+ dev_id: private data pointer
+Output:
+ Handle Result.
+ IRQ_HANDLED: interrupt handled successfully
+*********************************************************/
+static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
+{
+ struct goodix_ts_data *ts = dev_id;
+
+ GTP_DEBUG_FUNC();
+
+ if (gpio_irqstatus(ts->irq_gpio))
+ {
+ wmt_gpio_ack_irq(ts->irq_gpio);
+ if (is_gpio_irqenable(ts->irq_gpio) && l_suspend == 0)
+ {
+ gtp_irq_disable(ts);
+ queue_work(goodix_wq, &ts->work);
+ }
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+/*******************************************************
+Function:
+ Synchronization.
+Input:
+ ms: synchronization time in millisecond.
+Output:
+ None.
+*******************************************************/
+void gtp_int_sync(s32 ms)
+{
+ GTP_GPIO_OUTPUT(l_ts->irq_gpio, 0);
+ msleep(ms);
+ GTP_GPIO_AS_INPUT(l_ts->irq_gpio);
+ //GTP_GPIO_AS_INT(ts->irq_gpio);
+}
+
+/*******************************************************
+Function:
+ Reset chip.
+Input:
+ ms: reset time in millisecond
+Output:
+ None.
+*******************************************************/
+void gtp_reset_guitar(struct i2c_client *client, s32 ms)
+{
+ GTP_DEBUG_FUNC();
+
+ GTP_GPIO_OUTPUT(l_ts->rst_gpio, 0); // begin select I2C slave addr
+ msleep(ms); // T2: > 10ms
+ // HIGH: 0x28/0x29, LOW: 0xBA/0xBB
+ GTP_GPIO_OUTPUT(l_ts->irq_gpio, client->addr == 0x14);
+
+ msleep(2); // T3: > 100us
+ GTP_GPIO_OUTPUT(l_ts->rst_gpio, 1);
+
+ msleep(6); // T4: > 5ms
+
+ GTP_GPIO_AS_INPUT(l_ts->rst_gpio); // end select I2C slave addr
+
+ gtp_int_sync(50);
+
+#if GTP_ESD_PROTECT
+ gtp_init_ext_watchdog(client);
+#endif
+}
+
+#if GTP_SLIDE_WAKEUP
+/*******************************************************
+Function:
+ Enter doze mode for sliding wakeup.
+Input:
+ ts: goodix tp private data
+Output:
+ 1: succeed, otherwise failed
+*******************************************************/
+static s8 gtp_enter_doze(struct goodix_ts_data *ts)
+{
+ s8 ret = -1;
+ s8 retry = 0;
+ u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8};
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_DBL_CLK_WAKEUP
+ i2c_control_buf[2] = 0x09;
+#endif
+
+ gtp_irq_disable(ts);
+
+ GTP_DEBUG("entering doze mode...");
+ while(retry++ < 5)
+ {
+ i2c_control_buf[0] = 0x80;
+ i2c_control_buf[1] = 0x46;
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret < 0)
+ {
+ GTP_DEBUG("failed to set doze flag into 0x8046, %d", retry);
+ continue;
+ }
+ i2c_control_buf[0] = 0x80;
+ i2c_control_buf[1] = 0x40;
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret > 0)
+ {
+ doze_status = DOZE_ENABLED;
+ GTP_INFO("GTP has been working in doze mode!");
+ gtp_irq_enable(ts);
+ return ret;
+ }
+ msleep(10);
+ }
+ GTP_ERROR("GTP send doze cmd failed.");
+ gtp_irq_enable(ts);
+ return ret;
+}
+#else
+/*******************************************************
+Function:
+ Enter sleep mode.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ 1: succeed, otherwise failed.
+*******************************************************/
+#if 0
+static s8 gtp_enter_sleep(struct goodix_ts_data * ts)
+{
+ s8 ret = -1;
+ s8 retry = 0;
+ u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5};
+
+ GTP_DEBUG_FUNC();
+
+ GTP_GPIO_OUTPUT(ts->irq_gpio, 0);
+ msleep(5);
+
+ while(retry++ < 5)
+ {
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret > 0)
+ {
+ GTP_INFO("GTP enter sleep!");
+
+ return ret;
+ }
+ msleep(10);
+ }
+ GTP_ERROR("GTP send sleep cmd failed.");
+ return ret;
+}
+#endif
+#endif
+/*******************************************************
+Function:
+ Wakeup from sleep.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ >0: succeed, otherwise: failed.
+*******************************************************/
+#if 0
+static s8 gtp_wakeup_sleep(struct goodix_ts_data * ts)
+{
+ u8 retry = 0;
+ s8 ret = -1;
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_POWER_CTRL_SLEEP
+ while(retry++ < 5)
+ {
+ gtp_reset_guitar(ts->client, 20);
+
+ ret = gtp_send_cfg(ts);
+ if (ret < 0)
+ {
+ GTP_INFO("Wakeup sleep send config failed!");
+ continue;
+ }
+ GTP_INFO("GTP wakeup sleep");
+ return 1;
+ }
+#else
+ while(retry++ < 10)
+ {
+ #if GTP_SLIDE_WAKEUP
+ if (DOZE_WAKEUP != doze_status) // wakeup not by slide
+ {
+ gtp_reset_guitar(ts->client, 10);
+ }
+ else // wakeup by slide
+ {
+ doze_status = DOZE_DISABLED;
+ }
+ #else
+ if (chip_gt9xxs == 1)
+ {
+ gtp_reset_guitar(ts->client, 10);
+ }
+ else
+ {
+ GTP_GPIO_OUTPUT(ts->irq_gpio, 1);
+ msleep(5);
+ }
+ #endif
+ ret = gtp_i2c_test(ts->client);
+ if (ret > 0)
+ {
+ GTP_INFO("GTP wakeup sleep.");
+
+ #if (!GTP_SLIDE_WAKEUP)
+ if (chip_gt9xxs == 0)
+ {
+ gtp_int_sync(25);
+ msleep(20);
+ #if GTP_ESD_PROTECT
+ gtp_init_ext_watchdog(ts->client);
+ #endif
+ }
+ #endif
+ return ret;
+ }
+ gtp_reset_guitar(ts->client, 20);
+ }
+#endif
+
+ GTP_ERROR("GTP wakeup sleep failed.");
+ return ret;
+}
+#endif
+
+static int wmt_ts_load_firmware(char* firmwarename, unsigned char* firmdata)
+{
+ struct file *fp;
+ mm_segment_t fs;
+ loff_t pos;
+ long fsize;
+ int alloclen;
+ char filepath[64];
+
+ sprintf(filepath, "/system/etc/firmware/%s", firmwarename);
+ printk("ts firmware file:%s\n",filepath);
+
+ fp = filp_open(filepath, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ printk("create file error\n");
+ return -1;
+ }
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ alloclen = fp->f_op->llseek(fp, 0, SEEK_END);
+ printk("firmware file lengh:0x%x,\n", alloclen);
+ alloclen += alloclen%4;
+
+ fp->f_op->llseek(fp,0,0);
+ pos = 0;
+ fsize = vfs_read(fp, firmdata, alloclen, &pos);
+ printk("filesize:0x%ld,alloclen:0x%d\n",fsize,alloclen);
+ if (fsize <= 0)
+ {
+ printk("alloc size is too small.\n");
+ goto error_vfs_read;
+ }
+ filp_close(fp, NULL);
+ set_fs(fs);
+ printk("success to read firmware file!\n");;
+
+ return 0;
+error_vfs_read:
+ filp_close(fp, NULL);
+ set_fs(fs);
+ return -1;
+}
+
+static int read_cfg(char* cfgname, u8* cfg, int len_max)
+{
+ char endflag[]="/* End flag */";
+ unsigned char* p;
+ int val;
+ int i = 0;
+ unsigned char *rawdata;
+
+ rawdata = kzalloc(1024, GFP_KERNEL);
+ if (rawdata == NULL)
+ {
+ printk("Error when alloc memory for firmware file!\n");
+ return -ENOMEM;
+ }
+
+ if (wmt_ts_load_firmware(cfgname, rawdata))
+ return -1;
+
+ p = rawdata;
+ while (*p!='{') p++;
+ p++;
+
+ while (strncmp(p,endflag,strlen(endflag)))
+ {
+ if (!strncmp(p,"0x",strlen("0x")))
+ {
+ sscanf(p,"%x,",&val);
+ *(cfg++) = val&0x00FF;
+ i++;
+ }
+ if (i == len_max)
+ break;
+ p++;
+
+ };
+
+ kfree(rawdata);
+
+ return i;
+}
+
+/*******************************************************
+Function:
+ Initialize gtp.
+Input:
+ ts: goodix private data
+Output:
+ Executive outcomes.
+ 0: succeed, otherwise: failed
+*******************************************************/
+static s32 gtp_init_panel(struct goodix_ts_data *ts)
+{
+ s32 ret = -1;
+
+#if GTP_DRIVER_SEND_CFG
+ s32 i;
+ u8 check_sum = 0;
+ u8 opr_buf[16];
+ u8 sensor_id = 0;
+
+ u8 send_cfg_buf[256] = {0};
+ char cfgname[32] = {0};
+
+ ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
+ if (SUCCESS == ret)
+ {
+ if (opr_buf[0] != 0xBE)
+ {
+ ts->fw_error = 1;
+ GTP_ERROR("Firmware error, no config sent!");
+ return -1;
+ }
+ }
+
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1);
+ if (SUCCESS == ret)
+ {
+ if (sensor_id >= 0x06)
+ {
+ //GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id);
+ //return -1;
+ GTP_ERROR("Invalid sensor_id(0x%02X), Force set id to 0!", sensor_id);
+ sensor_id = 0;
+ }
+ }
+ else
+ {
+ GTP_ERROR("Failed to get sensor_id, No config sent!");
+ return -1;
+ }
+ GTP_DEBUG("Sensor_ID: %d", sensor_id);
+
+ sprintf(cfgname, "%s_id%d.cfg", ts->fw_name, sensor_id);
+ GTP_INFO("config file name: %s.", cfgname);
+ ret = read_cfg(cfgname, send_cfg_buf, 256);
+ if (ret < 0)
+ return -1;
+ ts->gtp_cfg_len = ret;
+
+ if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH)
+ {
+ GTP_ERROR("INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!");
+ return -1;
+ }
+
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1);
+
+ if (ret == SUCCESS)
+ {
+ GTP_DEBUG("Config Version: %d, 0x%02X; IC Config Version: %d, 0x%02X",
+ send_cfg_buf[0], send_cfg_buf[0], opr_buf[0], opr_buf[0]);
+
+ if (opr_buf[0] < 90)
+ {
+ grp_cfg_version = send_cfg_buf[0]; // backup group config version
+ send_cfg_buf[0] = 0x00;
+ ts->fixed_cfg = 0;
+ }
+ else // treated as fixed config, not send config
+ {
+ GTP_INFO("Ic fixed config with config version(%d, 0x%02X)", opr_buf[0], opr_buf[0]);
+ ts->fixed_cfg = 1;
+ }
+ }
+ else
+ {
+ GTP_ERROR("Failed to get ic config version!No config sent!");
+ return -1;
+ }
+
+ memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
+ memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf, ts->gtp_cfg_len);
+
+#if GTP_CUSTOM_CFG
+ config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
+ config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
+ config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
+ config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
+
+ if (GTP_INT_TRIGGER == 0) //RISING
+ {
+ config[TRIGGER_LOC] &= 0xfe;
+ }
+ else if (GTP_INT_TRIGGER == 1) //FALLING
+ {
+ config[TRIGGER_LOC] |= 0x01;
+ }
+#endif // GTP_CUSTOM_CFG
+
+ check_sum = 0;
+ for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
+ {
+ check_sum += config[i];
+ }
+ config[ts->gtp_cfg_len] = (~check_sum) + 1;
+
+#else // DRIVER NOT SEND CONFIG
+ ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
+ ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read Config Failed, Using Default Resolution & INT Trigger!");
+ ts->abs_x_max = GTP_MAX_WIDTH;
+ ts->abs_y_max = GTP_MAX_HEIGHT;
+ ts->int_trigger_type = GTP_INT_TRIGGER;
+ }
+#endif // GTP_DRIVER_SEND_CFG
+
+ GTP_DEBUG_FUNC();
+ if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0))
+ {
+ ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC];
+ ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2];
+ ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
+ }
+
+ ret = gtp_send_cfg(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("Send config error.");
+ }
+ //GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
+ //ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type);
+ GTP_INFO("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x(%s).",
+ ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type, ts->int_trigger_type?"Falling":"Rising");
+
+ msleep(10);
+ return 0;
+}
+
+/*******************************************************
+Function:
+ Read chip version.
+Input:
+ client: i2c device
+ version: buffer to keep ic firmware version
+Output:
+ read operation return.
+ 2: succeed, otherwise: failed
+*******************************************************/
+s32 gtp_read_version(struct i2c_client *client, u16* version)
+{
+ s32 ret = -1;
+ u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};
+
+ GTP_DEBUG_FUNC();
+
+ ret = gtp_i2c_read(client, buf, sizeof(buf));
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP read version failed");
+ return ret;
+ }
+
+ if (version)
+ {
+ *version = (buf[7] << 8) | buf[6];
+ }
+
+ if (buf[5] == 0x00)
+ {
+ GTP_INFO("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
+ }
+ else
+ {
+ if (buf[5] == 'S' || buf[5] == 's')
+ {
+ chip_gt9xxs = 1;
+ }
+ GTP_INFO("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ I2c test Function.
+Input:
+ client:i2c client.
+Output:
+ Executive outcomes.
+ 2: succeed, otherwise failed.
+*******************************************************/
+static s8 gtp_i2c_test(struct i2c_client *client)
+{
+ u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
+ u8 retry = 0;
+ s8 ret = -1;
+
+ GTP_DEBUG_FUNC();
+
+ while(retry++ < 5)
+ {
+ ret = gtp_i2c_read(client, test, 3);
+ if (ret > 0)
+ {
+ return ret;
+ }
+ GTP_ERROR("GTP i2c test failed time %d.",retry);
+ msleep(10);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Request gpio(INT & RST) ports.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ >= 0: succeed, < 0: failed
+*******************************************************/
+static s8 gtp_request_io_port(struct goodix_ts_data *ts)
+{
+ s32 ret = 0;
+
+ ret = GTP_GPIO_REQUEST(ts->irq_gpio, "GTP_INT_IRQ");
+ if (ret < 0)
+ {
+ GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d", (s32)ts->irq_gpio, ret);
+ ret = -ENODEV;
+ }
+ else
+ {
+ GTP_GPIO_AS_INPUT(ts->irq_gpio);
+ //GTP_GPIO_AS_INT(ts->irq_gpio);
+ //ts->client->irq = IRQ_GPIO;
+ }
+
+ ret = GTP_GPIO_REQUEST(ts->rst_gpio, "GTP_RST_PORT");
+ if (ret < 0)
+ {
+ GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d",(s32)ts->rst_gpio,ret);
+ ret = -ENODEV;
+ }
+
+ GTP_GPIO_AS_INPUT(ts->rst_gpio);
+ gtp_reset_guitar(ts->client, 20);
+
+
+ if(ret < 0)
+ {
+ GTP_GPIO_FREE(ts->rst_gpio);
+ GTP_GPIO_FREE(ts->irq_gpio);
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Request interrupt.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ 0: succeed, -1: failed.
+*******************************************************/
+static s8 gtp_request_irq(struct goodix_ts_data *ts)
+{
+ s32 ret = -1;
+ //const u8 irq_table[] = GTP_IRQ_TAB;
+
+ GTP_DEBUG("INT trigger type:%x", ts->int_trigger_type);
+
+ ret = request_irq(ts->client->irq,
+ goodix_ts_irq_handler,
+ IRQF_SHARED,
+ ts->client->name,
+ ts);
+ if (ret)
+ {
+ GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret);
+ GTP_GPIO_AS_INPUT(ts->irq_gpio);
+ GTP_GPIO_FREE(ts->irq_gpio);
+
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = goodix_ts_timer_handler;
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ return -1;
+ }
+ else
+ {
+ gtp_irq_disable(ts);
+ ts->use_irq = 1;
+ return 0;
+ }
+}
+
+/*******************************************************
+Function:
+ Request input device Function.
+Input:
+ ts:private data.
+Output:
+ Executive outcomes.
+ 0: succeed, otherwise: failed.
+*******************************************************/
+static s8 gtp_request_input_dev(struct goodix_ts_data *ts)
+{
+ s8 ret = -1;
+ s8 phys[32];
+#if GTP_HAVE_TOUCH_KEY
+ u8 index = 0;
+#endif
+
+ GTP_DEBUG_FUNC();
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL)
+ {
+ GTP_ERROR("Failed to allocate input device.");
+ return -ENOMEM;
+ }
+
+ ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
+ set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+#if GTP_ICS_SLOT_REPORT
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ input_mt_init_slots(ts->input_dev, 10); // in case of "out of memory"
+#else
+ //ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+#endif
+
+#if GTP_HAVE_TOUCH_KEY
+ for (index = 0; index < GTP_MAX_KEY_NUM; index++)
+ {
+ input_set_capability(ts->input_dev, EV_KEY, touch_key_array[index]);
+ }
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
+#endif
+
+#if GTP_WITH_PEN
+ // pen support
+ __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
+#endif
+
+#if GTP_CHANGE_X2Y
+ GTP_SWAP(ts->abs_x_max, ts->abs_y_max);
+#endif
+
+ if (ts->swap) {
+ s32 temp;
+ temp = ts->abs_x_max;
+ ts->abs_x_max = ts->abs_y_max;
+ ts->abs_y_max = temp;
+ }
+
+ if (ts->lcd_exchg) {
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_x_max, 0, 0);
+ } else {
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
+ }
+ //input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ //input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);
+
+ sprintf(phys, "input/ts");
+ ts->input_dev->name = goodix_ts_name;
+ ts->input_dev->phys = phys;
+ ts->input_dev->id.bustype = BUS_I2C;
+ ts->input_dev->id.vendor = 0xDEAD;
+ ts->input_dev->id.product = 0xBEEF;
+ ts->input_dev->id.version = 10427;
+
+ ret = input_register_device(ts->input_dev);
+ if (ret)
+ {
+ GTP_ERROR("Register %s input device failed", ts->input_dev->name);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = goodix_ts_early_suspend;
+ ts->early_suspend.resume = goodix_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ return 0;
+}
+
+
+static int wmt_check_touch_env(struct goodix_ts_data *ts)
+{
+ int ret = 0;
+ int len = 127;
+ char retval[128] = {0},*p=NULL,*s=NULL;
+ int Enable=0;
+ int x,y;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ GTP_ERROR("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+
+ //check touch enable
+ p = retval;
+ sscanf(p,"%d:", &Enable);
+ if(Enable == 0){
+ GTP_ERROR("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ //check touch IC name
+ p = strchr(p,':');p++;
+ if (strncmp(p, "gt9xx", strlen("gt9xx"))) {
+ GTP_ERROR("Can't find gt9xx!\n");
+ return -ENODEV;
+ }
+
+ //get firmware file name
+ s = strchr(p,':');
+ //p = p + strlen(fw_name) + 1;
+ if (s > (p + strlen("gt9xx") + 1)) {
+ memset(ts->fw_name,0x00,sizeof(ts->fw_name));
+ strncpy(ts->fw_name, p, (s-p));
+ GTP_DEBUG("ts_fwname=%s\n", ts->fw_name);
+ } else
+ GTP_DEBUG("needn't firmware\n");
+
+ //get other needed args
+ p = s + 1;
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%x",
+ &ts->irq_gpio,&x,&y,&ts->rst_gpio,
+ &ts->swap,&ts->xdir,&ts->ydir,
+ &ts->max_touch_num,
+ &ts->i2c_addr);
+ if (ret != 9)
+ {
+ GTP_ERROR("Wrong format ts u-boot param(%d)!\n",ret);
+ return -ENODEV;
+ }
+
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ ts->lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+/*******************************************************
+Function:
+ I2c probe.
+Input:
+ client: i2c device struct.
+ id: device id.
+Output:
+ Executive outcomes.
+ 0: succeed.
+*******************************************************/
+static int goodix_ts_probe(struct platform_device *pdev)
+{
+ s32 ret = -1;
+ struct goodix_ts_data *ts;
+ u16 version_info;
+
+ GTP_DEBUG_FUNC();
+
+ //do NOT remove these logs
+ GTP_INFO("GTP Driver Version: %s", GTP_DRIVER_VERSION);
+ GTP_INFO("GTP Driver Built@%s, %s", __TIME__, __DATE__);
+ GTP_INFO("GTP I2C Address: 0x%02x", i2c_connect_client->addr);
+
+ //i2c_connect_client = client;
+
+ if (!i2c_check_functionality(i2c_connect_client->adapter, I2C_FUNC_I2C))
+ {
+ GTP_ERROR("I2C check functionality failed.");
+ return -ENODEV;
+ }
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL)
+ {
+ GTP_ERROR("Alloc GFP_KERNEL memory failed.");
+ return -ENOMEM;
+ }
+ memset(ts, 0, sizeof(*ts));
+ l_ts = ts;
+
+ ret = wmt_check_touch_env(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP get touch env failed.");
+ kfree(ts);
+ return ret;
+ }
+
+ i2c_connect_client->addr = ts->i2c_addr;
+ INIT_WORK(&ts->work, goodix_ts_work_func);
+ ts->client = i2c_connect_client;
+ //spin_lock_init(&ts->irq_lock); // 2.6.39 later
+ // ts->irq_lock = SPIN_LOCK_UNLOCKED; // 2.6.39 & before
+ platform_set_drvdata(pdev, ts);
+
+ ts->gtp_rawdiff_mode = 0;
+
+ ret = gtp_request_io_port(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP request IO port failed.");
+ kfree(ts);
+ return ret;
+ }
+
+ ret = gtp_i2c_test(ts->client);
+ if (ret < 0)
+ {
+ GTP_ERROR("I2C communication ERROR!");
+ }
+
+#if GTP_AUTO_UPDATE
+ ret = gup_init_update_proc(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("Create update thread error.");
+ }
+#endif
+
+ ret = gtp_init_panel(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP init panel failed.");
+ ts->abs_x_max = GTP_MAX_WIDTH;
+ ts->abs_y_max = GTP_MAX_HEIGHT;
+ ts->int_trigger_type = GTP_INT_TRIGGER;
+ }
+
+ ret = gtp_request_input_dev(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP request input dev failed");
+ }
+ GTP_GPIO_AS_INT(ts->irq_gpio,IRQ_TYPE_EDGE_FALLING);
+ ts->client->irq = IRQ_GPIO;
+ ret = gtp_request_irq(ts);
+ if (ret < 0)
+ {
+ GTP_INFO("GTP works in polling mode.");
+ }
+ else
+ {
+ GTP_INFO("GTP works in interrupt mode.");
+ }
+
+ ret = gtp_read_version(ts->client, &version_info);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read version failed.");
+ }
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+
+#if GTP_CREATE_WR_NODE
+ init_wr_node(ts->client);
+#endif
+
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(ts, SWITCH_ON);
+#endif
+ return 0;
+}
+
+
+/*******************************************************
+Function:
+ Goodix touchscreen driver release function.
+Input:
+ client: i2c device struct.
+Output:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int goodix_ts_remove(struct platform_device *pdev)
+{
+ struct goodix_ts_data *ts = platform_get_drvdata(pdev);
+
+ GTP_DEBUG_FUNC();
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#if GTP_CREATE_WR_NODE
+ uninit_wr_node();
+#endif
+
+#if GTP_ESD_PROTECT
+ destroy_workqueue(gtp_esd_check_workqueue);
+#endif
+
+ if (ts)
+ {
+ if (ts->use_irq)
+ {
+ GTP_GPIO_AS_INPUT(ts->irq_gpio);
+ GTP_GPIO_FREE(ts->irq_gpio);
+ free_irq(ts->client->irq, ts);
+ }
+ else
+ {
+ hrtimer_cancel(&ts->timer);
+ }
+ }
+
+ GTP_GPIO_FREE(ts->rst_gpio);
+ GTP_INFO("GTP driver removing...");
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*******************************************************
+Function:
+ Early suspend function.
+Input:
+ h: early_suspend struct.
+Output:
+ None.
+*******************************************************/
+static void goodix_ts_early_suspend(struct early_suspend *h)
+{
+ struct goodix_ts_data *ts;
+ s8 ret = -1;
+ ts = container_of(h, struct goodix_ts_data, early_suspend);
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 1;
+ gtp_esd_switch(ts, SWITCH_OFF);
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ ret = gtp_enter_doze(ts);
+#else
+ if (ts->use_irq)
+ {
+ gtp_irq_disable(ts);
+ }
+ else
+ {
+ hrtimer_cancel(&ts->timer);
+ }
+ ret = gtp_enter_sleep(ts);
+#endif
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP early suspend failed.");
+ }
+ // to avoid waking up while not sleeping
+ // delay 48 + 10ms to ensure reliability
+ msleep(58);
+}
+
+/*******************************************************
+Function:
+ Late resume function.
+Input:
+ h: early_suspend struct.
+Output:
+ None.
+*******************************************************/
+static void goodix_ts_late_resume(struct early_suspend *h)
+{
+ struct goodix_ts_data *ts;
+ s8 ret = -1;
+ ts = container_of(h, struct goodix_ts_data, early_suspend);
+
+ GTP_DEBUG_FUNC();
+
+ ret = gtp_wakeup_sleep(ts);
+
+#if GTP_SLIDE_WAKEUP
+ doze_status = DOZE_DISABLED;
+#endif
+
+ if (ret < 0)
+ {
+ GTP_ERROR("GTP later resume failed.");
+ }
+
+ if (ts->use_irq)
+ {
+ gtp_irq_enable(ts);
+ }
+ else
+ {
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 0;
+ gtp_esd_switch(ts, SWITCH_ON);
+#endif
+}
+#endif
+
+#if 1
+/*******************************************************
+Function:
+ Suspend function.
+Input:
+ client: i2c_client struct.
+ mesg: pm_message_t struct.
+Output:
+ None.
+*******************************************************/
+static int goodix_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct goodix_ts_data *ts;
+ ts = dev_get_drvdata(&pdev->dev);
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 1;
+ gtp_esd_switch(ts, SWITCH_OFF);
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ ret = gtp_enter_doze(ts);
+#else
+ if (ts->use_irq)
+ {
+ gtp_irq_disable(ts);
+ }
+ else
+ {
+ hrtimer_cancel(&ts->timer);
+ }
+#endif
+ // to avoid waking up while not sleeping
+ // delay 48 + 10ms to ensure reliability
+ l_suspend = 1;
+ return 0;
+}
+
+
+/*******************************************************
+Function:
+ Late resume function.
+Input:
+ client: i2c_client struct.
+Output:
+ None.
+*******************************************************/
+static int goodix_ts_resume(struct platform_device *pdev)
+{
+ struct goodix_ts_data *ts;
+ s8 ret = -1;
+ ts = dev_get_drvdata(&pdev->dev);
+
+ GTP_DEBUG_FUNC();
+
+ GTP_GPIO_AS_INPUT(ts->irq_gpio);
+ GTP_GPIO_AS_INPUT(ts->rst_gpio);
+ gtp_reset_guitar(ts->client, 20);
+
+ ret = gtp_i2c_test(ts->client);
+ if (ret < 0)
+ {
+ GTP_ERROR("I2C communication ERROR!");
+ }
+
+#if GTP_SLIDE_WAKEUP
+ doze_status = DOZE_DISABLED;
+#endif
+
+ if (ts->use_irq)
+ {
+ GTP_GPIO_AS_INT(ts->irq_gpio,IRQ_TYPE_EDGE_FALLING);
+ gtp_irq_enable(ts);
+ }
+ else
+ {
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 0;
+ gtp_esd_switch(ts, SWITCH_ON);
+#endif
+ l_suspend = 0;
+ return 0;
+}
+#endif
+
+#if GTP_ESD_PROTECT
+/*******************************************************
+Function:
+ switch on & off esd delayed work
+Input:
+ client: i2c device
+ on: SWITCH_ON / SWITCH_OFF
+Output:
+ void
+*********************************************************/
+void gtp_esd_switch(struct goodix_ts_data * ts, s32 on)
+{
+
+ if (SWITCH_ON == on) // switch on esd
+ {
+ if (!ts->esd_running)
+ {
+ ts->esd_running = 1;
+ GTP_INFO("Esd started");
+ queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
+ }
+ }
+ else // switch off esd
+ {
+ if (ts->esd_running)
+ {
+ ts->esd_running = 0;
+ GTP_INFO("Esd cancelled");
+ cancel_delayed_work_sync(&gtp_esd_check_work);
+ }
+ }
+}
+
+/*******************************************************
+Function:
+ Initialize external watchdog for esd protect
+Input:
+ client: i2c device.
+Output:
+ result of i2c write operation.
+ 1: succeed, otherwise: failed
+*********************************************************/
+static s32 gtp_init_ext_watchdog(struct i2c_client *client)
+{
+ u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA};
+
+ struct i2c_msg msg; // in case of recursively reset by calling gtp_i2c_write
+ s32 ret = -1;
+ s32 retries = 0;
+
+ GTP_DEBUG("Init external watchdog...");
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = 4;
+ msg.buf = opr_buffer;
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)
+ {
+ return 1;
+ }
+ retries++;
+ }
+ if (retries >= 5)
+ {
+ GTP_ERROR("init external watchdog failed!");
+ }
+ return 0;
+}
+
+/*******************************************************
+Function:
+ Esd protect function.
+ Added external watchdog by meta, 2013/03/07
+Input:
+ work: delayed work
+Output:
+ None.
+*******************************************************/
+static void gtp_esd_check_func(struct work_struct *work)
+{
+ s32 i;
+ s32 ret = -1;
+ struct goodix_ts_data *ts = NULL;
+ u8 test[4] = {0x80, 0x40};
+
+ GTP_DEBUG_FUNC();
+
+ ts = container_of(work, struct goodix_ts_data, work);
+
+ if (ts->gtp_is_suspend)
+ {
+ ts->esd_running = 0;
+ GTP_INFO("Esd terminated!");
+ return;
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ ret = gtp_i2c_read(ts->client, test, 4);
+
+ GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]);
+ if ((ret < 0))
+ {
+ // IIC communication problem
+ continue;
+ }
+ else
+ {
+ if ((test[2] == 0xAA) || (test[3] != 0xAA))
+ {
+ // IC works abnormally..
+ i = 3;
+ break;
+ }
+ else
+ {
+ // IC works normally, Write 0x8040 0xAA, feed the dog
+ test[2] = 0xAA;
+ gtp_i2c_write(ts->client, test, 3);
+ break;
+ }
+ }
+ }
+ if (i >= 3)
+ {
+ GTP_ERROR("IC Working ABNORMALLY, Resetting Guitar...");
+ gtp_reset_guitar(ts->client, 50);
+ }
+
+ if(!ts->gtp_is_suspend)
+ {
+ queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
+ }
+ else
+ {
+ GTP_INFO("Esd terminated!");
+ ts->esd_running = 0;
+ }
+ return;
+}
+#endif
+
+static void gt9xx_release(struct device *device)
+{
+ return;
+}
+
+
+static struct platform_device gt9xx_device = {
+ .name = GTP_I2C_NAME,
+ .id = 0,
+ .dev = {.release = gt9xx_release},
+};
+
+static struct platform_driver gt9xx_driver = {
+ .driver = {
+ .name = GTP_I2C_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = goodix_ts_probe,
+ .remove = goodix_ts_remove,
+ .suspend = goodix_ts_suspend,
+ .resume = goodix_ts_resume,
+};
+
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = GTP_I2C_NAME,
+ .flags = 0x00,
+ .addr = GTP_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(0x01);/*in bus 1*/
+ if (NULL == adapter) {
+ GTP_ERROR("can not get i2c adapter, client address error.");
+ return -1;
+ }
+ i2c_connect_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (i2c_connect_client == NULL) {
+ GTP_ERROR("allocate i2c client failed.");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (i2c_connect_client != NULL)
+ {
+ i2c_unregister_device(i2c_connect_client);
+ i2c_connect_client = NULL;
+ }
+}
+
+/*******************************************************
+Function:
+ Driver Install function.
+Input:
+ None.
+Output:
+ Executive Outcomes. 0---succeed.
+********************************************************/
+static int __devinit goodix_ts_init(void)
+{
+ s32 ret;
+
+ GTP_DEBUG_FUNC();
+ GTP_INFO("GTP driver installing...");
+ goodix_wq = create_singlethread_workqueue("goodix_wq");
+ if (!goodix_wq)
+ {
+ GTP_ERROR("Creat workqueue failed.");
+ return -ENOMEM;
+ }
+#if GTP_ESD_PROTECT
+ INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
+ gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
+#endif
+ if (ts_i2c_register_device()<0)
+ {
+ destroy_workqueue(goodix_wq);
+ GTP_ERROR("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ ret = platform_device_register(&gt9xx_device);
+ if(ret){
+ GTP_ERROR("register platform drivver failed!\n");
+ goto err_register_platdev;
+ }
+
+ ret = platform_driver_register(&gt9xx_driver);
+ if(ret){
+ GTP_ERROR("register platform device failed!\n");
+ goto err_register_platdriver;
+ }
+ return 0;
+err_register_platdriver:
+ platform_device_unregister(&gt9xx_device);
+err_register_platdev:
+ destroy_workqueue(goodix_wq);
+ ts_i2c_unregister_device();
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Driver uninstall function.
+Input:
+ None.
+Output:
+ Executive Outcomes. 0---succeed.
+********************************************************/
+static void __exit goodix_ts_exit(void)
+{
+ GTP_DEBUG_FUNC();
+ GTP_INFO("GTP driver exited.");
+ ts_i2c_unregister_device();
+ platform_driver_unregister(&gt9xx_driver);
+ platform_device_unregister(&gt9xx_device);
+ if (goodix_wq)
+ {
+ destroy_workqueue(goodix_wq);
+ }
+}
+
+late_initcall(goodix_ts_init);
+module_exit(goodix_ts_exit);
+
+MODULE_DESCRIPTION("GTP Series Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/gt9xx_ts/gt9xx.h b/drivers/input/touchscreen/gt9xx_ts/gt9xx.h
new file mode 100755
index 00000000..c58b4800
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx_ts/gt9xx.h
@@ -0,0 +1,278 @@
+/* drivers/input/touchscreen/gt9xx.h
+ *
+ * 2010 - 2013 Goodix Technology.
+ *
+ * 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 a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ */
+
+#ifndef _GOODIX_GT9XX_H_
+#define _GOODIX_GT9XX_H_
+
+#include <linux/kernel.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <mach/gpio.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/platform_device.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+struct goodix_ts_data {
+ //spinlock_t irq_lock;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct hrtimer timer;
+ struct work_struct work;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ char fw_name[20];
+ s32 i2c_addr;
+ s32 irq_is_disable;
+ s32 use_irq;
+ s32 irq_gpio;
+ s32 rst_gpio;
+ s32 abs_x_max;
+ s32 abs_y_max;
+ s32 max_touch_num;
+ u8 int_trigger_type;
+ s32 swap;
+ s32 xdir;
+ s32 ydir;
+ s32 lcd_exchg;
+ u8 green_wake_mode;
+ u8 chip_type;
+ u8 enter_update;
+ u8 gtp_is_suspend;
+ u8 gtp_rawdiff_mode;
+ u8 gtp_cfg_len;
+ u8 fixed_cfg;
+ u8 esd_running;
+ u8 fw_error;
+};
+
+extern u16 show_len;
+extern u16 total_len;
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+//***************************PART1:ON/OFF define*******************************
+#define GTP_CUSTOM_CFG 0
+#define GTP_CHANGE_X2Y 0
+#define GTP_DRIVER_SEND_CFG 1
+#define GTP_HAVE_TOUCH_KEY 0
+#define GTP_POWER_CTRL_SLEEP 0
+#define GTP_ICS_SLOT_REPORT 0
+
+#define GTP_AUTO_UPDATE 1 // auto updated by .bin file as default
+#define GTP_HEADER_FW_UPDATE 0 // auto updated by head_fw_array in gt9xx_firmware.h, function together with GTP_AUTO_UPDATE
+
+#define GTP_CREATE_WR_NODE 1
+#define GTP_ESD_PROTECT 0
+#define GTP_WITH_PEN 0
+
+#define GTP_SLIDE_WAKEUP 0
+#define GTP_DBL_CLK_WAKEUP 0 // double-click wakeup, function together with GTP_SLIDE_WAKEUP
+
+#define GTP_DEBUG_ON 0
+#define GTP_DEBUG_ARRAY_ON 0
+#define GTP_DEBUG_FUNC_ON 0
+
+//*************************** PART2:TODO define **********************************
+// STEP_1(REQUIRED): Define Configuration Information Group(s)
+// Sensor_ID Map:
+/* sensor_opt1 sensor_opt2 Sensor_ID
+ GND GND 0
+ VDDIO GND 1
+ NC GND 2
+ GND NC/300K 3
+ VDDIO NC/300K 4
+ NC NC/300K 5
+*/
+// TODO: define your own default or for Sensor_ID == 0 config here.
+// The predefined one is just a sample config, which is not suitable for your tp in most cases.
+#define CTP_CFG_GROUP1 {\
+ 0x42,0x00,0x03,0x00,0x04,0x0A,0x34,0x00,0x01,0x3F,\
+ 0x28,0x0F,0x50,0x3C,0x03,0x05,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x16,0x18,0x1C,0x14,0x8B,0x2A,0x0E,\
+ 0x2D,0x3D,0x12,0x0C,0x00,0x00,0x00,0x01,0x03,0x1D,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x20,0x3D,0x94,0xC5,0x02,0x08,0x00,0x00,0x04,\
+ 0x9A,0x22,0x00,0x8F,0x26,0x00,0x81,0x2C,0x00,0x77,\
+ 0x32,0x00,0x6E,0x39,0x00,0x6E,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x0A,0x08,0x06,0x04,0x02,0x0C,0x0E,0x10,\
+ 0x12,0x14,0x16,0x18,0x1A,0x1C,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0F,\
+ 0x10,0x12,0x13,0x16,0x18,0x1C,0x1D,0x1E,0x1F,0x20,\
+ 0x21,0x22,0x24,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x9D,0x01\
+ }
+
+/*
+#define CTP_CFG_GROUP1 {\
+ 0x00,0x00,0x03,0x00,0x04,0x0A,0x35,0x00,0x01,0x08,\
+ 0x14,0x05,0x37,0x28,0x03,0x05,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x16,0x18,0x1A,0x14,0x8B,0x2A,0x0E,\
+ 0x63,0x5E,0x31,0x0D,0x00,0x00,0x02,0xB9,0x02,0x2D,\
+ 0x00,0x00,0x00,0x00,0x00,0x03,0x64,0x32,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+ 0x00,0x00,0x0A,0x08,0x06,0x04,0x02,0x0C,0x0E,0x10,\
+ 0x12,0x14,0x16,0x18,0x1A,0x1C,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0F,\
+ 0x10,0x12,0x13,0x16,0x18,0x1C,0x1D,0x1E,0x1F,0x20,\
+ 0x21,0x22,0x24,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
+ 0xFF,0xFF,0xFF,0xFF,0x4A,0x01\
+ }
+*/
+
+// TODO: define your config for Sensor_ID == 1 here, if needed
+#define CTP_CFG_GROUP2 {\
+ }
+// TODO: define your config for Sensor_ID == 2 here, if needed
+#define CTP_CFG_GROUP3 {\
+ }
+
+// TODO: define your config for Sensor_ID == 3 here, if needed
+#define CTP_CFG_GROUP4 {\
+ }
+
+// TODO: define your config for Sensor_ID == 4 here, if needed
+#define CTP_CFG_GROUP5 {\
+ }
+
+// TODO: define your config for Sensor_ID == 5 here, if needed
+#define CTP_CFG_GROUP6 {\
+ }
+
+// STEP_2(REQUIRED): Customize your I/O ports & I/O operations
+#define GTP_RST_PORT S5PV210_GPJ3(6)
+#define GTP_INT_PORT S5PV210_GPH1(3)
+#define GTP_INT_IRQ gpio_to_irq(GTP_INT_PORT)
+#define GTP_INT_CFG S3C_GPIO_SFN(0xF)
+
+#define GTP_GPIO_AS_INPUT(pin) do{\
+ gpio_direction_input(pin);\
+ wmt_gpio_setpull(pin, WMT_GPIO_PULL_NONE);\
+ }while(0)
+#define GTP_GPIO_AS_INT(pin,type) do{\
+ GTP_GPIO_AS_INPUT(pin);\
+ wmt_gpio_set_irq_type(pin,type);\
+ }while(0)
+#define GTP_GPIO_GET_VALUE(pin) gpio_get_value(pin)
+#define GTP_GPIO_OUTPUT(pin,level) gpio_direction_output(pin,level)
+#define GTP_GPIO_REQUEST(pin, label) gpio_request(pin, label)
+#define GTP_GPIO_FREE(pin) gpio_free(pin)
+#define GTP_IRQ_TAB {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH}
+
+// STEP_3(optional): Specify your special config info if needed
+#if GTP_CUSTOM_CFG
+ #define GTP_MAX_HEIGHT 800
+ #define GTP_MAX_WIDTH 480
+ #define GTP_INT_TRIGGER 0 // 0: Rising 1: Falling
+#else
+ #define GTP_MAX_HEIGHT 4096
+ #define GTP_MAX_WIDTH 4096
+ #define GTP_INT_TRIGGER 1
+#endif
+#define GTP_MAX_TOUCH 5
+#define GTP_ESD_CHECK_CIRCLE 2000 // jiffy: ms
+
+// STEP_4(optional): If keys are available and reported as keys, config your key info here
+#if GTP_HAVE_TOUCH_KEY
+ #define GTP_KEY_TAB {KEY_MENU, KEY_HOME, KEY_BACK}
+#endif
+
+//***************************PART3:OTHER define*********************************
+#define GTP_DRIVER_VERSION "V1.8<2013/06/08>"
+#define GTP_I2C_NAME "Goodix-TS"
+#define GTP_I2C_ADDR 0x5d
+#define GTP_POLL_TIME 10 // jiffy: ms
+#define GTP_ADDR_LENGTH 2
+#define GTP_CONFIG_MIN_LENGTH 186
+#define GTP_CONFIG_MAX_LENGTH 240
+#define FAIL 0
+#define SUCCESS 1
+#define SWITCH_OFF 0
+#define SWITCH_ON 1
+
+// Registers define
+#define GTP_READ_COOR_ADDR 0x814E
+#define GTP_REG_SLEEP 0x8040
+#define GTP_REG_SENSOR_ID 0x814A
+#define GTP_REG_CONFIG_DATA 0x8047
+#define GTP_REG_VERSION 0x8140
+
+#define RESOLUTION_LOC 3
+#define TRIGGER_LOC 8
+
+#define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0]))
+// Log define
+#define GTP_INFO(fmt,arg...) printk("<<-GTP-INFO->> "fmt"\n",##arg)
+#define GTP_ERROR(fmt,arg...) printk("<<-GTP-ERROR->> "fmt"\n",##arg)
+#define GTP_DEBUG(fmt,arg...) do{\
+ if(GTP_DEBUG_ON)\
+ printk("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
+ }while(0)
+#define GTP_DEBUG_ARRAY(array, num) do{\
+ s32 i;\
+ u8* a = array;\
+ if(GTP_DEBUG_ARRAY_ON)\
+ {\
+ printk("<<-GTP-DEBUG-ARRAY->>\n");\
+ for (i = 0; i < (num); i++)\
+ {\
+ printk("%02x ", (a)[i]);\
+ if ((i + 1 ) %10 == 0)\
+ {\
+ printk("\n");\
+ }\
+ }\
+ printk("\n");\
+ }\
+ }while(0)
+#define GTP_DEBUG_FUNC() do{\
+ if(GTP_DEBUG_FUNC_ON)\
+ printk("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
+ }while(0)
+#define GTP_SWAP(x, y) do{\
+ typeof(x) z = x;\
+ x = y;\
+ y = z;\
+ }while (0)
+
+//*****************************End of Part III********************************
+
+#endif /* _GOODIX_GT9XX_H_ */
diff --git a/drivers/input/touchscreen/gt9xx_ts/gt9xx_firmware.h b/drivers/input/touchscreen/gt9xx_ts/gt9xx_firmware.h
new file mode 100755
index 00000000..3998bf00
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx_ts/gt9xx_firmware.h
@@ -0,0 +1,6 @@
+// make sense only when GTP_HEADER_FW_UPDATE & GTP_AUTO_UPDATE are enabled
+// define your own firmware array here
+const unsigned char header_fw_array[] =
+{
+
+}; \ No newline at end of file
diff --git a/drivers/input/touchscreen/gt9xx_ts/gt9xx_update.c b/drivers/input/touchscreen/gt9xx_ts/gt9xx_update.c
new file mode 100755
index 00000000..88daf209
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx_ts/gt9xx_update.c
@@ -0,0 +1,1939 @@
+/* drivers/input/touchscreen/gt9xx_update.c
+ *
+ * 2010 - 2012 Goodix Technology.
+ *
+ * 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 a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ * Latest Version:1.6
+ * Author: andrew@goodix.com
+ * Revision Record:
+ * V1.0:
+ * first release. By Andrew, 2012/08/31
+ * V1.2:
+ * add force update,GT9110P pid map. By Andrew, 2012/10/15
+ * V1.4:
+ * 1. add config auto update function;
+ * 2. modify enter_update_mode;
+ * 3. add update file cal checksum.
+ * By Andrew, 2012/12/12
+ * V1.6:
+ * 1. replace guitar_client with i2c_connect_client;
+ * 2. support firmware header array update.
+ * By Meta, 2013/03/11
+ */
+#include <linux/kthread.h>
+#include "gt9xx.h"
+
+#if GTP_HEADER_FW_UPDATE
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include "gt9xx_firmware.h"
+#endif
+
+#define GUP_REG_HW_INFO 0x4220
+#define GUP_REG_FW_MSG 0x41E4
+#define GUP_REG_PID_VID 0x8140
+
+#define GUP_SEARCH_FILE_TIMES 2
+#define UPDATE_FILE_PATH_2 "/system/etc/firmware/_goodix_update_.bin"
+#define UPDATE_FILE_PATH_1 "/extsdcard/_goodix_update_.bin"
+
+#define CONFIG_FILE_PATH_1 "/extsdcard/_goodix_config_.cfg"
+#define CONFIG_FILE_PATH_2 "/system/etc/firmware/_goodix_config_.cfg"
+
+#define FW_HEAD_LENGTH 14
+#define FW_SECTION_LENGTH 0x2000
+#define FW_DSP_ISP_LENGTH 0x1000
+#define FW_DSP_LENGTH 0x1000
+#define FW_BOOT_LENGTH 0x800
+
+#define PACK_SIZE 256
+#define MAX_FRAME_CHECK_TIME 5
+
+#define _bRW_MISCTL__SRAM_BANK 0x4048
+#define _bRW_MISCTL__MEM_CD_EN 0x4049
+#define _bRW_MISCTL__CACHE_EN 0x404B
+#define _bRW_MISCTL__TMR0_EN 0x40B0
+#define _rRW_MISCTL__SWRST_B0_ 0x4180
+#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184
+#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190
+#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218
+#define _rRW_MISCTL__BOOT_CTL_ 0x5094
+
+#define FAIL 0
+#define SUCCESS 1
+
+#pragma pack(1)
+typedef struct
+{
+ u8 hw_info[4]; //hardware info//
+ u8 pid[8]; //product id //
+ u16 vid; //version id //
+}st_fw_head;
+#pragma pack()
+
+typedef struct
+{
+ u8 force_update;
+ u8 fw_flag;
+ struct file *file;
+ struct file *cfg_file;
+ st_fw_head ic_fw_msg;
+ mm_segment_t old_fs;
+}st_update_msg;
+
+st_update_msg update_msg;
+u16 show_len;
+u16 total_len;
+u8 got_file_flag = 0;
+u8 searching_file = 0;
+extern u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH];
+extern void gtp_reset_guitar(struct i2c_client *client, s32 ms);
+extern s32 gtp_send_cfg(struct goodix_ts_data * ts);
+extern struct i2c_client * i2c_connect_client;
+extern struct goodix_ts_data *l_ts;
+extern void gtp_irq_enable(struct goodix_ts_data *ts);
+extern void gtp_irq_disable(struct goodix_ts_data *ts);
+extern s32 gtp_i2c_read_dbl_check(struct i2c_client *, u16, u8 *, int);
+#if GTP_ESD_PROTECT
+extern void gtp_esd_switch(struct i2c_client *, s32);
+#endif
+/*******************************************************
+Function:
+ Read data from the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: read start address.
+ buf[2~len-1]: read data buffer.
+ len: GTP_ADDR_LENGTH + read bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 2: succeed, otherwise: failed
+*********************************************************/
+s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
+{
+ struct i2c_msg msgs[2];
+ s32 ret=-1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = client->addr;
+ msgs[0].len = GTP_ADDR_LENGTH;
+ msgs[0].buf = &buf[0];
+ //msgs[0].scl_rate = 300 * 1000; // for Rockchip
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = client->addr;
+ msgs[1].len = len - GTP_ADDR_LENGTH;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+ //msgs[1].scl_rate = 300 * 1000;
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Write data to the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: write start address.
+ buf[2~len-1]: data buffer
+ len: GTP_ADDR_LENGTH + write bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 1: succeed, otherwise: failed
+*********************************************************/
+s32 gup_i2c_write(struct i2c_client *client,u8 *buf,s32 len)
+{
+ struct i2c_msg msg;
+ s32 ret=-1;
+ s32 retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = len;
+ msg.buf = buf;
+ //msg.scl_rate = 300 * 1000; // for Rockchip
+
+ while(retries < 5)
+ {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)break;
+ retries++;
+ }
+
+ return ret;
+}
+
+static s32 gup_init_panel(struct goodix_ts_data *ts)
+{
+ s32 ret = 0;
+ s32 i = 0;
+ u8 check_sum = 0;
+ u8 opr_buf[16];
+ u8 sensor_id = 0;
+
+ u8 cfg_info_group1[] = CTP_CFG_GROUP1;
+ u8 cfg_info_group2[] = CTP_CFG_GROUP2;
+ u8 cfg_info_group3[] = CTP_CFG_GROUP3;
+ u8 cfg_info_group4[] = CTP_CFG_GROUP4;
+ u8 cfg_info_group5[] = CTP_CFG_GROUP5;
+ u8 cfg_info_group6[] = CTP_CFG_GROUP6;
+ u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3,
+ cfg_info_group4, cfg_info_group5, cfg_info_group6};
+ u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1),
+ CFG_GROUP_LEN(cfg_info_group2),
+ CFG_GROUP_LEN(cfg_info_group3),
+ CFG_GROUP_LEN(cfg_info_group4),
+ CFG_GROUP_LEN(cfg_info_group5),
+ CFG_GROUP_LEN(cfg_info_group6)};
+
+ if ((!cfg_info_len[1]) && (!cfg_info_len[2]) &&
+ (!cfg_info_len[3]) && (!cfg_info_len[4]) &&
+ (!cfg_info_len[5]))
+ {
+ sensor_id = 0;
+ }
+ else
+ {
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1);
+ if (SUCCESS == ret)
+ {
+ if (sensor_id >= 0x06)
+ {
+ GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id);
+ return -1;
+ }
+ }
+ else
+ {
+ GTP_ERROR("Failed to get sensor_id, No config sent!");
+ return -1;
+ }
+ }
+
+ GTP_DEBUG("Sensor_ID: %d", sensor_id);
+
+ ts->gtp_cfg_len = cfg_info_len[sensor_id];
+
+ if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH)
+ {
+ GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!", sensor_id);
+ return -1;
+ }
+
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1);
+
+ if (ret == SUCCESS)
+ {
+ GTP_DEBUG("CFG_GROUP%d Config Version: %d, IC Config Version: %d", sensor_id+1,
+ send_cfg_buf[sensor_id][0], opr_buf[0]);
+
+ send_cfg_buf[sensor_id][0] = opr_buf[0];
+ ts->fixed_cfg = 0;
+ /*
+ if (opr_buf[0] < 90)
+ {
+ grp_cfg_version = send_cfg_buf[sensor_id][0]; // backup group config version
+ send_cfg_buf[sensor_id][0] = 0x00;
+ ts->fixed_cfg = 0;
+ }
+ else // treated as fixed config, not send config
+ {
+ GTP_INFO("Ic fixed config with config version(%d)", opr_buf[0]);
+ ts->fixed_cfg = 1;
+ }*/
+ }
+ else
+ {
+ GTP_ERROR("Failed to get ic config version!No config sent!");
+ return -1;
+ }
+
+ memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
+ memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len);
+
+ GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
+ ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);
+
+ config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
+ config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
+ config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
+ config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
+
+ if (GTP_INT_TRIGGER == 0) //RISING
+ {
+ config[TRIGGER_LOC] &= 0xfe;
+ }
+ else if (GTP_INT_TRIGGER == 1) //FALLING
+ {
+ config[TRIGGER_LOC] |= 0x01;
+ }
+
+ check_sum = 0;
+ for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
+ {
+ check_sum += config[i];
+ }
+ config[ts->gtp_cfg_len] = (~check_sum) + 1;
+
+ GTP_DEBUG_FUNC();
+ ret = gtp_send_cfg(ts);
+ if (ret < 0)
+ {
+ GTP_ERROR("Send config error.");
+ }
+
+ msleep(10);
+ return 0;
+}
+
+
+static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8* msg, s32 len)
+{
+ s32 i = 0;
+
+ msg[0] = (addr >> 8) & 0xff;
+ msg[1] = addr & 0xff;
+
+ for (i = 0; i < 5; i++)
+ {
+ if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0)
+ {
+ break;
+ }
+ }
+
+ if (i >= 5)
+ {
+ GTP_ERROR("Read data from 0x%02x%02x failed!", msg[0], msg[1]);
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val)
+{
+ s32 i = 0;
+ u8 msg[3];
+
+ msg[0] = (addr >> 8) & 0xff;
+ msg[1] = addr & 0xff;
+ msg[2] = val;
+
+ for (i = 0; i < 5; i++)
+ {
+ if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0)
+ {
+ break;
+ }
+ }
+
+ if (i >= 5)
+ {
+ GTP_ERROR("Set data to 0x%02x%02x failed!", msg[0], msg[1]);
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_get_ic_fw_msg(struct i2c_client *client)
+{
+ s32 ret = -1;
+ u8 retry = 0;
+ u8 buf[16];
+ u8 i;
+
+ // step1:get hardware info
+ ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO, &buf[GTP_ADDR_LENGTH], 4);
+ if (FAIL == ret)
+ {
+ GTP_ERROR("[get_ic_fw_msg]get hw_info failed,exit");
+ return FAIL;
+ }
+
+ // buf[2~5]: 00 06 90 00
+ // hw_info: 00 90 06 00
+ for(i=0; i<4; i++)
+ {
+ update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i];
+ }
+ GTP_DEBUG("IC Hardware info:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1],
+ update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]);
+ // step2:get firmware message
+ for(retry=0; retry<2; retry++)
+ {
+ ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("Read firmware message fail.");
+ return ret;
+ }
+
+ update_msg.force_update = buf[GTP_ADDR_LENGTH];
+ if((0xBE != update_msg.force_update)&&(!retry))
+ {
+ GTP_INFO("The check sum in ic is error.");
+ GTP_INFO("The IC will be updated by force.");
+ continue;
+ }
+ break;
+ }
+ GTP_DEBUG("IC force update flag:0x%x", update_msg.force_update);
+
+ // step3:get pid & vid
+ ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID, &buf[GTP_ADDR_LENGTH], 6);
+ if (FAIL == ret)
+ {
+ GTP_ERROR("[get_ic_fw_msg]get pid & vid failed,exit");
+ return FAIL;
+ }
+
+ memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid));
+ memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4);
+ GTP_DEBUG("IC Product id:%s", update_msg.ic_fw_msg.pid);
+
+ //GT9XX PID MAPPING
+ /*|-----FLASH-----RAM-----|
+ |------918------918-----|
+ |------968------968-----|
+ |------913------913-----|
+ |------913P-----913P----|
+ |------927------927-----|
+ |------927P-----927P----|
+ |------9110-----9110----|
+ |------9110P----9111----|*/
+ if(update_msg.ic_fw_msg.pid[0] != 0)
+ {
+ if(!memcmp(update_msg.ic_fw_msg.pid, "9111", 4))
+ {
+ GTP_DEBUG("IC Mapping Product id:%s", update_msg.ic_fw_msg.pid);
+ memcpy(update_msg.ic_fw_msg.pid, "9110P", 5);
+ }
+ }
+
+ update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH+4] + (buf[GTP_ADDR_LENGTH+5]<<8);
+ GTP_DEBUG("IC version id:%04x", update_msg.ic_fw_msg.vid);
+
+ return SUCCESS;
+}
+
+s32 gup_enter_update_mode(struct goodix_ts_data *ts)
+{
+ s32 ret = -1;
+ s32 retry = 0;
+ u8 rd_buf[3];
+
+ //step1:RST output low last at least 2ms
+ GTP_GPIO_OUTPUT(ts->rst_gpio, 0);
+ msleep(2);
+
+ //step2:select I2C slave addr,INT:0--0xBA;1--0x28.
+ GTP_GPIO_OUTPUT(ts->irq_gpio, (ts->client->addr == 0x14));
+ msleep(2);
+
+ //step3:RST output high reset guitar
+ GTP_GPIO_OUTPUT(ts->rst_gpio, 1);
+
+ //20121211 modify start
+ msleep(5);
+ while(retry++ < 200)
+ {
+ //step4:Hold ss51 & dsp
+ ret = gup_set_ic_msg(ts->client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry);
+ continue;
+ }
+
+ //step5:Confirm hold
+ ret = gup_get_ic_msg(ts->client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry);
+ continue;
+ }
+ if(0x0C == rd_buf[GTP_ADDR_LENGTH])
+ {
+ GTP_DEBUG("Hold ss51 & dsp confirm SUCCESS");
+ break;
+ }
+ GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d", rd_buf[GTP_ADDR_LENGTH]);
+ }
+ if(retry >= 200)
+ {
+ GTP_ERROR("Enter update Hold ss51 failed.");
+ return FAIL;
+ }
+
+ //step6:DSP_CK and DSP_ALU_CK PowerOn
+ ret = gup_set_ic_msg(ts->client, 0x4010, 0x00);
+
+ //20121211 modify end
+ return ret;
+}
+
+void gup_leave_update_mode(struct goodix_ts_data *ts)
+{
+ //GTP_GPIO_AS_INT(ts->irq_gpio,IRQ_TYPE_EDGE_FALLING);
+
+ GTP_DEBUG("[leave_update_mode]reset chip.");
+ gtp_reset_guitar(i2c_connect_client, 20);
+}
+
+// Get the correct nvram data
+// The correct conditions:
+// 1. the hardware info is the same
+// 2. the product id is the same
+// 3. the firmware version in update file is greater than the firmware version in ic
+// or the check sum in ic is wrong
+/* Update Conditions:
+ 1. Same hardware info
+ 2. Same PID
+ 3. File PID > IC PID
+ Force Update Conditions:
+ 1. Wrong ic firmware checksum
+ 2. INVALID IC PID or VID
+ 3. IC PID == 91XX || File PID == 91XX
+*/
+
+static u8 gup_enter_update_judge(st_fw_head *fw_head)
+{
+ u16 u16_tmp;
+ s32 i = 0;
+
+ u16_tmp = fw_head->vid;
+ fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8);
+
+ GTP_DEBUG("FILE HARDWARE INFO:%02x%02x%02x%02x", fw_head->hw_info[0], fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]);
+ GTP_DEBUG("FILE PID:%s", fw_head->pid);
+ GTP_DEBUG("FILE VID:%04x", fw_head->vid);
+
+ GTP_DEBUG("IC HARDWARE INFO:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1],
+ update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]);
+ GTP_DEBUG("IC PID:%s", update_msg.ic_fw_msg.pid);
+ GTP_DEBUG("IC VID:%04x", update_msg.ic_fw_msg.vid);
+
+ //First two conditions
+ if ( !memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info, sizeof(update_msg.ic_fw_msg.hw_info)))
+ {
+ GTP_DEBUG("Get the same hardware info.");
+ if( update_msg.force_update != 0xBE )
+ {
+ GTP_INFO("FW chksum error,need enter update.");
+ return SUCCESS;
+ }
+
+ // 20130523 start
+ if (strlen(update_msg.ic_fw_msg.pid) < 3)
+ {
+ GTP_INFO("Illegal IC pid, need enter update");
+ return SUCCESS;
+ }
+ else
+ {
+ for (i = 0; i < 3; i++)
+ {
+ if ((update_msg.ic_fw_msg.pid[i] < 0x30) || (update_msg.ic_fw_msg.pid[i] > 0x39))
+ {
+ GTP_INFO("Illegal IC pid, out of bound, need enter update");
+ return SUCCESS;
+ }
+ }
+ }
+ // 20130523 end
+
+
+ if (( !memcmp(fw_head->pid, update_msg.ic_fw_msg.pid, (strlen(fw_head->pid)<3?3:strlen(fw_head->pid))))||
+ (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4))||
+ (!memcmp(fw_head->pid, "91XX", 4)))
+ {
+ if(!memcmp(fw_head->pid, "91XX", 4))
+ {
+ GTP_DEBUG("Force none same pid update mode.");
+ }
+ else
+ {
+ GTP_DEBUG("Get the same pid.");
+ }
+ //The third condition
+ if (fw_head->vid > update_msg.ic_fw_msg.vid)
+ {
+
+ GTP_INFO("Need enter update.");
+ return SUCCESS;
+ }
+ GTP_ERROR("Don't meet the third condition.");
+ GTP_ERROR("File VID <= Ic VID, update aborted!");
+ }
+ else
+ {
+ GTP_ERROR("File PID != Ic PID, update aborted!");
+ }
+ }
+ else
+ {
+ GTP_ERROR("Different Hardware, update aborted!");
+ }
+ return FAIL;
+}
+
+static u8 ascii2hex(u8 a)
+{
+ s8 value = 0;
+
+ if(a >= '0' && a <= '9')
+ {
+ value = a - '0';
+ }
+ else if(a >= 'A' && a <= 'F')
+ {
+ value = a - 'A' + 0x0A;
+ }
+ else if(a >= 'a' && a <= 'f')
+ {
+ value = a - 'a' + 0x0A;
+ }
+ else
+ {
+ value = 0xff;
+ }
+
+ return value;
+}
+
+static s8 gup_update_config(struct i2c_client *client)
+{
+ s32 file_len = 0;
+ s32 ret = 0;
+ s32 i = 0;
+ s32 file_cfg_len = 0;
+ s32 chip_cfg_len = 0;
+ s32 count = 0;
+ u8 *buf;
+ u8 *pre_buf;
+ u8 *file_config;
+ //u8 checksum = 0;
+ u8 pid[8];
+
+ if(NULL == update_msg.cfg_file)
+ {
+ GTP_ERROR("[update_cfg]No need to upgrade config!");
+ return FAIL;
+ }
+ file_len = update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_END);
+
+ ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_cfg]Read product id & version id fail.");
+ return FAIL;
+ }
+ pid[5] = '\0';
+ GTP_DEBUG("update cfg get pid:%s", &pid[GTP_ADDR_LENGTH]);
+
+ chip_cfg_len = 186;
+ if(!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) ||
+ !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) ||
+ !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3))
+ {
+ chip_cfg_len = 228;
+ }
+ GTP_DEBUG("[update_cfg]config file len:%d", file_len);
+ GTP_DEBUG("[update_cfg]need config len:%d",chip_cfg_len);
+ if((file_len+5) < chip_cfg_len*5)
+ {
+ GTP_ERROR("Config length error");
+ return -1;
+ }
+
+ buf = (u8*)kzalloc(file_len, GFP_KERNEL);
+ pre_buf = (u8*)kzalloc(file_len, GFP_KERNEL);
+ file_config = (u8*)kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL);
+ update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET);
+
+ GTP_DEBUG("[update_cfg]Read config from file.");
+ ret = update_msg.cfg_file->f_op->read(update_msg.cfg_file, (char*)pre_buf, file_len, &update_msg.cfg_file->f_pos);
+ if(ret<0)
+ {
+ GTP_ERROR("[update_cfg]Read config file failed.");
+ goto update_cfg_file_failed;
+ }
+
+ GTP_DEBUG("[update_cfg]Delete illgal charactor.");
+ for(i=0,count=0; i<file_len; i++)
+ {
+ if (pre_buf[i] == ' ' || pre_buf[i] == '\r' || pre_buf[i] == '\n')
+ {
+ continue;
+ }
+ buf[count++] = pre_buf[i];
+ }
+
+ GTP_DEBUG("[update_cfg]Ascii to hex.");
+ file_config[0] = GTP_REG_CONFIG_DATA >> 8;
+ file_config[1] = GTP_REG_CONFIG_DATA & 0xff;
+ for(i=0,file_cfg_len=GTP_ADDR_LENGTH; i<count; i+=5)
+ {
+ if((buf[i]=='0') && ((buf[i+1]=='x') || (buf[i+1]=='X')))
+ {
+ u8 high,low;
+ high = ascii2hex(buf[i+2]);
+ low = ascii2hex(buf[i+3]);
+
+ if((high == 0xFF) || (low == 0xFF))
+ {
+ ret = 0;
+ GTP_ERROR("[update_cfg]Illegal config file.");
+ goto update_cfg_file_failed;
+ }
+ file_config[file_cfg_len++] = (high<<4) + low;
+ }
+ else
+ {
+ ret = 0;
+ GTP_ERROR("[update_cfg]Illegal config file.");
+ goto update_cfg_file_failed;
+ }
+ }
+
+// //cal checksum
+// for(i=GTP_ADDR_LENGTH; i<chip_cfg_len; i++)
+// {
+// checksum += file_config[i];
+// }
+// file_config[chip_cfg_len] = (~checksum) + 1;
+// file_config[chip_cfg_len+1] = 0x01;
+
+ GTP_DEBUG("config:");
+ GTP_DEBUG_ARRAY(file_config+2, file_cfg_len);
+
+ i = 0;
+ while(i++ < 5)
+ {
+ ret = gup_i2c_write(client, file_config, file_cfg_len);
+ if(ret > 0)
+ {
+ GTP_INFO("[update_cfg]Send config SUCCESS.");
+ break;
+ }
+ GTP_ERROR("[update_cfg]Send config i2c error.");
+ }
+
+update_cfg_file_failed:
+ kfree(pre_buf);
+ kfree(buf);
+ kfree(file_config);
+ return ret;
+}
+
+#if GTP_HEADER_FW_UPDATE
+static u8 gup_check_fs_mounted(char *path_name)
+{
+ struct path root_path;
+ struct path path;
+ int err;
+ err = kern_path("/", LOOKUP_FOLLOW, &root_path);
+
+ if (err)
+ {
+ GTP_DEBUG("\"/\" NOT Mounted: %d", err);
+ return FAIL;
+ }
+ err = kern_path(path_name, LOOKUP_FOLLOW, &path);
+
+ if (err)
+ {
+ GTP_DEBUG("/data/ NOT Mounted: %d", err);
+ return FAIL;
+ }
+
+ return SUCCESS;
+
+ /*
+ if (path.mnt->mnt_sb == root_path.mnt->mnt_sb)
+ {
+ //-- not mounted
+ return FAIL;
+ }
+ else
+ {
+ return SUCCESS;
+ }*/
+
+}
+#endif
+static u8 gup_check_update_file(struct i2c_client *client, st_fw_head* fw_head, u8* path)
+{
+ s32 ret = 0;
+ s32 i = 0;
+ s32 fw_checksum = 0;
+ u8 buf[FW_HEAD_LENGTH];
+
+ char fwname[64] = {0};
+ struct goodix_ts_data *ts = l_ts;
+ sprintf(fwname, "/system/etc/firmware/%s.bin", ts->fw_name);
+ GTP_INFO("firmware file name: %s.", fwname);
+ if (path)
+ {
+ GTP_DEBUG("Update File path:%s, %d", path, strlen(path));
+ update_msg.file = filp_open(path, O_RDONLY, 0);
+
+ if (IS_ERR(update_msg.file))
+ {
+ GTP_ERROR("Open update file(%s) error!", path);
+ return FAIL;
+ }
+ }
+ else
+ {
+#if GTP_HEADER_FW_UPDATE
+ for (i = 0; i < (GUP_SEARCH_FILE_TIMES); i++)
+ {
+ GTP_DEBUG("Waiting for /data mounted [%d]", i);
+
+ if (gup_check_fs_mounted("/data") == SUCCESS)
+ {
+ GTP_DEBUG("/data Mounted!");
+ break;
+ }
+ msleep(3000);
+ }
+ if (i >= (GUP_SEARCH_FILE_TIMES))
+ {
+ GTP_ERROR("Wait for /data mounted timeout!");
+ return FAIL;
+ }
+
+ // update config
+ update_msg.cfg_file = filp_open(CONFIG_FILE_PATH_1, O_RDONLY, 0);
+ if (IS_ERR(update_msg.cfg_file))
+ {
+ GTP_DEBUG("%s is unavailable", CONFIG_FILE_PATH_1);
+ }
+ else
+ {
+ GTP_INFO("Update Config File: %s", CONFIG_FILE_PATH_1);
+ ret = gup_update_config(client);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Update config failed.");
+ }
+ filp_close(update_msg.cfg_file, NULL);
+ }
+
+ if (sizeof(header_fw_array) < (FW_HEAD_LENGTH+FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH))
+ {
+ GTP_ERROR("INVALID header_fw_array, check your gt9xx_firmware.h file!");
+ return FAIL;
+ }
+ update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_CREAT | O_RDWR, 0666);
+ if ((IS_ERR(update_msg.file)))
+ {
+ GTP_ERROR("Failed to Create file: %s for fw_header!", UPDATE_FILE_PATH_2);
+ return FAIL;
+ }
+ update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET);
+ update_msg.file->f_op->write(update_msg.file, (char *)header_fw_array, sizeof(header_fw_array), &update_msg.file->f_pos);
+ filp_close(update_msg.file, NULL);
+ update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_RDONLY, 0);
+#else
+ //u8 fp_len = max(sizeof(UPDATE_FILE_PATH_1), sizeof(UPDATE_FILE_PATH_2));
+ u8 fp_len = max(sizeof(UPDATE_FILE_PATH_1), sizeof(fwname));
+ u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1), sizeof(CONFIG_FILE_PATH_2));
+ u8 *search_update_path = (u8*)kzalloc(fp_len, GFP_KERNEL);
+ u8 *search_cfg_path = (u8*)kzalloc(cfp_len, GFP_KERNEL);
+ //Begin to search update file,the config file & firmware file must be in the same path,single or double.
+ searching_file = 1;
+ for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++)
+ {
+ if (searching_file == 0)
+ {
+ kfree(search_update_path);
+ kfree(search_cfg_path);
+ GTP_INFO(".bin/.cfg update file search forcely terminated!");
+ return FAIL;
+ }
+ if(i%2)
+ {
+ memcpy(search_update_path, UPDATE_FILE_PATH_1, sizeof(UPDATE_FILE_PATH_1));
+ memcpy(search_cfg_path, CONFIG_FILE_PATH_1, sizeof(CONFIG_FILE_PATH_1));
+ }
+ else
+ {
+ //memcpy(search_update_path, UPDATE_FILE_PATH_2, sizeof(UPDATE_FILE_PATH_2));
+ memcpy(search_update_path, fwname, sizeof(fwname));
+ memcpy(search_cfg_path, CONFIG_FILE_PATH_2, sizeof(CONFIG_FILE_PATH_2));
+ }
+
+ if(!(got_file_flag&0x0F))
+ {
+ update_msg.file = filp_open(search_update_path, O_RDONLY, 0);
+ if(!IS_ERR(update_msg.file))
+ {
+ GTP_DEBUG("Find the bin file");
+ got_file_flag |= 0x0F;
+ }
+ }
+ if(!(got_file_flag&0xF0))
+ {
+ update_msg.cfg_file = filp_open(search_cfg_path, O_RDONLY, 0);
+ if(!IS_ERR(update_msg.cfg_file))
+ {
+ GTP_DEBUG("Find the cfg file");
+ got_file_flag |= 0xF0;
+ }
+ }
+
+ if(got_file_flag)
+ {
+ if(got_file_flag == 0xFF)
+ {
+ break;
+ }
+ else
+ {
+ i += 4;
+ }
+ }
+ GTP_DEBUG("%3d:Searching %s %s file...", i, (got_file_flag&0x0F)?"":"bin", (got_file_flag&0xF0)?"":"cfg");
+ //msleep(3000);
+ }
+ searching_file = 0;
+ kfree(search_update_path);
+ kfree(search_cfg_path);
+
+ if(!got_file_flag)
+ {
+ GTP_ERROR("Can't find update file.");
+ goto load_failed;
+ }
+
+ if(got_file_flag&0xF0)
+ {
+ GTP_DEBUG("Got the update config file.");
+ ret = gup_update_config(client);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Update config failed.");
+ }
+ filp_close(update_msg.cfg_file, NULL);
+ msleep(500); //waiting config to be stored in FLASH.
+ }
+ if(got_file_flag&0x0F)
+ {
+ GTP_DEBUG("Got the update firmware file.");
+ }
+ else
+ {
+ GTP_ERROR("No need to upgrade firmware.");
+ goto load_failed;
+ }
+#endif
+ }
+
+ update_msg.old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET);
+ //update_msg.file->f_pos = 0;
+
+ ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, FW_HEAD_LENGTH, &update_msg.file->f_pos);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read firmware head in update file error.");
+ goto load_failed;
+ }
+ memcpy(fw_head, buf, FW_HEAD_LENGTH);
+
+ //check firmware legality
+ fw_checksum = 0;
+ for(i=0; i<FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH; i+=2)
+ {
+ u16 temp;
+ ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, 2, &update_msg.file->f_pos);
+ if (ret < 0)
+ {
+ GTP_ERROR("Read firmware file error.");
+ goto load_failed;
+ }
+ //GTP_DEBUG("BUF[0]:%x", buf[0]);
+ temp = (buf[0]<<8) + buf[1];
+ fw_checksum += temp;
+ }
+
+ GTP_DEBUG("firmware checksum:%x", fw_checksum&0xFFFF);
+ if(fw_checksum&0xFFFF)
+ {
+ GTP_ERROR("Illegal firmware file.");
+ goto load_failed;
+ }
+
+ return SUCCESS;
+
+load_failed:
+ set_fs(update_msg.old_fs);
+ return FAIL;
+}
+
+#if 0
+static u8 gup_check_update_header(struct i2c_client *client, st_fw_head* fw_head)
+{
+ const u8* pos;
+ int i = 0;
+ u8 mask_num = 0;
+ s32 ret = 0;
+
+ pos = HEADER_UPDATE_DATA;
+
+ memcpy(fw_head, pos, FW_HEAD_LENGTH);
+ pos += FW_HEAD_LENGTH;
+
+ ret = gup_enter_update_judge(fw_head);
+ if(SUCCESS == ret)
+ {
+ return SUCCESS;
+ }
+ return FAIL;
+}
+#endif
+
+static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr, u16 total_length)
+{
+ s32 ret = 0;
+ u16 burn_addr = start_addr;
+ u16 frame_length = 0;
+ u16 burn_length = 0;
+ u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH];
+ u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
+ u8 retry = 0;
+
+ GTP_DEBUG("Begin burn %dk data to addr 0x%x", (total_length/1024), start_addr);
+ while(burn_length < total_length)
+ {
+ GTP_DEBUG("B/T:%04d/%04d", burn_length, total_length);
+ frame_length = ((total_length - burn_length) > PACK_SIZE) ? PACK_SIZE : (total_length - burn_length);
+ wr_buf[0] = (u8)(burn_addr>>8);
+ rd_buf[0] = wr_buf[0];
+ wr_buf[1] = (u8)burn_addr;
+ rd_buf[1] = wr_buf[1];
+ memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length], frame_length);
+
+ for(retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++)
+ {
+ ret = gup_i2c_write(client, wr_buf, GTP_ADDR_LENGTH + frame_length);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Write frame data i2c error.");
+ continue;
+ }
+ ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH + frame_length);
+ if(ret <= 0)
+ {
+ GTP_ERROR("Read back frame data i2c error.");
+ continue;
+ }
+
+ if(memcmp(&wr_buf[GTP_ADDR_LENGTH], &rd_buf[GTP_ADDR_LENGTH], frame_length))
+ {
+ GTP_ERROR("Check frame data fail,not equal.");
+ GTP_DEBUG("write array:");
+ GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH], frame_length);
+ GTP_DEBUG("read array:");
+ GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length);
+ continue;
+ }
+ else
+ {
+ //GTP_DEBUG("Check frame data success.");
+ break;
+ }
+ }
+ if(retry >= MAX_FRAME_CHECK_TIME)
+ {
+ GTP_ERROR("Burn frame data time out,exit.");
+ return FAIL;
+ }
+ burn_length += frame_length;
+ burn_addr += frame_length;
+ }
+ return SUCCESS;
+}
+
+static u8 gup_load_section_file(u8* buf, u16 offset, u16 length)
+{
+ s32 ret = 0;
+
+ if(update_msg.file == NULL)
+ {
+ GTP_ERROR("cannot find update file,load section file fail.");
+ return FAIL;
+ }
+ update_msg.file->f_pos = FW_HEAD_LENGTH + offset;
+
+ ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, length, &update_msg.file->f_pos);
+ if(ret < 0)
+ {
+ GTP_ERROR("Read update file fail.");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_recall_check(struct i2c_client *client, u8* chk_src, u16 start_rd_addr, u16 chk_length)
+{
+ u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
+ s32 ret = 0;
+ u16 recall_addr = start_rd_addr;
+ u16 recall_length = 0;
+ u16 frame_length = 0;
+
+ while(recall_length < chk_length)
+ {
+ frame_length = ((chk_length - recall_length) > PACK_SIZE) ? PACK_SIZE : (chk_length - recall_length);
+ ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length);
+ if(ret <= 0)
+ {
+ GTP_ERROR("recall i2c error,exit");
+ return FAIL;
+ }
+
+ if(memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length], frame_length))
+ {
+ GTP_ERROR("Recall frame data fail,not equal.");
+ GTP_DEBUG("chk_src array:");
+ GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length);
+ GTP_DEBUG("recall array:");
+ GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length);
+ return FAIL;
+ }
+
+ recall_length += frame_length;
+ recall_addr += frame_length;
+ }
+ GTP_DEBUG("Recall check %dk firmware success.", (chk_length/1024));
+
+ return SUCCESS;
+}
+
+static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section, u16 start_addr, u8 bank_cmd )
+{
+ s32 ret = 0;
+ u8 rd_buf[5];
+
+ //step1:hold ss51 & dsp
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]hold ss51 & dsp fail.");
+ return FAIL;
+ }
+
+ //step2:set scramble
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]set scramble fail.");
+ return FAIL;
+ }
+
+ //step3:select bank
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F);
+ return FAIL;
+ }
+
+ //step4:enable accessing code
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]enable accessing code fail.");
+ return FAIL;
+ }
+
+ //step5:burn 8k fw section
+ ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_section]burn fw_section fail.");
+ return FAIL;
+ }
+
+ //step6:hold ss51 & release dsp
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]hold ss51 & release dsp fail.");
+ return FAIL;
+ }
+ //must delay
+ msleep(1);
+
+ //step7:send burn cmd to move data to flash from sram
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]send burn cmd fail.");
+ return FAIL;
+ }
+ GTP_DEBUG("[burn_fw_section]Wait for the burn is complete......");
+ do{
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]Get burn state fail");
+ return FAIL;
+ }
+ msleep(10);
+ //GTP_DEBUG("[burn_fw_section]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]);
+ }while(rd_buf[GTP_ADDR_LENGTH]);
+
+ //step8:select bank
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F);
+ return FAIL;
+ }
+
+ //step9:enable accessing code
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]enable accessing code fail.");
+ return FAIL;
+ }
+
+ //step10:recall 8k fw section
+ ret = gup_recall_check(client, fw_section, start_addr, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_section]recall check 8k firmware fail.");
+ return FAIL;
+ }
+
+ //step11:disable accessing code
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_section]disable accessing code fail.");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static u8 gup_burn_dsp_isp(struct i2c_client *client)
+{
+ s32 ret = 0;
+ u8* fw_dsp_isp = NULL;
+ u8 retry = 0;
+
+ GTP_DEBUG("[burn_dsp_isp]Begin burn dsp isp---->>");
+
+ //step1:alloc memory
+ GTP_DEBUG("[burn_dsp_isp]step1:alloc memory");
+ while(retry++ < 5)
+ {
+ fw_dsp_isp = (u8*)kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL);
+ if(fw_dsp_isp == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_dsp_isp]Alloc %dk byte memory success.", (FW_DSP_ISP_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load dsp isp file data
+ GTP_DEBUG("[burn_dsp_isp]step2:load dsp isp file data");
+ ret = gup_load_section_file(fw_dsp_isp, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail.");
+ goto exit_burn_dsp_isp;
+ }
+
+ //step3:disable wdt,clear cache enable
+ GTP_DEBUG("[burn_dsp_isp]step3:disable wdt,clear cache enable");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]disable wdt fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]clear cache enable fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step4:hold ss51 & dsp
+ GTP_DEBUG("[burn_dsp_isp]step4:hold ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step5:set boot from sram
+ GTP_DEBUG("[burn_dsp_isp]step5:set boot from sram");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]set boot from sram fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step6:software reboot
+ GTP_DEBUG("[burn_dsp_isp]step6:software reboot");
+ ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]software reboot fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step7:select bank2
+ GTP_DEBUG("[burn_dsp_isp]step7:select bank2");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]select bank2 fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step8:enable accessing code
+ GTP_DEBUG("[burn_dsp_isp]step8:enable accessing code");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]enable accessing code fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+
+ //step9:burn 4k dsp_isp
+ GTP_DEBUG("[burn_dsp_isp]step9:burn 4k dsp_isp");
+ ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail.");
+ goto exit_burn_dsp_isp;
+ }
+
+ //step10:set scramble
+ GTP_DEBUG("[burn_dsp_isp]step10:set scramble");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_dsp_isp]set scramble fail.");
+ ret = FAIL;
+ goto exit_burn_dsp_isp;
+ }
+ ret = SUCCESS;
+
+exit_burn_dsp_isp:
+ kfree(fw_dsp_isp);
+ return ret;
+}
+
+static u8 gup_burn_fw_ss51(struct i2c_client *client)
+{
+ u8* fw_ss51 = NULL;
+ u8 retry = 0;
+ s32 ret = 0;
+
+ GTP_DEBUG("[burn_fw_ss51]Begin burn ss51 firmware---->>");
+
+ //step1:alloc memory
+ GTP_DEBUG("[burn_fw_ss51]step1:alloc memory");
+ while(retry++ < 5)
+ {
+ fw_ss51 = (u8*)kzalloc(FW_SECTION_LENGTH, GFP_KERNEL);
+ if(fw_ss51 == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_fw_ss51]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load ss51 firmware section 1 file data
+ GTP_DEBUG("[burn_fw_ss51]step2:load ss51 firmware section 1 file data");
+ ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step3:clear control flag
+ GTP_DEBUG("[burn_fw_ss51]step3:clear control flag");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_ss51]clear control flag fail.");
+ ret = FAIL;
+ goto exit_burn_fw_ss51;
+ }
+
+ //step4:burn ss51 firmware section 1
+ GTP_DEBUG("[burn_fw_ss51]step4:burn ss51 firmware section 1");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step5:load ss51 firmware section 2 file data
+ GTP_DEBUG("[burn_fw_ss51]step5:load ss51 firmware section 2 file data");
+ ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step6:burn ss51 firmware section 2
+ GTP_DEBUG("[burn_fw_ss51]step6:burn ss51 firmware section 2");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step7:load ss51 firmware section 3 file data
+ GTP_DEBUG("[burn_fw_ss51]step7:load ss51 firmware section 3 file data");
+ ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step8:burn ss51 firmware section 3
+ GTP_DEBUG("[burn_fw_ss51]step8:burn ss51 firmware section 3");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step9:load ss51 firmware section 4 file data
+ GTP_DEBUG("[burn_fw_ss51]step9:load ss51 firmware section 4 file data");
+ ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH, FW_SECTION_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ //step10:burn ss51 firmware section 4
+ GTP_DEBUG("[burn_fw_ss51]step10:burn ss51 firmware section 4");
+ ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail.");
+ goto exit_burn_fw_ss51;
+ }
+
+ ret = SUCCESS;
+
+exit_burn_fw_ss51:
+ kfree(fw_ss51);
+ return ret;
+}
+
+static u8 gup_burn_fw_dsp(struct i2c_client *client)
+{
+ s32 ret = 0;
+ u8* fw_dsp = NULL;
+ u8 retry = 0;
+ u8 rd_buf[5];
+
+ GTP_DEBUG("[burn_fw_dsp]Begin burn dsp firmware---->>");
+ //step1:alloc memory
+ GTP_DEBUG("[burn_fw_dsp]step1:alloc memory");
+ while(retry++ < 5)
+ {
+ fw_dsp = (u8*)kzalloc(FW_DSP_LENGTH, GFP_KERNEL);
+ if(fw_dsp == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_fw_dsp]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load firmware dsp
+ GTP_DEBUG("[burn_fw_dsp]step2:load firmware dsp");
+ ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_dsp]load firmware dsp fail.");
+ goto exit_burn_fw_dsp;
+ }
+
+ //step3:select bank3
+ GTP_DEBUG("[burn_fw_dsp]step3:select bank3");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]select bank3 fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+
+ //step4:hold ss51 & dsp
+ GTP_DEBUG("[burn_fw_dsp]step4:hold ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+
+ //step5:set scramble
+ GTP_DEBUG("[burn_fw_dsp]step5:set scramble");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]set scramble fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+
+ //step6:release ss51 & dsp
+ GTP_DEBUG("[burn_fw_dsp]step6:release ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_dsp;
+ }
+ //must delay
+ msleep(1);
+
+ //step7:burn 4k dsp firmware
+ GTP_DEBUG("[burn_fw_dsp]step7:burn 4k dsp firmware");
+ ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_dsp]burn fw_section fail.");
+ goto exit_burn_fw_dsp;
+ }
+
+ //step8:send burn cmd to move data to flash from sram
+ GTP_DEBUG("[burn_fw_dsp]step8:send burn cmd to move data to flash from sram");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]send burn cmd fail.");
+ goto exit_burn_fw_dsp;
+ }
+ GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......");
+ do{
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_dsp]Get burn state fail");
+ goto exit_burn_fw_dsp;
+ }
+ msleep(10);
+ //GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]);
+ }while(rd_buf[GTP_ADDR_LENGTH]);
+
+ //step9:recall check 4k dsp firmware
+ GTP_DEBUG("[burn_fw_dsp]step9:recall check 4k dsp firmware");
+ ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail.");
+ goto exit_burn_fw_dsp;
+ }
+
+ ret = SUCCESS;
+
+exit_burn_fw_dsp:
+ kfree(fw_dsp);
+ return ret;
+}
+
+static u8 gup_burn_fw_boot(struct i2c_client *client)
+{
+ s32 ret = 0;
+ u8* fw_boot = NULL;
+ u8 retry = 0;
+ u8 rd_buf[5];
+
+ GTP_DEBUG("[burn_fw_boot]Begin burn bootloader firmware---->>");
+
+ //step1:Alloc memory
+ GTP_DEBUG("[burn_fw_boot]step1:Alloc memory");
+ while(retry++ < 5)
+ {
+ fw_boot = (u8*)kzalloc(FW_BOOT_LENGTH, GFP_KERNEL);
+ if(fw_boot == NULL)
+ {
+ continue;
+ }
+ else
+ {
+ GTP_INFO("[burn_fw_boot]Alloc %dk byte memory success.", (FW_BOOT_LENGTH/1024));
+ break;
+ }
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit.");
+ return FAIL;
+ }
+
+ //step2:load firmware bootloader
+ GTP_DEBUG("[burn_fw_boot]step2:load firmware bootloader");
+ ret = gup_load_section_file(fw_boot, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH), FW_BOOT_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_boot]load firmware dsp fail.");
+ goto exit_burn_fw_boot;
+ }
+
+ //step3:hold ss51 & dsp
+ GTP_DEBUG("[burn_fw_boot]step3:hold ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step4:set scramble
+ GTP_DEBUG("[burn_fw_boot]step4:set scramble");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]set scramble fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step5:release ss51 & dsp
+ GTP_DEBUG("[burn_fw_boot]step5:release ss51 & dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+ //must delay
+ msleep(1);
+
+ //step6:select bank3
+ GTP_DEBUG("[burn_fw_boot]step6:select bank3");
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]select bank3 fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step7:burn 2k bootloader firmware
+ GTP_DEBUG("[burn_fw_boot]step7:burn 2k bootloader firmware");
+ ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_boot]burn fw_section fail.");
+ goto exit_burn_fw_boot;
+ }
+
+ //step7:send burn cmd to move data to flash from sram
+ GTP_DEBUG("[burn_fw_boot]step7:send burn cmd to move data to flash from sram");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]send burn cmd fail.");
+ goto exit_burn_fw_boot;
+ }
+ GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......");
+ do{
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]Get burn state fail");
+ goto exit_burn_fw_boot;
+ }
+ msleep(10);
+ //GTP_DEBUG("[burn_fw_boot]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]);
+ }while(rd_buf[GTP_ADDR_LENGTH]);
+
+ //step8:recall check 2k bootloader firmware
+ GTP_DEBUG("[burn_fw_boot]step8:recall check 2k bootloader firmware");
+ ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail.");
+ goto exit_burn_fw_boot;
+ }
+
+ //step9:enable download DSP code
+ GTP_DEBUG("[burn_fw_boot]step9:enable download DSP code ");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]enable download DSP code fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ //step10:release ss51 & hold dsp
+ GTP_DEBUG("[burn_fw_boot]step10:release ss51 & hold dsp");
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08);
+ if(ret <= 0)
+ {
+ GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail.");
+ ret = FAIL;
+ goto exit_burn_fw_boot;
+ }
+
+ ret = SUCCESS;
+
+exit_burn_fw_boot:
+ kfree(fw_boot);
+ return ret;
+}
+
+s32 gup_update_proc(void *dir)
+{
+ s32 ret = 0;
+ u8 retry = 0;
+ st_fw_head fw_head;
+ struct goodix_ts_data *ts = NULL;
+
+ GTP_DEBUG("[update_proc]Begin update ......");
+
+ show_len = 1;
+ total_len = 100;
+ if(dir == NULL)
+ {
+ //msleep(3000); //wait main thread to be completed
+ }
+
+ ts = l_ts;
+
+ if (searching_file)
+ {
+ searching_file = 0; // exit .bin update file searching
+ GTP_INFO("Exiting searching .bin update file...");
+ while ((show_len != 200) && (show_len != 100)) // wait for auto update quitted completely
+ {
+ msleep(100);
+ }
+ }
+
+ update_msg.file = NULL;
+ ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8*)dir); //20121211
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]check update file fail.");
+ goto file_fail;
+ }
+
+ //gtp_reset_guitar(i2c_connect_client, 20);
+ ret = gup_get_ic_fw_msg(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]get ic message fail.");
+ goto file_fail;
+ }
+
+ ret = gup_enter_update_judge(&fw_head);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]Check *.bin file fail.");
+ goto file_fail;
+ }
+
+ ts->enter_update = 1;
+ gtp_irq_disable(ts);
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(ts->client, SWITCH_OFF);
+#endif
+ ret = gup_enter_update_mode(ts);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]enter update mode fail.");
+ goto update_fail;
+ }
+
+ while(retry++ < 5)
+ {
+ show_len = 10;
+ total_len = 100;
+ ret = gup_burn_dsp_isp(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn dsp isp fail.");
+ continue;
+ }
+
+ show_len += 10;
+ ret = gup_burn_fw_ss51(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn ss51 firmware fail.");
+ continue;
+ }
+
+ show_len += 40;
+ ret = gup_burn_fw_dsp(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn dsp firmware fail.");
+ continue;
+ }
+
+ show_len += 20;
+ ret = gup_burn_fw_boot(i2c_connect_client);
+ if(FAIL == ret)
+ {
+ GTP_ERROR("[update_proc]burn bootloader firmware fail.");
+ continue;
+ }
+ show_len += 10;
+ GTP_INFO("[update_proc]UPDATE SUCCESS.");
+ break;
+ }
+ if(retry >= 5)
+ {
+ GTP_ERROR("[update_proc]retry timeout,UPDATE FAIL.");
+ goto update_fail;
+ }
+
+ GTP_DEBUG("[update_proc]leave update mode.");
+ gup_leave_update_mode(ts);
+
+ msleep(100);
+// GTP_DEBUG("[update_proc]send config.");
+// ret = gtp_send_cfg(i2c_connect_client);
+// if(ret < 0)
+// {
+// GTP_ERROR("[update_proc]send config fail.");
+// }
+ if (ts->fw_error)
+ {
+ GTP_INFO("firmware error auto update, resent config!");
+ gup_init_panel(ts);
+ }
+ show_len = 100;
+ total_len = 100;
+ ts->enter_update = 0;
+ gtp_irq_enable(ts);
+
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(ts->client, SWITCH_ON);
+#endif
+ filp_close(update_msg.file, NULL);
+ return SUCCESS;
+
+update_fail:
+ ts->enter_update = 0;
+ gtp_irq_enable(ts);
+
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(ts->client, SWITCH_ON);
+#endif
+
+file_fail:
+ if(update_msg.file && !IS_ERR(update_msg.file))
+ {
+ filp_close(update_msg.file, NULL);
+ }
+ show_len = 200;
+ total_len = 100;
+ return FAIL;
+}
+
+#if GTP_AUTO_UPDATE
+u8 gup_init_update_proc(struct goodix_ts_data *ts)
+{
+ //struct task_struct *thread = NULL;
+
+ GTP_INFO("Ready to run update thread.");
+ if(!gup_update_proc(NULL))
+ GTP_ERROR("fail to update.");
+ /*thread = kthread_run(gup_update_proc, (void*)NULL, "guitar_update");
+ if (IS_ERR(thread))
+ {
+ GTP_ERROR("Failed to create update thread.\n");
+ return -1;
+ }*/
+
+ return 0;
+}
+#endif
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
new file mode 100644
index 00000000..a54f90e0
--- /dev/null
+++ b/drivers/input/touchscreen/gunze.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gunze AHL-51S touchscreen driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Gunze AHL-51S touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define GUNZE_MAX_LENGTH 10
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct gunze {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[GUNZE_MAX_LENGTH];
+ char phys[32];
+};
+
+static void gunze_process_packet(struct gunze* gunze)
+{
+ struct input_dev *dev = gunze->dev;
+
+ if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
+ (gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
+ printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data);
+ return;
+ }
+
+ input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
+ input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
+ input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
+ input_sync(dev);
+}
+
+static irqreturn_t gunze_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct gunze* gunze = serio_get_drvdata(serio);
+
+ if (data == '\r') {
+ gunze_process_packet(gunze);
+ gunze->idx = 0;
+ } else {
+ if (gunze->idx < GUNZE_MAX_LENGTH)
+ gunze->data[gunze->idx++] = data;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * gunze_disconnect() is the opposite of gunze_connect()
+ */
+
+static void gunze_disconnect(struct serio *serio)
+{
+ struct gunze *gunze = serio_get_drvdata(serio);
+
+ input_get_device(gunze->dev);
+ input_unregister_device(gunze->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(gunze->dev);
+ kfree(gunze);
+}
+
+/*
+ * gunze_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int gunze_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct gunze *gunze;
+ struct input_dev *input_dev;
+ int err;
+
+ gunze = kzalloc(sizeof(struct gunze), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!gunze || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ gunze->serio = serio;
+ gunze->dev = input_dev;
+ snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Gunze AHL-51S TouchScreen";
+ input_dev->phys = gunze->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_GUNZE;
+ input_dev->id.product = 0x0051;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 24, 1000, 0, 0);
+
+ serio_set_drvdata(serio, gunze);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(gunze->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(gunze);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id gunze_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_GUNZE,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, gunze_serio_ids);
+
+static struct serio_driver gunze_drv = {
+ .driver = {
+ .name = "gunze",
+ },
+ .description = DRIVER_DESC,
+ .id_table = gunze_serio_ids,
+ .interrupt = gunze_interrupt,
+ .connect = gunze_connect,
+ .disconnect = gunze_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init gunze_init(void)
+{
+ return serio_register_driver(&gunze_drv);
+}
+
+static void __exit gunze_exit(void)
+{
+ serio_unregister_driver(&gunze_drv);
+}
+
+module_init(gunze_init);
+module_exit(gunze_exit);
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
new file mode 100644
index 00000000..6107e563
--- /dev/null
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com
+ *
+ * Sponsored by Transvirtual Technology.
+ *
+ * Derived from the code in h3600_ts.[ch] by Charles Flynn
+ */
+
+/*
+ * Driver for the h3600 Touch Screen and other Atmel controlled devices.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <jsimmons@transvirtual.com>.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+/* SA1100 serial defines */
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+#define DRIVER_DESC "H3600 touchscreen driver"
+
+MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+/* The start and end of frame characters SOF and EOF */
+#define CHAR_SOF 0x02
+#define CHAR_EOF 0x03
+#define FRAME_OVERHEAD 3 /* CHAR_SOF,CHAR_EOF,LENGTH = 3 */
+
+/*
+ Atmel events and response IDs contained in frame.
+ Programmer has no control over these numbers.
+ TODO there are holes - specifically 1,7,0x0a
+*/
+#define VERSION_ID 0 /* Get Version (request/response) */
+#define KEYBD_ID 2 /* Keyboard (event) */
+#define TOUCHS_ID 3 /* Touch Screen (event)*/
+#define EEPROM_READ_ID 4 /* (request/response) */
+#define EEPROM_WRITE_ID 5 /* (request/response) */
+#define THERMAL_ID 6 /* (request/response) */
+#define NOTIFY_LED_ID 8 /* (request/response) */
+#define BATTERY_ID 9 /* (request/response) */
+#define SPI_READ_ID 0x0b /* ( request/response) */
+#define SPI_WRITE_ID 0x0c /* ( request/response) */
+#define FLITE_ID 0x0d /* backlight ( request/response) */
+#define STX_ID 0xa1 /* extension pack status (req/resp) */
+
+#define MAX_ID 14
+
+#define H3600_MAX_LENGTH 16
+#define H3600_KEY 0xf
+
+#define H3600_SCANCODE_RECORD 1 /* 1 -> record button */
+#define H3600_SCANCODE_CALENDAR 2 /* 2 -> calendar */
+#define H3600_SCANCODE_CONTACTS 3 /* 3 -> contact */
+#define H3600_SCANCODE_Q 4 /* 4 -> Q button */
+#define H3600_SCANCODE_START 5 /* 5 -> start menu */
+#define H3600_SCANCODE_UP 6 /* 6 -> up */
+#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */
+#define H3600_SCANCODE_LEFT 8 /* 8 -> left */
+#define H3600_SCANCODE_DOWN 9 /* 9 -> down */
+
+/*
+ * Per-touchscreen data.
+ */
+struct h3600_dev {
+ struct input_dev *dev;
+ struct serio *serio;
+ unsigned char event; /* event ID from packet */
+ unsigned char chksum;
+ unsigned char len;
+ unsigned char idx;
+ unsigned char buf[H3600_MAX_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t action_button_handler(int irq, void *dev_id)
+{
+ int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1;
+ struct input_dev *dev = dev_id;
+
+ input_report_key(dev, KEY_ENTER, down);
+ input_sync(dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t npower_button_handler(int irq, void *dev_id)
+{
+ int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1;
+ struct input_dev *dev = dev_id;
+
+ /*
+ * This interrupt is only called when we release the key. So we have
+ * to fake a key press.
+ */
+ input_report_key(dev, KEY_SUSPEND, 1);
+ input_report_key(dev, KEY_SUSPEND, down);
+ input_sync(dev);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+
+static int flite_brightness = 25;
+
+enum flite_pwr {
+ FLITE_PWR_OFF = 0,
+ FLITE_PWR_ON = 1
+};
+
+/*
+ * h3600_flite_power: enables or disables power to frontlight, using last bright */
+unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr)
+{
+ unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness;
+ struct h3600_dev *ts = input_get_drvdata(dev);
+
+ /* Must be in this order */
+ serio_write(ts->serio, 1);
+ serio_write(ts->serio, pwr);
+ serio_write(ts->serio, brightness);
+
+ return 0;
+}
+
+#endif
+
+/*
+ * This function translates the native event packets to linux input event
+ * packets. Some packets coming from serial are not touchscreen related. In
+ * this case we send them off to be processed elsewhere.
+ */
+static void h3600ts_process_packet(struct h3600_dev *ts)
+{
+ struct input_dev *dev = ts->dev;
+ static int touched = 0;
+ int key, down = 0;
+
+ switch (ts->event) {
+ /*
+ Buttons - returned as a single byte
+ 7 6 5 4 3 2 1 0
+ S x x x N N N N
+
+ S switch state ( 0=pressed 1=released)
+ x Unused.
+ NNNN switch number 0-15
+
+ Note: This is true for non interrupt generated key events.
+ */
+ case KEYBD_ID:
+ down = (ts->buf[0] & 0x80) ? 0 : 1;
+
+ switch (ts->buf[0] & 0x7f) {
+ case H3600_SCANCODE_RECORD:
+ key = KEY_RECORD;
+ break;
+ case H3600_SCANCODE_CALENDAR:
+ key = KEY_PROG1;
+ break;
+ case H3600_SCANCODE_CONTACTS:
+ key = KEY_PROG2;
+ break;
+ case H3600_SCANCODE_Q:
+ key = KEY_Q;
+ break;
+ case H3600_SCANCODE_START:
+ key = KEY_PROG3;
+ break;
+ case H3600_SCANCODE_UP:
+ key = KEY_UP;
+ break;
+ case H3600_SCANCODE_RIGHT:
+ key = KEY_RIGHT;
+ break;
+ case H3600_SCANCODE_LEFT:
+ key = KEY_LEFT;
+ break;
+ case H3600_SCANCODE_DOWN:
+ key = KEY_DOWN;
+ break;
+ default:
+ key = 0;
+ }
+ if (key)
+ input_report_key(dev, key, down);
+ break;
+ /*
+ * Native touchscreen event data is formatted as shown below:-
+ *
+ * +-------+-------+-------+-------+
+ * | Xmsb | Xlsb | Ymsb | Ylsb |
+ * +-------+-------+-------+-------+
+ * byte 0 1 2 3
+ */
+ case TOUCHS_ID:
+ if (!touched) {
+ input_report_key(dev, BTN_TOUCH, 1);
+ touched = 1;
+ }
+
+ if (ts->len) {
+ unsigned short x, y;
+
+ x = ts->buf[0]; x <<= 8; x += ts->buf[1];
+ y = ts->buf[2]; y <<= 8; y += ts->buf[3];
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ } else {
+ input_report_key(dev, BTN_TOUCH, 0);
+ touched = 0;
+ }
+ break;
+ default:
+ /* Send a non input event elsewhere */
+ break;
+ }
+
+ input_sync(dev);
+}
+
+/*
+ * h3600ts_event() handles events from the input module.
+ */
+static int h3600ts_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+#if 0
+ struct h3600_dev *ts = input_get_drvdata(dev);
+
+ switch (type) {
+ case EV_LED: {
+ // serio_write(ts->serio, SOME_CMD);
+ return 0;
+ }
+ }
+ return -1;
+#endif
+ return 0;
+}
+
+/*
+ Frame format
+ byte 1 2 3 len + 4
+ +-------+---------------+---------------+--=------------+
+ |SOF |id |len | len bytes | Chksum |
+ +-------+---------------+---------------+--=------------+
+ bit 0 7 8 11 12 15 16
+
+ +-------+---------------+-------+
+ |SOF |id |0 |Chksum | - Note Chksum does not include SOF
+ +-------+---------------+-------+
+ bit 0 7 8 11 12 15 16
+
+*/
+
+static int state;
+
+/* decode States */
+#define STATE_SOF 0 /* start of FRAME */
+#define STATE_ID 1 /* state where we decode the ID & len */
+#define STATE_DATA 2 /* state where we decode data */
+#define STATE_EOF 3 /* state where we decode checksum or EOF */
+
+static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags)
+{
+ struct h3600_dev *ts = serio_get_drvdata(serio);
+
+ /*
+ * We have a new frame coming in.
+ */
+ switch (state) {
+ case STATE_SOF:
+ if (data == CHAR_SOF)
+ state = STATE_ID;
+ break;
+ case STATE_ID:
+ ts->event = (data & 0xf0) >> 4;
+ ts->len = (data & 0xf);
+ ts->idx = 0;
+ if (ts->event >= MAX_ID) {
+ state = STATE_SOF;
+ break;
+ }
+ ts->chksum = data;
+ state = (ts->len > 0) ? STATE_DATA : STATE_EOF;
+ break;
+ case STATE_DATA:
+ ts->chksum += data;
+ ts->buf[ts->idx]= data;
+ if (++ts->idx == ts->len)
+ state = STATE_EOF;
+ break;
+ case STATE_EOF:
+ state = STATE_SOF;
+ if (data == CHAR_EOF || data == ts->chksum)
+ h3600ts_process_packet(ts);
+ break;
+ default:
+ printk("Error3\n");
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * h3600ts_connect() is the routine that is called when someone adds a
+ * new serio device that supports H3600 protocol and registers it as
+ * an input device.
+ */
+static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct h3600_dev *ts;
+ struct input_dev *input_dev;
+ int err;
+
+ ts = kzalloc(sizeof(struct h3600_dev), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ ts->serio = serio;
+ ts->dev = input_dev;
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "H3600 TouchScreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_H3600;
+ input_dev->id.product = 0x0666; /* FIXME !!! We can ask the hardware */
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_set_drvdata(input_dev, ts);
+
+ input_dev->event = h3600ts_event;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
+ BIT_MASK(EV_LED) | BIT_MASK(EV_PWR);
+ input_dev->ledbit[0] = BIT_MASK(LED_SLEEP);
+ input_set_abs_params(input_dev, ABS_X, 60, 985, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 35, 1024, 0, 0);
+
+ set_bit(KEY_RECORD, input_dev->keybit);
+ set_bit(KEY_Q, input_dev->keybit);
+ set_bit(KEY_PROG1, input_dev->keybit);
+ set_bit(KEY_PROG2, input_dev->keybit);
+ set_bit(KEY_PROG3, input_dev->keybit);
+ set_bit(KEY_UP, input_dev->keybit);
+ set_bit(KEY_RIGHT, input_dev->keybit);
+ set_bit(KEY_LEFT, input_dev->keybit);
+ set_bit(KEY_DOWN, input_dev->keybit);
+ set_bit(KEY_ENTER, input_dev->keybit);
+ set_bit(KEY_SUSPEND, input_dev->keybit);
+ set_bit(BTN_TOUCH, input_dev->keybit);
+
+ /* Device specific stuff */
+ set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES);
+ set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);
+
+ if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,
+ IRQF_SHARED, "h3600_action", ts->dev)) {
+ printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
+ IRQF_SHARED, "h3600_suspend", ts->dev)) {
+ printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
+ err = -EBUSY;
+ goto fail2;
+ }
+
+ serio_set_drvdata(serio, ts);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail3;
+
+ //h3600_flite_control(1, 25); /* default brightness */
+ err = input_register_device(ts->dev);
+ if (err)
+ goto fail4;
+
+ return 0;
+
+fail4: serio_close(serio);
+fail3: serio_set_drvdata(serio, NULL);
+ free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
+fail2: free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
+fail1: input_free_device(input_dev);
+ kfree(ts);
+ return err;
+}
+
+/*
+ * h3600ts_disconnect() is the opposite of h3600ts_connect()
+ */
+
+static void h3600ts_disconnect(struct serio *serio)
+{
+ struct h3600_dev *ts = serio_get_drvdata(serio);
+
+ free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
+ free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
+ input_get_device(ts->dev);
+ input_unregister_device(ts->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(ts->dev);
+ kfree(ts);
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id h3600ts_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_H3600,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, h3600ts_serio_ids);
+
+static struct serio_driver h3600ts_drv = {
+ .driver = {
+ .name = "h3600ts",
+ },
+ .description = DRIVER_DESC,
+ .id_table = h3600ts_serio_ids,
+ .interrupt = h3600ts_interrupt,
+ .connect = h3600ts_connect,
+ .disconnect = h3600ts_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init h3600ts_init(void)
+{
+ return serio_register_driver(&h3600ts_drv);
+}
+
+static void __exit h3600ts_exit(void)
+{
+ serio_unregister_driver(&h3600ts_drv);
+}
+
+module_init(h3600ts_init);
+module_exit(h3600ts_exit);
diff --git a/drivers/input/touchscreen/hampshire.c b/drivers/input/touchscreen/hampshire.c
new file mode 100644
index 00000000..2da6cc31
--- /dev/null
+++ b/drivers/input/touchscreen/hampshire.c
@@ -0,0 +1,205 @@
+/*
+ * Hampshire serial touchscreen driver
+ *
+ * Copyright (c) 2010 Adam Bennett
+ * Based on the dynapro driver (c) Tias Guns
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2010/04/08 Adam Bennett <abennett72@gmail.com>
+ * Copied dynapro.c and edited for Hampshire 4-byte protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Hampshire serial touchscreen driver"
+
+MODULE_AUTHOR("Adam Bennett <abennett72@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define HAMPSHIRE_FORMAT_TOUCH_BIT 0x40
+#define HAMPSHIRE_FORMAT_LENGTH 4
+#define HAMPSHIRE_RESPONSE_BEGIN_BYTE 0x80
+
+#define HAMPSHIRE_MIN_XC 0
+#define HAMPSHIRE_MAX_XC 0x1000
+#define HAMPSHIRE_MIN_YC 0
+#define HAMPSHIRE_MAX_YC 0x1000
+
+#define HAMPSHIRE_GET_XC(data) (((data[3] & 0x0c) >> 2) | (data[1] << 2) | ((data[0] & 0x38) << 6))
+#define HAMPSHIRE_GET_YC(data) ((data[3] & 0x03) | (data[2] << 2) | ((data[0] & 0x07) << 9))
+#define HAMPSHIRE_GET_TOUCHED(data) (HAMPSHIRE_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct hampshire {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[HAMPSHIRE_FORMAT_LENGTH];
+ char phys[32];
+};
+
+static void hampshire_process_data(struct hampshire *phampshire)
+{
+ struct input_dev *dev = phampshire->dev;
+
+ if (HAMPSHIRE_FORMAT_LENGTH == ++phampshire->idx) {
+ input_report_abs(dev, ABS_X, HAMPSHIRE_GET_XC(phampshire->data));
+ input_report_abs(dev, ABS_Y, HAMPSHIRE_GET_YC(phampshire->data));
+ input_report_key(dev, BTN_TOUCH,
+ HAMPSHIRE_GET_TOUCHED(phampshire->data));
+ input_sync(dev);
+
+ phampshire->idx = 0;
+ }
+}
+
+static irqreturn_t hampshire_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct hampshire *phampshire = serio_get_drvdata(serio);
+
+ phampshire->data[phampshire->idx] = data;
+
+ if (HAMPSHIRE_RESPONSE_BEGIN_BYTE & phampshire->data[0])
+ hampshire_process_data(phampshire);
+ else
+ dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+ phampshire->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+static void hampshire_disconnect(struct serio *serio)
+{
+ struct hampshire *phampshire = serio_get_drvdata(serio);
+
+ input_get_device(phampshire->dev);
+ input_unregister_device(phampshire->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(phampshire->dev);
+ kfree(phampshire);
+}
+
+/*
+ * hampshire_connect() is the routine that is called when someone adds a
+ * new serio device that supports hampshire protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int hampshire_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct hampshire *phampshire;
+ struct input_dev *input_dev;
+ int err;
+
+ phampshire = kzalloc(sizeof(struct hampshire), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!phampshire || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ phampshire->serio = serio;
+ phampshire->dev = input_dev;
+ snprintf(phampshire->phys, sizeof(phampshire->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Hampshire Serial TouchScreen";
+ input_dev->phys = phampshire->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_HAMPSHIRE;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(phampshire->dev, ABS_X,
+ HAMPSHIRE_MIN_XC, HAMPSHIRE_MAX_XC, 0, 0);
+ input_set_abs_params(phampshire->dev, ABS_Y,
+ HAMPSHIRE_MIN_YC, HAMPSHIRE_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, phampshire);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(phampshire->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(phampshire);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id hampshire_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_HAMPSHIRE,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, hampshire_serio_ids);
+
+static struct serio_driver hampshire_drv = {
+ .driver = {
+ .name = "hampshire",
+ },
+ .description = DRIVER_DESC,
+ .id_table = hampshire_serio_ids,
+ .interrupt = hampshire_interrupt,
+ .connect = hampshire_connect,
+ .disconnect = hampshire_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init hampshire_init(void)
+{
+ return serio_register_driver(&hampshire_drv);
+}
+
+static void __exit hampshire_exit(void)
+{
+ serio_unregister_driver(&hampshire_drv);
+}
+
+module_init(hampshire_init);
+module_exit(hampshire_exit);
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
new file mode 100644
index 00000000..85cf9bee
--- /dev/null
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -0,0 +1,127 @@
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/adc.h>
+#include <mach/hp6xx.h>
+
+#define MODNAME "hp680_ts_input"
+
+#define HP680_TS_ABS_X_MIN 40
+#define HP680_TS_ABS_X_MAX 950
+#define HP680_TS_ABS_Y_MIN 80
+#define HP680_TS_ABS_Y_MAX 910
+
+#define PHDR 0xa400012e
+#define SCPDR 0xa4000136
+
+static void do_softint(struct work_struct *work);
+
+static struct input_dev *hp680_ts_dev;
+static DECLARE_DELAYED_WORK(work, do_softint);
+
+static void do_softint(struct work_struct *work)
+{
+ int absx = 0, absy = 0;
+ u8 scpdr;
+ int touched = 0;
+
+ if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) {
+ scpdr = __raw_readb(SCPDR);
+ scpdr |= SCPDR_TS_SCAN_ENABLE;
+ scpdr &= ~SCPDR_TS_SCAN_Y;
+ __raw_writeb(scpdr, SCPDR);
+ udelay(30);
+
+ absy = adc_single(ADC_CHANNEL_TS_Y);
+
+ scpdr = __raw_readb(SCPDR);
+ scpdr |= SCPDR_TS_SCAN_Y;
+ scpdr &= ~SCPDR_TS_SCAN_X;
+ __raw_writeb(scpdr, SCPDR);
+ udelay(30);
+
+ absx = adc_single(ADC_CHANNEL_TS_X);
+
+ scpdr = __raw_readb(SCPDR);
+ scpdr |= SCPDR_TS_SCAN_X;
+ scpdr &= ~SCPDR_TS_SCAN_ENABLE;
+ __raw_writeb(scpdr, SCPDR);
+ udelay(100);
+ touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN;
+ }
+
+ if (touched) {
+ input_report_key(hp680_ts_dev, BTN_TOUCH, 1);
+ input_report_abs(hp680_ts_dev, ABS_X, absx);
+ input_report_abs(hp680_ts_dev, ABS_Y, absy);
+ } else {
+ input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
+ }
+
+ input_sync(hp680_ts_dev);
+ enable_irq(HP680_TS_IRQ);
+}
+
+static irqreturn_t hp680_ts_interrupt(int irq, void *dev)
+{
+ disable_irq_nosync(irq);
+ schedule_delayed_work(&work, HZ / 20);
+
+ return IRQ_HANDLED;
+}
+
+static int __init hp680_ts_init(void)
+{
+ int err;
+
+ hp680_ts_dev = input_allocate_device();
+ if (!hp680_ts_dev)
+ return -ENOMEM;
+
+ hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+ hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(hp680_ts_dev, ABS_X,
+ HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
+ input_set_abs_params(hp680_ts_dev, ABS_Y,
+ HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);
+
+ hp680_ts_dev->name = "HP Jornada touchscreen";
+ hp680_ts_dev->phys = "hp680_ts/input0";
+
+ if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
+ 0, MODNAME, NULL) < 0) {
+ printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
+ HP680_TS_IRQ);
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ err = input_register_device(hp680_ts_dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: free_irq(HP680_TS_IRQ, NULL);
+ cancel_delayed_work_sync(&work);
+ fail1: input_free_device(hp680_ts_dev);
+ return err;
+}
+
+static void __exit hp680_ts_exit(void)
+{
+ free_irq(HP680_TS_IRQ, NULL);
+ cancel_delayed_work_sync(&work);
+ input_unregister_device(hp680_ts_dev);
+}
+
+module_init(hp680_ts_init);
+module_exit(hp680_ts_exit);
+
+MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
+MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/htcpen.c b/drivers/input/touchscreen/htcpen.c
new file mode 100644
index 00000000..d13143b6
--- /dev/null
+++ b/drivers/input/touchscreen/htcpen.c
@@ -0,0 +1,250 @@
+/*
+ * HTC Shift touchscreen driver
+ *
+ * Copyright (C) 2008 Pau Oliva Fora <pof@eslack.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/isa.h>
+#include <linux/ioport.h>
+#include <linux/dmi.h>
+
+MODULE_AUTHOR("Pau Oliva Fora <pau@eslack.org>");
+MODULE_DESCRIPTION("HTC Shift touchscreen driver");
+MODULE_LICENSE("GPL");
+
+#define HTCPEN_PORT_IRQ_CLEAR 0x068
+#define HTCPEN_PORT_INIT 0x06c
+#define HTCPEN_PORT_INDEX 0x0250
+#define HTCPEN_PORT_DATA 0x0251
+#define HTCPEN_IRQ 3
+
+#define DEVICE_ENABLE 0xa2
+#define DEVICE_DISABLE 0xa3
+
+#define X_INDEX 3
+#define Y_INDEX 5
+#define TOUCH_INDEX 0xb
+#define LSB_XY_INDEX 0xc
+#define X_AXIS_MAX 2040
+#define Y_AXIS_MAX 2040
+
+static bool invert_x;
+module_param(invert_x, bool, 0644);
+MODULE_PARM_DESC(invert_x, "If set, X axis is inverted");
+static bool invert_y;
+module_param(invert_y, bool, 0644);
+MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
+
+static irqreturn_t htcpen_interrupt(int irq, void *handle)
+{
+ struct input_dev *htcpen_dev = handle;
+ unsigned short x, y, xy;
+
+ /* 0 = press; 1 = release */
+ outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX);
+
+ if (inb_p(HTCPEN_PORT_DATA)) {
+ input_report_key(htcpen_dev, BTN_TOUCH, 0);
+ } else {
+ outb_p(X_INDEX, HTCPEN_PORT_INDEX);
+ x = inb_p(HTCPEN_PORT_DATA);
+
+ outb_p(Y_INDEX, HTCPEN_PORT_INDEX);
+ y = inb_p(HTCPEN_PORT_DATA);
+
+ outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX);
+ xy = inb_p(HTCPEN_PORT_DATA);
+
+ /* get high resolution value of X and Y using LSB */
+ x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf));
+ y = (y * 8) + (xy & 0xf);
+ if (invert_x)
+ x = X_AXIS_MAX - x;
+ if (invert_y)
+ y = Y_AXIS_MAX - y;
+
+ if (x != X_AXIS_MAX && x != 0) {
+ input_report_key(htcpen_dev, BTN_TOUCH, 1);
+ input_report_abs(htcpen_dev, ABS_X, x);
+ input_report_abs(htcpen_dev, ABS_Y, y);
+ }
+ }
+
+ input_sync(htcpen_dev);
+
+ inb_p(HTCPEN_PORT_IRQ_CLEAR);
+
+ return IRQ_HANDLED;
+}
+
+static int htcpen_open(struct input_dev *dev)
+{
+ outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
+
+ return 0;
+}
+
+static void htcpen_close(struct input_dev *dev)
+{
+ outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
+ synchronize_irq(HTCPEN_IRQ);
+}
+
+static int __devinit htcpen_isa_probe(struct device *dev, unsigned int id)
+{
+ struct input_dev *htcpen_dev;
+ int err = -EBUSY;
+
+ if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) {
+ printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+ HTCPEN_PORT_IRQ_CLEAR);
+ goto request_region1_failed;
+ }
+
+ if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) {
+ printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+ HTCPEN_PORT_INIT);
+ goto request_region2_failed;
+ }
+
+ if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) {
+ printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+ HTCPEN_PORT_INDEX);
+ goto request_region3_failed;
+ }
+
+ htcpen_dev = input_allocate_device();
+ if (!htcpen_dev) {
+ printk(KERN_ERR "htcpen: can't allocate device\n");
+ err = -ENOMEM;
+ goto input_alloc_failed;
+ }
+
+ htcpen_dev->name = "HTC Shift EC TouchScreen";
+ htcpen_dev->id.bustype = BUS_ISA;
+
+ htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+ htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0);
+ input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0);
+
+ htcpen_dev->open = htcpen_open;
+ htcpen_dev->close = htcpen_close;
+
+ err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen",
+ htcpen_dev);
+ if (err) {
+ printk(KERN_ERR "htcpen: irq busy\n");
+ goto request_irq_failed;
+ }
+
+ inb_p(HTCPEN_PORT_IRQ_CLEAR);
+
+ err = input_register_device(htcpen_dev);
+ if (err)
+ goto input_register_failed;
+
+ dev_set_drvdata(dev, htcpen_dev);
+
+ return 0;
+
+ input_register_failed:
+ free_irq(HTCPEN_IRQ, htcpen_dev);
+ request_irq_failed:
+ input_free_device(htcpen_dev);
+ input_alloc_failed:
+ release_region(HTCPEN_PORT_INDEX, 2);
+ request_region3_failed:
+ release_region(HTCPEN_PORT_INIT, 1);
+ request_region2_failed:
+ release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
+ request_region1_failed:
+ return err;
+}
+
+static int __devexit htcpen_isa_remove(struct device *dev, unsigned int id)
+{
+ struct input_dev *htcpen_dev = dev_get_drvdata(dev);
+
+ input_unregister_device(htcpen_dev);
+
+ free_irq(HTCPEN_IRQ, htcpen_dev);
+
+ release_region(HTCPEN_PORT_INDEX, 2);
+ release_region(HTCPEN_PORT_INIT, 1);
+ release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int htcpen_isa_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
+
+ return 0;
+}
+
+static int htcpen_isa_resume(struct device *dev, unsigned int n)
+{
+ outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
+
+ return 0;
+}
+#endif
+
+static struct isa_driver htcpen_isa_driver = {
+ .probe = htcpen_isa_probe,
+ .remove = __devexit_p(htcpen_isa_remove),
+#ifdef CONFIG_PM
+ .suspend = htcpen_isa_suspend,
+ .resume = htcpen_isa_resume,
+#endif
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "htcpen",
+ }
+};
+
+static struct dmi_system_id __initdata htcshift_dmi_table[] = {
+ {
+ .ident = "Shift",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Shift"),
+ },
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);
+
+static int __init htcpen_isa_init(void)
+{
+ if (!dmi_check_system(htcshift_dmi_table))
+ return -ENODEV;
+
+ return isa_register_driver(&htcpen_isa_driver, 1);
+}
+
+static void __exit htcpen_isa_exit(void)
+{
+ isa_unregister_driver(&htcpen_isa_driver);
+}
+
+module_init(htcpen_isa_init);
+module_exit(htcpen_isa_exit);
diff --git a/drivers/input/touchscreen/icn83xx_ts/Kconfig b/drivers/input/touchscreen/icn83xx_ts/Kconfig
new file mode 100755
index 00000000..dbd6a729
--- /dev/null
+++ b/drivers/input/touchscreen/icn83xx_ts/Kconfig
@@ -0,0 +1,16 @@
+#
+# ICN83XX capacity touch screen driver configuration
+#
+config TOUCHSCREEN_ICN83XX
+ tristate "ICN83XX I2C Capacitive Touchscreen Input Driver Support"
+ depends on ARCH_WMT
+ default y
+ help
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_icn83xx
+
diff --git a/drivers/input/touchscreen/icn83xx_ts/Makefile b/drivers/input/touchscreen/icn83xx_ts/Makefile
new file mode 100755
index 00000000..e1070854
--- /dev/null
+++ b/drivers/input/touchscreen/icn83xx_ts/Makefile
@@ -0,0 +1,32 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_icn83xx
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := icn83xx.o flash.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers modules.builtin
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers modules.builtin
diff --git a/drivers/input/touchscreen/icn83xx_ts/flash.c b/drivers/input/touchscreen/icn83xx_ts/flash.c
new file mode 100755
index 00000000..595545d8
--- /dev/null
+++ b/drivers/input/touchscreen/icn83xx_ts/flash.c
@@ -0,0 +1,973 @@
+/*++
+
+ Copyright (c) 2012-2022 ChipOne Technology (Beijing) Co., Ltd. All Rights Reserved.
+ This PROPRIETARY SOFTWARE is the property of ChipOne Technology (Beijing) Co., Ltd.
+ and may contains trade secrets and/or other confidential information of ChipOne
+ Technology (Beijing) Co., Ltd. This file shall not be disclosed to any third party,
+ in whole or in part, without prior written consent of ChipOne.
+ THIS PROPRIETARY SOFTWARE & ANY RELATED DOCUMENTATION ARE PROVIDED AS IS,
+ WITH ALL FAULTS, & WITHOUT WARRANTY OF ANY KIND. CHIPONE DISCLAIMS ALL EXPRESS OR
+ IMPLIED WARRANTIES.
+
+ File Name: flash.c
+ Abstract:
+ flash operation, read write etc.
+ Author: Zhimin Tian
+ Date : 10 30,2012
+ Version: 0.1[.revision]
+ History :
+ Change logs.
+ --*/
+#include "icn83xx.h"
+
+struct file *fp;
+int g_status = R_OK;
+static char fw_mode = 0;
+static int fw_size = 0;
+static unsigned char *fw_buf;
+
+void icn83xx_rawdatadump(short *mem, int size, char br)
+{
+ int i;
+ for(i=0;i<size; i++)
+ {
+ if((i!=0)&&(i%br == 0))
+ printk("\n");
+ printk(" %5d", mem[i]);
+ }
+ printk("\n");
+}
+
+void icn83xx_memdump(char *mem, int size)
+{
+ int i;
+ for(i=0;i<size; i++)
+ {
+ if(i%16 == 0)
+ printk("\n");
+ printk(" 0x%2x", mem[i]);
+ }
+ printk("\n");
+}
+
+int icn83xx_checksum(int sum, char *buf, unsigned int size)
+{
+ int i;
+ for(i=0; i<size; i++)
+ {
+ sum = sum + buf[i];
+ }
+ return sum;
+}
+
+
+int icn83xx_update_status(int status)
+{
+// flash_info("icn83xx_update_status: %d\n", status);
+ g_status = status;
+ return 0;
+}
+
+int icn83xx_get_status(void)
+{
+ return g_status;
+}
+
+void icn83xx_set_fw(int size, unsigned char *buf)
+{
+ fw_size = size;
+ fw_buf = buf;
+
+}
+
+/***********************************************************************************************
+Name : icn83xx_writeInfo
+Input : addr, value
+Output :
+function : write Flash Info
+***********************************************************************************************/
+
+int icn83xx_writeInfo(unsigned short addr, char value)
+{
+ int ret = -1;
+ char temp_buf[3];
+
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(230, temp_buf, 2);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(2);
+ temp_buf[0] = value;
+ ret = icn83xx_i2c_txdata(232, temp_buf, 1);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(5);
+ return 0;
+}
+/***********************************************************************************************
+Name : icn83xx_readInfo
+Input :
+Output :
+function : read Flash info
+***********************************************************************************************/
+
+int icn83xx_readInfo(unsigned short addr, char *value)
+{
+ int ret = -1;
+ char temp_buf[3];
+
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(230, temp_buf, 2);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(2);
+ ret = icn83xx_i2c_rxdata(232, value, 1);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(2);
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_writeReg
+Input : addr, value
+Output :
+function : write MCU xdata and reg
+***********************************************************************************************/
+
+int icn83xx_writeReg(unsigned short addr, char value)
+{
+ int ret = -1;
+ char temp_buf[3];
+
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(224, temp_buf, 2);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(2);
+ temp_buf[0] = value;
+ ret = icn83xx_i2c_txdata(226, temp_buf, 1);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(5);
+ return 0;
+}
+/***********************************************************************************************
+Name : icn83xx_readReg
+Input :
+Output :
+function : read MCU xdata and reg
+***********************************************************************************************/
+
+int icn83xx_readReg(unsigned short addr, char *value)
+{
+ int ret = -1;
+ char temp_buf[3];
+
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(224, temp_buf, 2);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(2);
+
+ ret = icn83xx_i2c_rxdata(226, value, 1);
+ if (ret < 0) {
+ op_error("%s failed! ret: %d\n", __func__, ret);
+ return -1;
+ }
+ mdelay(2);
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_open_fw
+Input : *fw
+
+Output : file size
+function : open the fw file, and return total size
+***********************************************************************************************/
+int icn83xx_open_fw( char *fw)
+{
+ int file_size;
+ mm_segment_t fs;
+ struct inode *inode = NULL;
+ if(strcmp(fw, "icn83xx_firmware") == 0)
+ {
+ fw_mode = 1; //use inner array
+ return fw_size;
+ }
+ else
+ {
+ fw_mode = 0; //use file in file system
+ }
+
+ fp = filp_open(fw, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ flash_error("read fw file error\n");
+ return -1;
+ }
+ else
+ flash_info("open fw file ok\n");
+
+ inode = fp->f_dentry->d_inode;
+ file_size = inode->i_size;
+ flash_info("file size: %d\n", file_size);
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ return file_size;
+
+}
+
+/***********************************************************************************************
+Name : icn83xx_read_fw
+Input : offset
+ length, read length
+ buf, return buffer
+Output :
+function : read data to buffer
+***********************************************************************************************/
+int icn83xx_read_fw(int offset, int length, char *buf)
+{
+ loff_t pos = offset;
+ if(fw_mode == 1)
+ {
+ memcpy(buf, fw_buf+offset, length);
+ }
+ else
+ {
+ vfs_read(fp, buf, length, &pos);
+ }
+// icn83xx_memdump(buf, length);
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_close_fw
+Input :
+Output :
+function : close file
+***********************************************************************************************/
+int icn83xx_close_fw(void)
+{
+ if(fw_mode == 0)
+ {
+ filp_close(fp, NULL);
+ }
+
+ return 0;
+}
+/***********************************************************************************************
+Name : icn83xx_readVersion
+Input : void
+Output :
+function : return version
+***********************************************************************************************/
+int icn83xx_readVersion(void)
+{
+ int err = 0;
+ char tmp[2];
+ short CurVersion;
+ err = icn83xx_i2c_rxdata(12, tmp, 2);
+ if (err < 0) {
+ calib_error("%s failed: %d\n", __func__, err);
+ return err;
+ }
+ CurVersion = (tmp[0]<<8) | tmp[1];
+ return CurVersion;
+}
+
+/***********************************************************************************************
+Name : icn83xx_changemode
+Input : normal/factory/config
+Output :
+function : change work mode
+***********************************************************************************************/
+int icn83xx_changemode(char mode)
+{
+ char value = 0x0;
+ icn83xx_write_reg(0, mode);
+ mdelay(1);
+ icn83xx_read_reg(1, &value);
+ while(value != 0)
+ {
+ mdelay(1);
+ icn83xx_read_reg(1, &value);
+ }
+// calib_info("icn83xx_changemode ok\n");
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_readrawdata
+Input : rownum and length
+Output :
+function : read one row rawdata
+***********************************************************************************************/
+
+int icn83xx_readrawdata(char *buffer, char row, char length)
+{
+ int err = 0;
+ int i;
+// calib_info("readrawdata: %d, length: %d\n", row, length);
+ icn83xx_write_reg(3, row);
+ mdelay(1);
+ err = icn83xx_i2c_rxdata(160, buffer, length);
+ if (err < 0) {
+ calib_error("%s failed: %d\n", __func__, err);
+ return err;
+ }
+
+ for(i=0; i<length; i=i+2)
+ {
+ swap_ab(buffer[i], buffer[i+1]);
+ }
+ return err;
+}
+
+/***********************************************************************************************
+Name : icn83xx_scanTP
+Input :
+Output :
+function : scan one frame rawdata
+***********************************************************************************************/
+
+int icn83xx_scanTP(void)
+{
+ char value = 0;
+ icn83xx_write_reg(2, 0x0);
+ mdelay(1);
+ icn83xx_read_reg(2, &value);
+ while(value != 1)
+ {
+ mdelay(1);
+ icn83xx_read_reg(2, &value);
+ }
+// calib_info("icn83xx_scanTP ok\n");
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_readTP
+Input : rownum and columnnum
+Output :
+function : read one frame rawdata
+***********************************************************************************************/
+
+int icn83xx_readTP(char row_num, char column_num, char *buffer)
+{
+ int err = 0;
+ int i;
+// calib_info("icn83xx_readTP\n");
+ icn83xx_changemode(1);
+ icn83xx_scanTP();
+ for(i=0; i<row_num; i++)
+ {
+ icn83xx_readrawdata(&buffer[i*16*2], i, column_num*2);
+ }
+ icn83xx_changemode(0);
+ return err;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_goto_progmode
+Input :
+Output :
+function : change MCU to progmod
+***********************************************************************************************/
+int icn83xx_goto_progmode(void)
+{
+ int ret = -1;
+// char value[64];
+ char regValue = 0;
+
+ flash_info("icn83xx_goto_progmode\n");
+
+ ret = icn83xx_readReg(0x009, &regValue);
+ if(ret != 0)
+ return ret;
+ flash_info("[0x009]: 0x%x\n", regValue);
+
+// open clock
+ if(regValue != 0xDF)
+ {
+ icn83xx_changemode(2);
+ ret = icn83xx_writeReg(0x002, 0x00);
+ if(ret != 0)
+ return ret;
+ ret = icn83xx_writeReg(0x009, 0xDF);
+ if(ret != 0)
+ return ret;
+ ret = icn83xx_writeReg(0x010, 0x00);
+ if(ret != 0)
+ return ret;
+
+ }
+
+/*
+ addr = 0x0;
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(230, temp_buf, 2);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+
+ temp_buf[0] = 0xff;
+ ret = icn83xx_i2c_txdata(232, temp_buf, 1);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+*/
+ ret = icn83xx_writeInfo(0x0, 0xff);
+ if(ret != 0)
+ return ret;
+
+/*
+ addr = 0x1;
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(230, temp_buf, 2);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+
+ temp_buf[0] = 0xff;
+ ret = icn83xx_i2c_txdata(232, temp_buf, 1);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+*/
+ ret = icn83xx_writeInfo(0x1, 0xff);
+ if(ret != 0)
+ return ret;
+
+ ret = icn83xx_writeInfo(0x10, 0xff);
+ if(ret != 0)
+ return ret;
+
+ ret = icn83xx_writeInfo(0x11, 0xff);
+ if(ret != 0)
+ return ret;
+/*
+ addr = 0xf00;
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(224, temp_buf, 2);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+ temp_buf[0] = 0x1;
+ ret = icn83xx_i2c_txdata(226, temp_buf, 1);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+*/
+ ret = icn83xx_writeReg(0xf00, 1);
+ if(ret != 0)
+ return ret;
+ icn83xx_ts_reset();
+ //mdelay(100);
+ msleep(100);
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_check_progmod
+Input :
+Output :
+function : check if MCU at progmode or not
+***********************************************************************************************/
+int icn83xx_check_progmod(void)
+{
+ int ret;
+ unsigned char ucTemp = 0x0;
+ ret = icn83xx_prog_i2c_rxdata(0x0, &ucTemp, 1);
+ flash_info("icn83xx_check_progmod: 0x%x\n", ucTemp);
+ if(ret < 0)
+ {
+ flash_error("icn83xx_check_progmod error, ret: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_uu
+Input :
+Output :
+function : unlock flash
+***********************************************************************************************/
+int icn83xx_uu(void)
+{
+ unsigned char ucTemp = 0x0;
+ ucTemp = 0x1e;
+ icn83xx_prog_i2c_txdata(0x050a, &ucTemp, 1);
+ ucTemp = 0x10;
+ icn83xx_prog_i2c_txdata(0x050b, &ucTemp, 1);
+ return 0;
+}
+/***********************************************************************************************
+Name : icn83xx_ll
+Input :
+Output :
+function : lock flash
+***********************************************************************************************/
+void icn83xx_ll(void)
+{
+ unsigned char ucTemp = 0x0;
+ ucTemp = 0xcc;
+ icn83xx_prog_i2c_txdata(0x050a, &ucTemp, 1);
+ ucTemp = 0xcc;
+ icn83xx_prog_i2c_txdata(0x050b, &ucTemp, 1);
+}
+
+/***********************************************************************************************
+Name : icn83xx_op1
+Input :
+Output :
+function : erase flash
+***********************************************************************************************/
+
+int icn83xx_op1(char info, unsigned short offset, unsigned int size)
+{
+ int count = 0;
+ unsigned char ucTemp = 0x0;
+ unsigned short uiAddress = 0x0;
+ int i;
+
+ icn83xx_uu();
+ for(i=0; i<size; )
+ {
+ uiAddress = offset + i;
+// flash_info("uiAddress: 0x%x\n", uiAddress);
+ ucTemp = U16LOBYTE(uiAddress);
+ icn83xx_prog_i2c_txdata(0x0502, &ucTemp, 1);
+ ucTemp = U16HIBYTE(uiAddress);
+ icn83xx_prog_i2c_txdata(0x0503, &ucTemp, 1);
+
+ ucTemp = 0x02;
+ icn83xx_prog_i2c_txdata(0x0500, &ucTemp, 1);
+ ucTemp = 0x01;
+ count = 0;
+ while(ucTemp)
+ {
+ icn83xx_prog_i2c_rxdata(0x0501, &ucTemp, 1);
+ count++;
+ if(count > 5000)
+ {
+ flash_error("op1 ucTemp: 0x%x\n", ucTemp);
+ return 1;
+ }
+ }
+ i = i+1024;
+ }
+ icn83xx_ll();
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_op2
+Input :
+Output :
+function : progm flash
+***********************************************************************************************/
+int icn83xx_op2(char info, unsigned short offset, unsigned char * buffer, unsigned int size)
+{
+ int count = 0;
+ unsigned int flash_size;
+ unsigned char ucTemp;
+ unsigned short uiAddress;
+ ucTemp = 0x00;
+ uiAddress = 0x1000;
+
+ icn83xx_prog_i2c_txdata(uiAddress, buffer, size);
+
+ icn83xx_uu();
+
+ ucTemp = U16LOBYTE(offset);
+ icn83xx_prog_i2c_txdata(0x0502, &ucTemp, 1);
+ ucTemp = U16HIBYTE(offset);
+ icn83xx_prog_i2c_txdata(0x0503, &ucTemp, 1);
+
+ icn83xx_prog_i2c_txdata(0x0504, (char *)&uiAddress, 2);
+
+
+//ensure size is even
+ if(size%2 != 0)
+ {
+ flash_info("write op size: %d\n", size);
+ flash_size = size+1;
+ }
+ else
+ flash_size = size;
+
+ ucTemp = U16LOBYTE(flash_size);
+ icn83xx_prog_i2c_txdata(0x0506, &ucTemp, 1);
+ ucTemp = U16HIBYTE(flash_size);
+ icn83xx_prog_i2c_txdata(0x0507, &ucTemp, 1);
+ ucTemp = 0x01;
+
+ if(info > 0)
+ ucTemp = 0x01 | (1<<3);
+
+ icn83xx_prog_i2c_txdata(0x0500, &ucTemp, 1); //
+ while(ucTemp)
+ {
+ icn83xx_prog_i2c_rxdata(0x0501, &ucTemp, 1);
+ count++;
+ if(count > 5000)
+ {
+ flash_error("op2 ucTemp: 0x%x\n", ucTemp);
+ return 1;
+ }
+
+ }
+ icn83xx_ll();
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_op3
+Input :
+Output :
+function : read flash
+***********************************************************************************************/
+int icn83xx_op3(char info, unsigned short offset, unsigned char * buffer, unsigned int size)
+{
+ int count = 0;
+ unsigned int flash_size;
+ unsigned char ucTemp;
+ unsigned short uiAddress;
+ ucTemp = 0x00;
+ uiAddress = 0x1000;
+ icn83xx_uu();
+ ucTemp = U16LOBYTE(offset);
+ icn83xx_prog_i2c_txdata(0x0502, &ucTemp, 1);
+ ucTemp = U16HIBYTE(offset);
+ icn83xx_prog_i2c_txdata(0x0503, &ucTemp, 1);
+
+ icn83xx_prog_i2c_txdata(0x0504, (unsigned char*)&uiAddress, 2);
+
+//ensure size is even
+ if(size%2 != 0)
+ {
+ flash_info("read op size: %d\n", size);
+ flash_size = size+1;
+ }
+ else
+ flash_size = size;
+
+ ucTemp = U16LOBYTE(flash_size);
+ icn83xx_prog_i2c_txdata(0x0506, &ucTemp, 1);
+
+ ucTemp = U16HIBYTE(flash_size);
+ icn83xx_prog_i2c_txdata(0x0507, &ucTemp, 1);
+ ucTemp = 0x40;
+
+ if(info > 0)
+ ucTemp = 0x40 | (1<<3);
+
+ icn83xx_prog_i2c_txdata(0x0500, &ucTemp, 1);
+ ucTemp = 0x01;
+ while(ucTemp)
+ {
+ icn83xx_prog_i2c_rxdata(0x0501, &ucTemp, 1);
+ count++;
+ if(count > 5000)
+ {
+ flash_error("op3 ucTemp: 0x%x\n", ucTemp);
+ return 1;
+ }
+
+ }
+ icn83xx_ll();
+ icn83xx_prog_i2c_rxdata(uiAddress, buffer, size);
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_goto_nomalmode
+Input :
+Output :
+function : when prog flash ok, change flash info flag
+***********************************************************************************************/
+int icn83xx_goto_nomalmode(void)
+{
+ int ret = -1;
+ //unsigned short addr = 0;
+ char temp_buf[3];
+
+ flash_info("icn83xx_goto_nomalmode\n");
+ temp_buf[0] = 0x03;
+ icn83xx_prog_i2c_txdata(0x0f00, temp_buf, 1);
+
+ msleep(100);
+/*
+ addr = 0;
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ temp_buf[2] = 0;
+ ret = icn83xx_i2c_txdata(230, temp_buf, 2);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+
+ icn83xx_i2c_rxdata(232, &temp_buf[2], 1);
+ flash_info("temp_buf[2]: 0x%x\n", temp_buf[2]);
+*/
+ ret = icn83xx_readInfo(0, &temp_buf[2]);
+ if(ret != 0)
+ return ret;
+ flash_info("temp_buf[2]: 0x%x\n", temp_buf[2]);
+ if(temp_buf[2] == 0xff)
+ {
+/*
+ addr = 0;
+ temp_buf[0] = U16HIBYTE(addr);
+ temp_buf[1] = U16LOBYTE(addr);
+ ret = icn83xx_i2c_txdata(230, temp_buf, 2);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+ temp_buf[0] = 0x11;
+ ret = icn83xx_i2c_txdata(232, temp_buf, 1);
+ if (ret < 0) {
+ pr_err("write reg failed! ret: %d\n", ret);
+ return -1;
+ }
+*/
+ ret = icn83xx_writeInfo(0, 0x11);
+ if(ret != 0)
+ return ret;
+
+ }
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_read_fw_Ver
+Input : fw
+Output :
+function : read fw version
+***********************************************************************************************/
+
+short icn83xx_read_fw_Ver(char *fw)
+{
+ short FWversion;
+ char tmp[2];
+ int file_size;
+ file_size = icn83xx_open_fw(fw);
+ if(file_size < 0)
+ {
+ return -1;
+ }
+ icn83xx_read_fw(0x4000, 2, &tmp[0]);
+
+ icn83xx_close_fw();
+ FWversion = (tmp[0]<<8)|tmp[1];
+// flash_info("FWversion: 0x%x\n", FWversion);
+ return FWversion;
+}
+
+
+
+
+/***********************************************************************************************
+Name : icn83xx_fw_update
+Input : fw
+Output :
+function : upgrade fw
+***********************************************************************************************/
+
+E_UPGRADE_ERR_TYPE icn83xx_fw_update(char *fw)
+{
+ int file_size, last_length;
+ int j, num;
+ int checksum_bak = 0;
+ int checksum = 0;
+ char temp_buf[B_SIZE];
+#ifdef ENABLE_BYTE_CHECK
+ char temp_buf1[B_SIZE];
+#endif
+
+ file_size = icn83xx_open_fw(fw);
+ if(file_size < 0)
+ {
+ icn83xx_update_status(R_FILE_ERR);
+ return R_FILE_ERR;
+ }
+
+ if(icn83xx_goto_progmode() != 0)
+ {
+ if(icn83xx_check_progmod() < 0)
+ {
+ icn83xx_update_status(R_STATE_ERR);
+ icn83xx_close_fw();
+ return R_STATE_ERR;
+ }
+ }
+// msleep(50);
+
+ if(icn83xx_op1(0, 0, file_size) != 0)
+ {
+ flash_error("icn83xx_op1 error\n");
+ icn83xx_update_status(R_ERASE_ERR);
+ icn83xx_close_fw();
+ return R_ERASE_ERR;
+ }
+ icn83xx_update_status(5);
+
+ num = file_size/B_SIZE;
+ for(j=0; j < num; j++)
+ {
+ icn83xx_read_fw(j*B_SIZE, B_SIZE, temp_buf);
+
+// icn83xx_op3(0, j*B_SIZE, temp_buf1, B_SIZE);
+// icn83xx_memdump(temp_buf1, B_SIZE);
+
+ if(icn83xx_op2(0, j*B_SIZE, temp_buf, B_SIZE) != 0)
+ {
+ icn83xx_update_status(R_PROGRAM_ERR);
+ icn83xx_close_fw();
+ return R_PROGRAM_ERR;
+ }
+ checksum_bak = icn83xx_checksum(checksum_bak, temp_buf, B_SIZE);
+
+ icn83xx_update_status(5+(int)(60*j/num));
+ }
+ last_length = file_size - B_SIZE*j;
+ if(last_length > 0)
+ {
+ icn83xx_read_fw(j*B_SIZE, last_length, temp_buf);
+
+// icn83xx_op3(0, j*B_SIZE, temp_buf1, B_SIZE);
+// icn83xx_memdump(temp_buf1, B_SIZE);
+
+ if(icn83xx_op2(0, j*B_SIZE, temp_buf, last_length) != 0)
+ {
+ icn83xx_update_status(R_PROGRAM_ERR);
+ icn83xx_close_fw();
+ return R_PROGRAM_ERR;
+ }
+ checksum_bak = icn83xx_checksum(checksum_bak, temp_buf, last_length);
+ }
+
+ icn83xx_close_fw();
+ icn83xx_update_status(65);
+
+#ifdef ENABLE_BYTE_CHECK
+ file_size = icn83xx_open_fw(fw);
+ num = file_size/B_SIZE;
+#endif
+
+ for(j=0; j < num; j++)
+ {
+
+#ifdef ENABLE_BYTE_CHECK
+ icn83xx_read_fw(j*B_SIZE, B_SIZE, temp_buf1);
+#endif
+ icn83xx_op3(0, j*B_SIZE, temp_buf, B_SIZE);
+ checksum = icn83xx_checksum(checksum, temp_buf, B_SIZE);
+
+#ifdef ENABLE_BYTE_CHECK
+ if(memcmp(temp_buf1, temp_buf, B_SIZE) != 0)
+ {
+ flash_error("cmp error, %d\n", j);
+ icn83xx_memdump(temp_buf1, B_SIZE);
+ icn83xx_memdump(temp_buf, B_SIZE);
+ icn83xx_update_status(R_VERIFY_ERR);
+#ifdef ENABLE_BYTE_CHECK
+ icn83xx_close_fw();
+#endif
+ return R_VERIFY_ERR;
+ //while(1);
+ }
+#endif
+ icn83xx_update_status(65+(int)(30*j/num));
+ }
+
+#ifdef ENABLE_BYTE_CHECK
+ last_length = file_size - B_SIZE*j;
+#endif
+ if(last_length > 0)
+ {
+#ifdef ENABLE_BYTE_CHECK
+ icn83xx_read_fw(j*B_SIZE, last_length, temp_buf1);
+#endif
+ icn83xx_op3(0, j*B_SIZE, temp_buf, last_length);
+ checksum = icn83xx_checksum(checksum, temp_buf, last_length);
+
+#ifdef ENABLE_BYTE_CHECK
+ if(memcmp(temp_buf1, temp_buf, last_length) != 0)
+ {
+ flash_error("cmp error, %d\n", j);
+ icn83xx_memdump(temp_buf1, last_length);
+ icn83xx_memdump(temp_buf, last_length);
+ icn83xx_update_status(R_VERIFY_ERR);
+#ifdef ENABLE_BYTE_CHECK
+ icn83xx_close_fw();
+#endif
+ return R_VERIFY_ERR;
+ //while(1);
+ }
+#endif
+
+ }
+
+#ifdef ENABLE_BYTE_CHECK
+ icn83xx_close_fw();
+#endif
+
+ flash_info("checksum_bak: 0x%x, checksum: 0x%x\n", checksum_bak, checksum);
+ if(checksum_bak != checksum)
+ {
+ flash_error("upgrade checksum error\n");
+ icn83xx_update_status(R_VERIFY_ERR);
+ return R_VERIFY_ERR;
+ }
+
+ if(icn83xx_goto_nomalmode() != 0)
+ {
+ flash_error("icn83xx_goto_nomalmode error\n");
+ icn83xx_update_status(R_STATE_ERR);
+ return R_STATE_ERR;
+ }
+
+ icn83xx_update_status(R_OK);
+ flash_info("upgrade ok\n");
+ return R_OK;
+}
diff --git a/drivers/input/touchscreen/icn83xx_ts/icn83xx.c b/drivers/input/touchscreen/icn83xx_ts/icn83xx.c
new file mode 100755
index 00000000..60e42e50
--- /dev/null
+++ b/drivers/input/touchscreen/icn83xx_ts/icn83xx.c
@@ -0,0 +1,2034 @@
+/*++
+
+ Copyright (c) 2012-2022 ChipOne Technology (Beijing) Co., Ltd. All Rights Reserved.
+ This PROPRIETARY SOFTWARE is the property of ChipOne Technology (Beijing) Co., Ltd.
+ and may contains trade secrets and/or other confidential information of ChipOne
+ Technology (Beijing) Co., Ltd. This file shall not be disclosed to any third party,
+ in whole or in part, without prior written consent of ChipOne.
+ THIS PROPRIETARY SOFTWARE & ANY RELATED DOCUMENTATION ARE PROVIDED AS IS,
+ WITH ALL FAULTS, & WITHOUT WARRANTY OF ANY KIND. CHIPONE DISCLAIMS ALL EXPRESS OR
+ IMPLIED WARRANTIES.
+
+ File Name: icn83xx.c
+Abstract:
+input driver.
+Author: Zhimin Tian
+Date : 01,17,2013
+Version: 1.0
+History :
+2012,10,30, V0.1 first version
+--*/
+
+#include "icn83xx.h"
+
+#if COMPILE_FW_WITH_DRIVER
+#include "icn83xx_fw.h"
+#endif
+
+static struct touch_param g_param;
+static struct i2c_client *this_client;
+short log_rawdata[28][16];// = {0,};
+short log_diffdata[28][16];// = {0,};
+static int l_suspend = 0; // 1:suspend, 0:normal state
+
+#if SUPPORT_ROCKCHIP
+//if file system not ready,you can use inner array
+//static char firmware[128] = "icn83xx_firmware";
+#endif
+static char firmware[128] = {"/system/etc/firmware/fw.bin"};
+
+//static void icn_delayedwork_fun(struct work_struct *work);
+extern int register_bl_notifier(struct notifier_block *nb);
+extern int unregister_bl_notifier(struct notifier_block *nb);
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+#define dbg(fmt, args...) do{if (g_param.dbg) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args);}while(0)
+
+static ssize_t cat_dbg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "echo 1 > dbg : print debug message.\necho 0 > dbg : Do not print debug message.\n");
+}
+static ssize_t echo_dbg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ g_param.dbg = simple_strtoul(buf, NULL, 10) ? 1 : 0;
+ return count;
+}
+static DEVICE_ATTR(dbg, S_IRUGO | S_IWUSR, cat_dbg, echo_dbg);
+
+#if SUPPORT_SYSFS
+static enum hrtimer_restart chipone_timer_func(struct hrtimer *timer);
+static ssize_t icn83xx_show_update(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t icn83xx_store_update(struct device* cd, struct device_attribute *attr, const char* buf, size_t len);
+static ssize_t icn83xx_show_process(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t icn83xx_store_process(struct device* cd, struct device_attribute *attr,const char* buf, size_t len);
+
+static DEVICE_ATTR(update, S_IRUGO | S_IWUSR, icn83xx_show_update, icn83xx_store_update);
+static DEVICE_ATTR(process, S_IRUGO | S_IWUSR, icn83xx_show_process, icn83xx_store_process);
+
+static ssize_t icn83xx_show_process(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+ sprintf(buf, "icn83xx process\n");
+ ret = strlen(buf) + 1;
+ return ret;
+}
+
+static ssize_t icn83xx_store_process(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ unsigned long on_off = simple_strtoul(buf, NULL, 10);
+ if(on_off == 0)
+ {
+ icn83xx_ts->work_mode = on_off;
+ }
+ else if((on_off == 1) || (on_off == 2))
+ {
+ if((icn83xx_ts->work_mode == 0) && (icn83xx_ts->use_irq == 1))
+ {
+ hrtimer_init(&icn83xx_ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ icn83xx_ts->timer.function = chipone_timer_func;
+ hrtimer_start(&icn83xx_ts->timer, ktime_set(CTP_START_TIMER/1000, (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ icn83xx_ts->work_mode = on_off;
+ }
+ return len;
+}
+
+static ssize_t icn83xx_show_update(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+ sprintf(buf, "icn83xx firmware\n");
+ ret = strlen(buf) + 1;
+ return ret;
+}
+
+static ssize_t icn83xx_store_update(struct device* cd, struct device_attribute *attr, const char* buf, size_t len)
+{
+ //int err=0;
+ //unsigned long on_off = simple_strtoul(buf, NULL, 10);
+ return len;
+}
+
+static int icn83xx_create_sysfs(struct i2c_client *client)
+{
+ int err;
+ struct device *dev = &(client->dev);
+ icn83xx_trace("%s: \n",__func__);
+ err = device_create_file(dev, &dev_attr_update);
+ err = device_create_file(dev, &dev_attr_process);
+ return err;
+}
+
+#endif
+
+#if SUPPORT_PROC_FS
+
+pack_head cmd_head;
+static struct proc_dir_entry *icn83xx_proc_entry;
+int DATA_LENGTH = 0;
+static int icn83xx_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data)
+{
+ int ret = 0;
+
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ proc_info("%s \n",__func__);
+ if(down_interruptible(&icn83xx_ts->sem))
+ {
+ return -1;
+ }
+ ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ else
+ {
+ ret = CMD_HEAD_LENGTH;
+ }
+
+ proc_info("wr :0x%02x.\n", cmd_head.wr);
+ proc_info("flag:0x%02x.\n", cmd_head.flag);
+ proc_info("circle :%d.\n", (int)cmd_head.circle);
+ proc_info("times :%d.\n", (int)cmd_head.times);
+ proc_info("retry :%d.\n", (int)cmd_head.retry);
+ proc_info("data len:%d.\n", (int)cmd_head.data_len);
+ proc_info("addr len:%d.\n", (int)cmd_head.addr_len);
+ proc_info("addr:0x%02x%02x.\n", cmd_head.addr[0], cmd_head.addr[1]);
+ proc_info("len:%d.\n", (int)len);
+ proc_info("data:0x%02x%02x.\n", buff[CMD_HEAD_LENGTH], buff[CMD_HEAD_LENGTH+1]);
+ if (1 == cmd_head.wr) // write iic
+ {
+ if(1 == cmd_head.addr_len)
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ ret = icn83xx_i2c_txdata(cmd_head.addr[0], &cmd_head.data[0], cmd_head.data_len);
+ if (ret < 0) {
+ proc_error("write iic failed! ret: %d\n", ret);
+ goto write_out;
+ }
+ ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+ goto write_out;
+ }
+ }
+ else if(3 == cmd_head.wr)
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+ memset(firmware, 0, 128);
+ memcpy(firmware, &cmd_head.data[0], cmd_head.data_len);
+ proc_info("firmware : %s\n", firmware);
+ }
+ else if(5 == cmd_head.wr)
+ {
+ icn83xx_update_status(1);
+ ret = kernel_thread((int (*)(void *))icn83xx_fw_update,firmware,CLONE_KERNEL);
+ icn83xx_trace("the kernel_thread result is:%d\n", ret);
+ }
+ else if(7 == cmd_head.wr) //write reg
+ {
+ if(2 == cmd_head.addr_len)
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ ret = icn83xx_writeReg((cmd_head.addr[0]<<8)|cmd_head.addr[1], cmd_head.data[0]);
+ if (ret < 0) {
+ proc_error("write reg failed! ret: %d\n", ret);
+ goto write_out;
+ }
+ ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+ goto write_out;
+
+ }
+ }
+
+write_out:
+ up(&icn83xx_ts->sem);
+ return len;
+
+}
+static int icn83xx_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data )
+{
+ int i;
+ int ret = 0;
+ int data_len = 0;
+ int len = 0;
+ int loc = 0;
+ char retvalue;
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ if(down_interruptible(&icn83xx_ts->sem))
+ {
+ return -1;
+ }
+ proc_info("%s: count:%d, off:%d, cmd_head.data_len: %d\n",__func__, count, (int)off, cmd_head.data_len);
+ if (cmd_head.wr % 2)
+ {
+ ret = 0;
+ goto read_out;
+ }
+ else if (0 == cmd_head.wr) //read iic
+ {
+ if(1 == cmd_head.addr_len)
+ {
+ data_len = cmd_head.data_len;
+ if(cmd_head.addr[0] == 0xff)
+ {
+ page[0] = 83;
+ proc_info("read ic type: %d\n", page[0]);
+ }
+ else
+ {
+ while(data_len>0)
+ {
+ if (data_len > DATA_LENGTH)
+ {
+ len = DATA_LENGTH;
+ }
+ else
+ {
+ len = data_len;
+ }
+ data_len -= len;
+ memset(&cmd_head.data[0], 0, len+1);
+ ret = icn83xx_i2c_rxdata(cmd_head.addr[0]+loc, &cmd_head.data[0], len);
+ //proc_info("cmd_head.data[0]: 0x%02x\n", cmd_head.data[0]);
+ //proc_info("cmd_head.data[1]: 0x%02x\n", cmd_head.data[1]);
+ if(ret < 0)
+ {
+ icn83xx_error("read iic failed: %d\n", ret);
+ goto read_out;
+ }
+ else
+ {
+ //proc_info("iic read out %d bytes, loc: %d\n", len, loc);
+ memcpy(&page[loc], &cmd_head.data[0], len);
+ }
+ loc += len;
+ }
+ proc_info("page[0]: 0x%02x\n", page[0]);
+ proc_info("page[1]: 0x%02x\n", page[1]);
+ }
+ }
+ }
+ else if(2 == cmd_head.wr) //read rawdata
+ {
+ //scan tp rawdata
+ icn83xx_write_reg(4, 0x20);
+ mdelay(cmd_head.times);
+ icn83xx_read_reg(2, &retvalue);
+ while(retvalue != 1)
+ {
+ mdelay(cmd_head.times);
+ icn83xx_read_reg(2, &retvalue);
+ }
+
+ if(2 == cmd_head.addr_len)
+ {
+ for(i=0; i<cmd_head.addr[1]; i++)
+ {
+ icn83xx_write_reg(3, i);
+ mdelay(cmd_head.times);
+ ret = icn83xx_i2c_rxdata(128, &cmd_head.data[0], cmd_head.addr[0]*2);
+ if (ret < 0)
+ {
+ icn83xx_error("read rawdata failed: %d\n", ret);
+ goto read_out;
+ }
+ else
+ {
+ //proc_info("read rawdata out %d bytes, loc: %d\n", cmd_head.addr[0]*2, loc);
+ memcpy(&page[loc], &cmd_head.data[0], cmd_head.addr[0]*2);
+ }
+ loc += cmd_head.addr[0]*2;
+ }
+ for(i=0; i<cmd_head.data_len; i=i+2)
+ {
+ swap_ab(page[i], page[i+1]);
+ }
+ //icn83xx_rawdatadump(&page[0], cmd_head.data_len/2, cmd_head.addr[0]);
+ }
+
+ //finish scan tp rawdata
+ icn83xx_write_reg(2, 0x0);
+
+ }
+ else if(4 == cmd_head.wr) //get update status
+ {
+ page[0] = icn83xx_get_status();
+ }
+ else if(6 == cmd_head.wr) //read reg
+ {
+ if(2 == cmd_head.addr_len)
+ {
+ ret = icn83xx_readReg((cmd_head.addr[0]<<8)|cmd_head.addr[1], &cmd_head.data[0]);
+ if (ret < 0) {
+ proc_error("reg reg failed! ret: %d\n", ret);
+ goto read_out;
+ }
+ page[0] = cmd_head.data[0];
+ goto read_out;
+ }
+ }
+read_out:
+ up(&icn83xx_ts->sem);
+ proc_info("%s out: %d, cmd_head.data_len: %d\n\n",__func__, count, cmd_head.data_len);
+ return cmd_head.data_len;
+}
+
+int init_proc_node(void)
+{
+ int i;
+ memset(&cmd_head, 0, sizeof(cmd_head));
+ cmd_head.data = NULL;
+
+ i = 5;
+ while ((!cmd_head.data) && i)
+ {
+ cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
+ if (NULL != cmd_head.data)
+ {
+ break;
+ }
+ i--;
+ }
+ if (i)
+ {
+ //DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH;
+ DATA_LENGTH = i * DATA_LENGTH_UINT;
+ icn83xx_trace("alloc memory size:%d.\n", DATA_LENGTH);
+ }
+ else
+ {
+ proc_error("alloc for memory failed.\n");
+ return 0;
+ }
+
+ icn83xx_proc_entry = create_proc_entry(ICN83XX_ENTRY_NAME, 0666, NULL);
+ if (icn83xx_proc_entry == NULL)
+ {
+ proc_error("Couldn't create proc entry!\n");
+ return 0;
+ }
+ else
+ {
+ icn83xx_trace("Create proc entry success!\n");
+ icn83xx_proc_entry->write_proc = icn83xx_tool_write;
+ icn83xx_proc_entry->read_proc = icn83xx_tool_read;
+ }
+
+ return 1;
+}
+
+void uninit_proc_node(void)
+{
+ kfree(cmd_head.data);
+ cmd_head.data = NULL;
+ remove_proc_entry(ICN83XX_ENTRY_NAME, NULL);
+}
+
+#endif
+
+
+#if TOUCH_VIRTUAL_KEYS
+static ssize_t virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf,
+ __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":100:1030:50:60"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":280:1030:50:60"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":470:1030:50:60"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":900:1030:50:60"
+ "\n");
+}
+
+static struct kobj_attribute virtual_keys_attr = {
+ .attr = {
+ .name = "virtualkeys.chipone-ts",
+ .mode = S_IRUGO,
+ },
+ .show = &virtual_keys_show,
+};
+
+static struct attribute *properties_attrs[] = {
+ &virtual_keys_attr.attr,
+ NULL
+};
+
+static struct attribute_group properties_attr_group = {
+ .attrs = properties_attrs,
+};
+
+static void icn83xx_ts_virtual_keys_init(void)
+{
+ int ret = 0;
+ struct kobject *properties_kobj;
+ properties_kobj = kobject_create_and_add("board_properties", NULL);
+ if (properties_kobj)
+ ret = sysfs_create_group(properties_kobj,
+ &properties_attr_group);
+ if (!properties_kobj || ret)
+ pr_err("failed to create board_properties\n");
+}
+#endif
+
+
+/* ---------------------------------------------------------------------
+ *
+ * Chipone panel related driver
+ *
+ *
+ ----------------------------------------------------------------------*/
+/***********************************************************************************************
+Name : icn83xx_ts_wakeup
+Input : void
+Output : ret
+function : this function is used to wakeup tp
+ ***********************************************************************************************/
+void icn83xx_ts_wakeup(void)
+{
+ //#if def TOUCH_RESET_PIN
+
+}
+
+/***********************************************************************************************
+Name : icn83xx_ts_reset
+Input : void
+Output : ret
+function : this function is used to reset tp, you should not delete it
+ ***********************************************************************************************/
+void icn83xx_ts_reset(void)
+{
+ int rst = g_param.rstgpio;
+ gpio_direction_output(rst, 0);
+ //mdelay(30);
+ msleep(50);
+ gpio_direction_output(rst, 1);
+ //mdelay(50);
+ msleep(50);
+
+}
+
+/***********************************************************************************************
+Name : icn83xx_irq_disable
+Input : void
+Output : ret
+function : this function is used to disable irq
+ ***********************************************************************************************/
+void icn83xx_irq_disable(void)
+{
+ unsigned long irqflags;
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+
+ spin_lock_irqsave(&icn83xx_ts->irq_lock, irqflags);
+ if (!icn83xx_ts->irq_is_disable)
+ {
+ icn83xx_ts->irq_is_disable = 1;
+ wmt_gpio_mask_irq(g_param.irqgpio);
+ //disable_irq_nosync(icn83xx_ts->irq);
+ //disable_irq(icn83xx_ts->irq);
+ }
+ spin_unlock_irqrestore(&icn83xx_ts->irq_lock, irqflags);
+}
+
+/***********************************************************************************************
+Name : icn83xx_irq_enable
+Input : void
+Output : ret
+function : this function is used to enable irq
+ ***********************************************************************************************/
+void icn83xx_irq_enable(void)
+{
+ unsigned long irqflags = 0;
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+
+ spin_lock_irqsave(&icn83xx_ts->irq_lock, irqflags);
+ if (icn83xx_ts->irq_is_disable)
+ {
+ wmt_gpio_unmask_irq(g_param.irqgpio);
+ //enable_irq(icn83xx_ts->irq);
+ icn83xx_ts->irq_is_disable = 0;
+ }
+ spin_unlock_irqrestore(&icn83xx_ts->irq_lock, irqflags);
+
+}
+
+/***********************************************************************************************
+Name : icn83xx_prog_i2c_rxdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : read data from icn83xx, prog mode
+ ***********************************************************************************************/
+int icn83xx_prog_i2c_rxdata(unsigned short addr, char *rxdata, int length)
+{
+ int ret = -1;
+ int retries = 0;
+#if 0
+ struct i2c_msg msgs[] = {
+ {
+ .addr = ICN83XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ };
+
+ icn83xx_prog_i2c_txdata(addr, NULL, 0);
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msgs, 1);
+ if(ret == 1)break;
+ retries++;
+ }
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn83xx_error("%s i2c read error: %d\n", __func__, ret);
+ // icn83xx_ts_reset();
+ }
+#else
+ unsigned char tmp_buf[2];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = ICN83XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ {
+ .addr = ICN83XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ };
+ tmp_buf[0] = U16HIBYTE(addr);
+ tmp_buf[1] = U16LOBYTE(addr);
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn83xx_error("%s i2c read error: %d\n", __func__, ret);
+ // icn83xx_ts_reset();
+ }
+#endif
+ return ret;
+}
+/***********************************************************************************************
+Name : icn83xx_prog_i2c_txdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : send data to icn83xx , prog mode
+ ***********************************************************************************************/
+int icn83xx_prog_i2c_txdata(unsigned short addr, char *txdata, int length)
+{
+ int ret = -1;
+ char tmp_buf[128];
+ int retries = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = ICN83XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = 0,
+ .len = length + 2,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ };
+
+ if (length > 125)
+ {
+ icn83xx_error("%s too big datalen = %d!\n", __func__, length);
+ return -1;
+ }
+
+ tmp_buf[0] = U16HIBYTE(addr);
+ tmp_buf[1] = U16LOBYTE(addr);
+
+ if (length != 0 && txdata != NULL)
+ {
+ memcpy(&tmp_buf[2], txdata, length);
+ }
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msg, 1);
+ if(ret == 1)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn83xx_error("%s i2c write error: %d\n", __func__, ret);
+ // icn83xx_ts_reset();
+ }
+ return ret;
+}
+/***********************************************************************************************
+Name : icn83xx_prog_write_reg
+Input : addr -- address
+para -- parameter
+Output :
+function : write register of icn83xx, prog mode
+ ***********************************************************************************************/
+int icn83xx_prog_write_reg(unsigned short addr, char para)
+{
+ char buf[3];
+ int ret = -1;
+
+ buf[0] = para;
+ ret = icn83xx_prog_i2c_txdata(addr, buf, 1);
+ if (ret < 0) {
+ icn83xx_error("write reg failed! %#x ret: %d\n", buf[0], ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_prog_read_reg
+Input : addr
+pdata
+Output :
+function : read register of icn83xx, prog mode
+ ***********************************************************************************************/
+int icn83xx_prog_read_reg(unsigned short addr, char *pdata)
+{
+ int ret = -1;
+ ret = icn83xx_prog_i2c_rxdata(addr, pdata, 1);
+ return ret;
+}
+
+/***********************************************************************************************
+Name : icn83xx_i2c_rxdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : read data from icn83xx, normal mode
+ ***********************************************************************************************/
+int icn83xx_i2c_rxdata(unsigned char addr, char *rxdata, int length)
+{
+ int ret = -1;
+ int retries = 0;
+#if 0
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ };
+
+ icn83xx_i2c_txdata(addr, NULL, 0);
+ while(retries < IIC_RETRY_NUM)
+ {
+
+ ret = i2c_transfer(this_client->adapter, msgs, 1);
+ if(ret == 1)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn83xx_error("%s i2c read error: %d\n", __func__, ret);
+ // icn83xx_ts_reset();
+ }
+
+#else
+ unsigned char tmp_buf[1];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ };
+ tmp_buf[0] = addr;
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn83xx_error("%s i2c read error: %d\n", __func__, ret);
+ icn83xx_ts_reset();
+ }
+#endif
+
+ return ret;
+}
+/***********************************************************************************************
+Name : icn83xx_i2c_txdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : send data to icn83xx , normal mode
+ ***********************************************************************************************/
+int icn83xx_i2c_txdata(unsigned char addr, char *txdata, int length)
+{
+ int ret = -1;
+ unsigned char tmp_buf[128];
+ int retries = 0;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length + 1,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN83XX_I2C_SCL,
+#endif
+ },
+ };
+
+ if (length > 125)
+ {
+ icn83xx_error("%s too big datalen = %d!\n", __func__, length);
+ return -1;
+ }
+
+ tmp_buf[0] = addr;
+
+ if (length != 0 && txdata != NULL)
+ {
+ memcpy(&tmp_buf[1], txdata, length);
+ }
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msg, 1);
+ if(ret == 1)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn83xx_error("%s i2c write error: %d\n", __func__, ret);
+ icn83xx_ts_reset();
+ }
+
+ return ret;
+}
+
+/***********************************************************************************************
+Name : icn83xx_write_reg
+Input : addr -- address
+para -- parameter
+Output :
+function : write register of icn83xx, normal mode
+ ***********************************************************************************************/
+int icn83xx_write_reg(unsigned char addr, char para)
+{
+ char buf[3];
+ int ret = -1;
+
+ buf[0] = para;
+ ret = icn83xx_i2c_txdata(addr, buf, 1);
+ if (ret < 0) {
+ icn83xx_error("write reg failed! %#x ret: %d\n", buf[0], ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_read_reg
+Input : addr
+pdata
+Output :
+function : read register of icn83xx, normal mode
+ ***********************************************************************************************/
+int icn83xx_read_reg(unsigned char addr, char *pdata)
+{
+ int ret = -1;
+ ret = icn83xx_i2c_rxdata(addr, pdata, 1);
+ return ret;
+}
+
+#if SUPPORT_FW_UPDATE
+/***********************************************************************************************
+Name : icn83xx_log
+Input : 0: rawdata, 1: diff data
+Output : err type
+function : calibrate param
+ ***********************************************************************************************/
+void icn83xx_log(char diff)
+{
+ char row = 0;
+ char column = 0;
+ int i, j;
+ icn83xx_read_reg(160, &row);
+ icn83xx_read_reg(161, &column);
+
+ if(diff == 1)
+ {
+ icn83xx_readTP(row, column, (char *)&log_diffdata[0][0]);
+
+ for(i=0; i<row; i++)
+ {
+ for(j=0; j<column; j++)
+ {
+ log_diffdata[i][j] = log_diffdata[i][j] - log_rawdata[i][j];
+ }
+ }
+ icn83xx_rawdatadump(&log_diffdata[0][0], row*16, 16);
+ }
+ else
+ {
+ icn83xx_readTP(row, column, (char *)&log_rawdata[0][0]);
+ icn83xx_rawdatadump(&log_rawdata[0][0], row*16, 16);
+ }
+}
+#endif
+
+/***********************************************************************************************
+Name : icn83xx_iic_test
+Input : void
+Output :
+function : 0 success,
+ ***********************************************************************************************/
+static int icn83xx_iic_test(void)
+{
+ int ret = -1;
+ char value = 0;
+ int retry = 0;
+ while(retry++ < 3)
+ {
+ ret = icn83xx_read_reg(0, &value);
+ if(ret > 0)
+ {
+ return ret;
+ }
+ icn83xx_error("iic test error! %d\n", retry);
+ msleep(3);
+ }
+ return ret;
+}
+
+/***********************************************************************************************
+Name : icn83xx_report_value_B
+Input : void
+Output :
+function : reprot touch ponit
+ ***********************************************************************************************/
+#if CTP_REPORT_PROTOCOL
+static int icn83xx_report_value_B(void)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ char buf[POINT_NUM*POINT_SIZE+3]={0};
+ static unsigned char finger_last[POINT_NUM + 1]={0};
+ unsigned char finger_current[POINT_NUM + 1] = {0};
+ unsigned int position = 0;
+ int temp = 0;
+ int ret = -1;
+ int x,y;
+ icn83xx_info("==icn83xx_report_value_B ==\n");
+ // icn83xx_trace("==icn83xx_report_value_B ==\n");
+ ret = icn83xx_i2c_rxdata(16, buf, POINT_NUM*POINT_SIZE+2);
+ if (ret < 0) {
+ icn83xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ icn83xx_ts->point_num = buf[1];
+ if (icn83xx_ts->point_num > 5) {
+ printk("error point_num : %d\n",icn83xx_ts->point_num);
+ return -1;
+ }
+ if(icn83xx_ts->point_num > 0)
+ {
+ for(position = 0; position<icn83xx_ts->point_num; position++)
+ {
+ temp = buf[2 + POINT_SIZE*position] + 1;
+ finger_current[temp] = 1;
+ icn83xx_ts->point_info[temp].u8ID = buf[2 + POINT_SIZE*position];
+ icn83xx_ts->point_info[temp].u16PosX = (buf[3 + POINT_SIZE*position]<<8) + buf[4 + POINT_SIZE*position];
+ icn83xx_ts->point_info[temp].u16PosY = (buf[5 + POINT_SIZE*position]<<8) + buf[6 + POINT_SIZE*position];
+ icn83xx_ts->point_info[temp].u8Pressure = buf[7 + POINT_SIZE*position];
+ icn83xx_ts->point_info[temp].u8EventId = buf[8 + POINT_SIZE*position];
+
+ if(icn83xx_ts->point_info[temp].u8EventId == 4)
+ finger_current[temp] = 0;
+
+ if(1 == icn83xx_ts->revert_x_flag)
+ {
+ icn83xx_ts->point_info[temp].u16PosX = icn83xx_ts->screen_max_x- icn83xx_ts->point_info[temp].u16PosX;
+ }
+ if(1 == icn83xx_ts->revert_y_flag)
+ {
+ icn83xx_ts->point_info[temp].u16PosY = icn83xx_ts->screen_max_y- icn83xx_ts->point_info[temp].u16PosY;
+ }
+ icn83xx_info("temp %d\n", temp);
+ icn83xx_info("u8ID %d\n", icn83xx_ts->point_info[temp].u8ID);
+ icn83xx_info("u16PosX %d\n", icn83xx_ts->point_info[temp].u16PosX);
+ icn83xx_info("u16PosY %d\n", icn83xx_ts->point_info[temp].u16PosY);
+ icn83xx_info("u8Pressure %d\n", icn83xx_ts->point_info[temp].u8Pressure);
+ icn83xx_info("u8EventId %d\n", icn83xx_ts->point_info[temp].u8EventId);
+ //icn83xx_info("u8Pressure %d\n", icn83xx_ts->point_info[temp].u8Pressure*16);
+ }
+ }
+ else
+ {
+ for(position = 1; position < POINT_NUM+1; position++)
+ {
+ finger_current[position] = 0;
+ }
+ icn83xx_info("no touch\n");
+ }
+
+ for(position = 1; position < POINT_NUM + 1; position++)
+ {
+ if((finger_current[position] == 0) && (finger_last[position] != 0))
+ {
+ input_mt_slot(icn83xx_ts->input_dev, position-1);
+ input_mt_report_slot_state(icn83xx_ts->input_dev, MT_TOOL_FINGER, false);
+ icn83xx_point_info("one touch up: %d\n", position);
+ }
+ else if(finger_current[position])
+ {
+ if (g_param.xyswap == 0)
+ {
+ x = icn83xx_ts->point_info[position].u16PosX;
+ y = icn83xx_ts->point_info[position].u16PosY;
+ } else {
+ y = icn83xx_ts->point_info[position].u16PosX;
+ x = icn83xx_ts->point_info[position].u16PosY;
+ }
+ if (g_param.xdir == -1)
+ {
+ x = g_param.panelres_x - x;
+ }
+ if (g_param.ydir == -1)
+ {
+ y = g_param.panelres_y - y;
+ }
+
+ if (g_param.lcd_exchg) {
+ int tmp;
+ tmp = x;
+ x = y;
+ y = g_param.panelres_x - tmp;
+ }
+
+ input_mt_slot(icn83xx_ts->input_dev, position-1);
+ input_mt_report_slot_state(icn83xx_ts->input_dev, MT_TOOL_FINGER, true);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 1);
+ //input_report_abs(icn83xx_ts->input_dev, ABS_MT_PRESSURE, icn83xx_ts->point_info[position].u8Pressure);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_PRESSURE, 200);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_Y, y);
+ icn83xx_point_info("===position: %d, x = %d,y = %d, press = %d ====\n", position, icn83xx_ts->point_info[position].u16PosX,icn83xx_ts->point_info[position].u16PosY, icn83xx_ts->point_info[position].u8Pressure);
+ // icn83xx_trace("===position: %d, x = %d,y = %d, press = %d ====\n", position, icn83xx_ts->point_info[position].u16PosX,icn83xx_ts->point_info[position].u16PosY, icn83xx_ts->point_info[position].u8Pressure);
+ dbg("raw%d(%d,%d), rpt%d(%d,%d)\n", position, icn83xx_ts->point_info[position].u16PosX, icn83xx_ts->point_info[position].u16PosY, position, x, y);
+ }
+
+ }
+ input_sync(icn83xx_ts->input_dev);
+
+ for(position = 1; position < POINT_NUM + 1; position++)
+ {
+ finger_last[position] = finger_current[position];
+ }
+ return 0;
+}
+
+#else
+
+/***********************************************************************************************
+Name : icn83xx_ts_release
+Input : void
+Output :
+function : touch release
+ ***********************************************************************************************/
+static void icn83xx_ts_release(void)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ icn83xx_info("==icn83xx_ts_release ==\n");
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_sync(icn83xx_ts->input_dev);
+}
+
+/***********************************************************************************************
+Name : icn83xx_report_value_A
+Input : void
+Output :
+function : reprot touch ponit
+ ***********************************************************************************************/
+static int icn83xx_report_value_A(void)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ char buf[POINT_NUM*POINT_SIZE+3]={0};
+ int ret = -1;
+ int i;
+#if TOUCH_VIRTUAL_KEYS
+ unsigned char button;
+ static unsigned char button_last;
+#endif
+ icn83xx_info("==icn83xx_report_value_A ==\n");
+
+ ret = icn83xx_i2c_rxdata(16, buf, POINT_NUM*POINT_SIZE+2);
+ if (ret < 0) {
+ icn83xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ return ret;
+ }
+#if TOUCH_VIRTUAL_KEYS
+ button = buf[0];
+ icn83xx_info("%s: button=%d\n",__func__, button);
+
+ if((button_last != 0) && (button == 0))
+ {
+ icn83xx_ts_release();
+ button_last = button;
+ return 1;
+ }
+ if(button != 0)
+ {
+ switch(button)
+ {
+ case ICN_VIRTUAL_BUTTON_HOME:
+ icn83xx_info("ICN_VIRTUAL_BUTTON_HOME down\n");
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 200);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_X, 280);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_Y, 1030);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn83xx_ts->input_dev);
+ input_sync(icn83xx_ts->input_dev);
+ break;
+ case ICN_VIRTUAL_BUTTON_BACK:
+ icn83xx_info("ICN_VIRTUAL_BUTTON_BACK down\n");
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 200);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_X, 470);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_Y, 1030);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn83xx_ts->input_dev);
+ input_sync(icn83xx_ts->input_dev);
+ break;
+ case ICN_VIRTUAL_BUTTON_MENU:
+ icn83xx_info("ICN_VIRTUAL_BUTTON_MENU down\n");
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 200);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_X, 100);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_Y, 1030);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn83xx_ts->input_dev);
+ input_sync(icn83xx_ts->input_dev);
+ break;
+ default:
+ icn83xx_info("other gesture\n");
+ break;
+ }
+ button_last = button;
+ return 1;
+ }
+#endif
+
+ icn83xx_ts->point_num = buf[1];
+ if (icn83xx_ts->point_num == 0) {
+ icn83xx_ts_release();
+ return 1;
+ }
+ for(i=0;i<icn83xx_ts->point_num;i++){
+ if(buf[8 + POINT_SIZE*i] != 4) break ;
+ }
+
+ if(i == icn83xx_ts->point_num) {
+ icn83xx_ts_release();
+ return 1;
+ }
+
+ for(i=0; i<icn83xx_ts->point_num; i++)
+ {
+ icn83xx_ts->point_info[i].u8ID = buf[2 + POINT_SIZE*i];
+ icn83xx_ts->point_info[i].u16PosX = (buf[3 + POINT_SIZE*i]<<8) + buf[4 + POINT_SIZE*i];
+ icn83xx_ts->point_info[i].u16PosY = (buf[5 + POINT_SIZE*i]<<8) + buf[6 + POINT_SIZE*i];
+ icn83xx_ts->point_info[i].u8Pressure = 200;//buf[7 + POINT_SIZE*i];
+ icn83xx_ts->point_info[i].u8EventId = buf[8 + POINT_SIZE*i];
+
+ if(1 == icn83xx_ts->revert_x_flag)
+ {
+ icn83xx_ts->point_info[i].u16PosX = icn83xx_ts->screen_max_x- icn83xx_ts->point_info[i].u16PosX;
+ }
+ if(1 == icn83xx_ts->revert_y_flag)
+ {
+ icn83xx_ts->point_info[i].u16PosY = icn83xx_ts->screen_max_y- icn83xx_ts->point_info[i].u16PosY;
+ }
+
+ icn83xx_info("u8ID %d\n", icn83xx_ts->point_info[i].u8ID);
+ icn83xx_info("u16PosX %d\n", icn83xx_ts->point_info[i].u16PosX);
+ icn83xx_info("u16PosY %d\n", icn83xx_ts->point_info[i].u16PosY);
+ icn83xx_info("u8Pressure %d\n", icn83xx_ts->point_info[i].u8Pressure);
+ icn83xx_info("u8EventId %d\n", icn83xx_ts->point_info[i].u8EventId);
+
+
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_TRACKING_ID, icn83xx_ts->point_info[i].u8ID);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, icn83xx_ts->point_info[i].u8Pressure);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_X, icn83xx_ts->point_info[i].u16PosX);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_POSITION_Y, icn83xx_ts->point_info[i].u16PosY);
+ input_report_abs(icn83xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn83xx_ts->input_dev);
+ icn83xx_point_info("point: %d ===x = %d,y = %d, press = %d ====\n",i, icn83xx_ts->point_info[i].u16PosX,icn83xx_ts->point_info[i].u16PosY, icn83xx_ts->point_info[i].u8Pressure);
+ }
+
+ input_sync(icn83xx_ts->input_dev);
+ return 0;
+}
+#endif
+
+/***********************************************************************************************
+Name : icn83xx_ts_pen_irq_work
+Input : void
+Output :
+function : work_struct
+ ***********************************************************************************************/
+static void icn83xx_ts_pen_irq_work(struct work_struct *work)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+#if SUPPORT_PROC_FS
+ if(down_interruptible(&icn83xx_ts->sem))
+ {
+ return;
+ }
+#endif
+
+ if(icn83xx_ts->work_mode == 0)
+ {
+#if CTP_REPORT_PROTOCOL
+ icn83xx_report_value_B();
+#else
+ icn83xx_report_value_A();
+#endif
+
+ }
+#if SUPPORT_FW_UPDATE
+ else if(icn83xx_ts->work_mode == 1)
+ {
+ printk("log raw data\n");
+ icn83xx_log(0); //raw data
+ }
+ else if(icn83xx_ts->work_mode == 2)
+ {
+ printk("log diff data\n");
+ icn83xx_log(1); //diff data
+ }
+#endif
+
+#if SUPPORT_PROC_FS
+ up(&icn83xx_ts->sem);
+#endif
+ wmt_gpio_unmask_irq(g_param.irqgpio);
+
+}
+/***********************************************************************************************
+Name : chipone_timer_func
+Input : void
+Output :
+function : Timer interrupt service routine.
+ ***********************************************************************************************/
+static enum hrtimer_restart chipone_timer_func(struct hrtimer *timer)
+{
+ struct icn83xx_ts_data *icn83xx_ts = container_of(timer, struct icn83xx_ts_data, timer);
+ queue_work(icn83xx_ts->ts_workqueue, &icn83xx_ts->pen_event_work);
+
+ if(icn83xx_ts->use_irq == 1)
+ {
+ if((icn83xx_ts->work_mode == 1) || (icn83xx_ts->work_mode == 2))
+ {
+ hrtimer_start(&icn83xx_ts->timer, ktime_set(CTP_POLL_TIMER/1000, (CTP_POLL_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ }
+ else
+ {
+ hrtimer_start(&icn83xx_ts->timer, ktime_set(CTP_POLL_TIMER/1000, (CTP_POLL_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ return HRTIMER_NORESTART;
+}
+/***********************************************************************************************
+Name : icn83xx_ts_interrupt
+Input : void
+Output :
+function : interrupt service routine
+ ***********************************************************************************************/
+static irqreturn_t icn83xx_ts_interrupt(int irq, void *dev_id)
+{
+ struct icn83xx_ts_data *icn83xx_ts = dev_id;
+ int irqindex = g_param.irqgpio;
+
+ icn83xx_info("==========------icn83xx_ts TS Interrupt-----============\n");
+ if (gpio_irqstatus(irqindex)) {
+ wmt_gpio_ack_irq(irqindex);
+ if (is_gpio_irqenable(irqindex) && l_suspend == 0) {
+ wmt_gpio_mask_irq(irqindex);
+ if(icn83xx_ts->work_mode != 0) {
+ wmt_gpio_unmask_irq(irqindex);
+ return IRQ_HANDLED;
+ }
+ //icn83xx_irq_disable();
+ if (!work_pending(&icn83xx_ts->pen_event_work)) {
+ //icn83xx_info("Enter work\n");
+ queue_work(icn83xx_ts->ts_workqueue, &icn83xx_ts->pen_event_work);
+ }
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/***********************************************************************************************
+Name : icn83xx_ts_suspend
+Input : void
+Output :
+function : tp enter sleep mode
+ ***********************************************************************************************/
+static void icn83xx_ts_early_suspend(struct early_suspend *handler)
+{
+ int retry = 0;
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ icn83xx_trace("icn83xx_ts_suspend: write ICN83XX_REG_PMODE .\n");
+ if (icn83xx_ts->use_irq)
+ {
+ icn83xx_irq_disable();
+ icn83xx_trace("icn83xx_ts_suspend:disable irq .\n");
+ }
+ else
+ {
+ hrtimer_cancel(&icn83xx_ts->timer);
+ }
+ for(retry = 0;retry <3; retry++ )
+ {
+ icn83xx_write_reg(ICN83XX_REG_PMODE, PMODE_HIBERNATE);
+ }
+}
+
+/***********************************************************************************************
+Name : icn83xx_ts_resume
+Input : void
+Output :
+function : wakeup tp or reset tp
+ ***********************************************************************************************/
+static void icn83xx_ts_late_resume(struct early_suspend *handler)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ int i;
+ printk("==icn83xx_ts_resume== \n");
+ // icn83xx_ts_reset();
+ //report touch release
+#if CTP_REPORT_PROTOCOL
+ for(i = 0; i < POINT_NUM; i++)
+ {
+ input_mt_slot(icn83xx_ts->input_dev, i);
+ input_mt_report_slot_state(icn83xx_ts->input_dev, MT_TOOL_FINGER, false);
+ }
+#else
+ icn83xx_ts_release();
+#endif
+ icn83xx_ts_wakeup();
+ icn83xx_ts_reset();
+ if (icn83xx_ts->use_irq)
+ {
+ printk("icn83xx_irq_enable\n");
+ icn83xx_irq_enable();
+ }
+ else
+ { printk("icn83xx_ts_resume hrtimer_start\n");
+ hrtimer_start(&icn83xx_ts->timer, ktime_set(CTP_START_TIMER/1000, (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+
+}
+#endif
+
+#ifdef CONFIG_PM
+/***********************************************************************************************
+Name : icn83xx_ts_suspend
+Input : void
+Output :
+function : tp enter sleep mode
+ ***********************************************************************************************/
+static int icn83xx_ts_suspend(struct device *pdev)
+{
+ //int retry = 0;
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ icn83xx_trace("icn83xx_ts_suspend: write ICN83XX_REG_PMODE .\n");
+ if (icn83xx_ts->use_irq)
+ {
+ icn83xx_irq_disable();
+ icn83xx_trace("icn83xx_ts_suspend:disable irq .\n");
+ }
+ else
+ {
+ hrtimer_cancel(&icn83xx_ts->timer);
+ }
+ /*for(retry = 0;retry <3; retry++ )
+ {
+ icn83xx_write_reg(ICN83XX_REG_PMODE, PMODE_HIBERNATE);
+ } */
+ l_suspend = 1;
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_ts_resume
+Input : void
+Output :
+function : wakeup tp or reset tp
+ ***********************************************************************************************/
+static int icn83xx_ts_resume(struct device *pdev)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(this_client);
+ int i;
+ printk("==icn83xx_ts_resume== \n");
+ // icn83xx_ts_reset();
+ //report touch release
+#if CTP_REPORT_PROTOCOL
+ for(i = 0; i < POINT_NUM; i++)
+ {
+ input_mt_slot(icn83xx_ts->input_dev, i);
+ input_mt_report_slot_state(icn83xx_ts->input_dev, MT_TOOL_FINGER, false);
+ }
+#else
+ icn83xx_ts_release();
+#endif
+ //icn83xx_ts_wakeup();
+ icn83xx_ts_reset();
+ l_suspend = 0;
+ if (icn83xx_ts->use_irq)
+ {
+ printk("icn83xx_irq_enable\n");
+ icn83xx_irq_enable();
+ }
+ else
+ { printk("icn83xx_ts_resume hrtimer_start\n");
+ hrtimer_start(&icn83xx_ts->timer, ktime_set(CTP_START_TIMER/1000, (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ return 0;
+}
+#else
+#define icn83xx_ts_suspend NULL
+#define icn83xx_ts_resume NULL
+#endif
+
+/***********************************************************************************************
+Name : icn83xx_request_io_port
+Input : void
+Output :
+function : 0 success,
+ ***********************************************************************************************/
+static int icn83xx_request_io_port(struct icn83xx_ts_data *icn83xx_ts)
+{
+#if SUPPORT_ROCKCHIP
+ icn83xx_ts->screen_max_x = SCREEN_MAX_X;
+ icn83xx_ts->screen_max_y = SCREEN_MAX_Y;
+ icn83xx_ts->irq = CTP_IRQ_PORT;
+#endif
+ icn83xx_ts->irq = IRQ_GPIO;
+
+ if (gpio_request(g_param.rstgpio, "ts_rst") < 0) {
+ printk("gpio(%d) touchscreen reset request fail\n", g_param.rstgpio);
+ return -EIO;
+ }
+ gpio_direction_output(g_param.rstgpio, 1);
+
+ if (gpio_request(g_param.irqgpio, "ts_irq") < 0) {
+ printk("gpio(%d) touchscreen interrupt request fail\n", g_param.irqgpio);
+ gpio_free(g_param.rstgpio);
+ return -EIO;
+ }
+ wmt_gpio_setpull(g_param.irqgpio, WMT_GPIO_PULL_UP);
+ gpio_direction_input(g_param.irqgpio);
+
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn83xx_free_io_port
+Input : void
+Output :
+function : 0 success,
+ ***********************************************************************************************/
+static void icn83xx_free_io_port(void)
+{
+ gpio_free(g_param.rstgpio);
+ gpio_free(g_param.irqgpio);
+}
+
+/***********************************************************************************************
+Name : icn83xx_request_irq
+Input : void
+Output :
+function : 0 success,
+ ***********************************************************************************************/
+static int icn83xx_request_irq(struct icn83xx_ts_data *icn83xx_ts)
+{
+ int err = -1;
+
+ /*err = gpio_request(icn83xx_ts->irq, "TS_INT"); //Request IO
+ if (err < 0)
+ {
+ icn83xx_error("Failed to request GPIO:%d, ERRNO:%d\n", (int)icn83xx_ts->irq, err);
+ return err;
+ }
+ gpio_direction_input(icn83xx_ts->irq);*/
+
+ wmt_gpio_set_irq_type(g_param.irqgpio, IRQ_TYPE_EDGE_FALLING);
+ err = request_irq(icn83xx_ts->irq, icn83xx_ts_interrupt, IRQF_SHARED, "icn83xx_ts", icn83xx_ts);
+ if (err < 0)
+ {
+ icn83xx_error("icn83xx_ts_probe: request irq failed\n");
+ return err;
+ }
+ else
+ {
+ icn83xx_irq_disable();
+ icn83xx_ts->use_irq = 1;
+ }
+
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn83xx_free_irq
+Input : void
+Output :
+function : 0 success,
+ ***********************************************************************************************/
+static void icn83xx_free_irq(struct icn83xx_ts_data *icn83xx_ts)
+{
+ if (icn83xx_ts)
+ {
+ if (icn83xx_ts->use_irq)
+ {
+ free_irq(icn83xx_ts->irq, icn83xx_ts);
+ }
+ else
+ {
+ hrtimer_cancel(&icn83xx_ts->timer);
+ }
+ }
+}
+
+/***********************************************************************************************
+Name : icn83xx_request_input_dev
+Input : void
+Output :
+function : 0 success,
+ ***********************************************************************************************/
+static int icn83xx_request_input_dev(struct icn83xx_ts_data *icn83xx_ts)
+{
+ int ret = -1;
+ struct input_dev *input_dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ icn83xx_error("failed to allocate input device\n");
+ return -ENOMEM;
+ }
+ icn83xx_ts->input_dev = input_dev;
+
+ icn83xx_ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
+#if CTP_REPORT_PROTOCOL
+ __set_bit(INPUT_PROP_DIRECT, icn83xx_ts->input_dev->propbit);
+ input_mt_init_slots(icn83xx_ts->input_dev, 255);
+#else
+ set_bit(ABS_MT_TOUCH_MAJOR, icn83xx_ts->input_dev->absbit);
+ set_bit(ABS_MT_POSITION_X, icn83xx_ts->input_dev->absbit);
+ set_bit(ABS_MT_POSITION_Y, icn83xx_ts->input_dev->absbit);
+ set_bit(ABS_MT_WIDTH_MAJOR, icn83xx_ts->input_dev->absbit);
+#endif
+ if (g_param.lcd_exchg) {
+ input_set_abs_params(icn83xx_ts->input_dev, ABS_MT_POSITION_X, 0, g_param.panelres_y, 0, 0);
+ input_set_abs_params(icn83xx_ts->input_dev, ABS_MT_POSITION_Y, 0, g_param.panelres_x, 0, 0);
+ } else {
+ input_set_abs_params(icn83xx_ts->input_dev, ABS_MT_POSITION_X, 0, g_param.panelres_x, 0, 0);
+ input_set_abs_params(icn83xx_ts->input_dev, ABS_MT_POSITION_Y, 0, g_param.panelres_y, 0, 0);
+ }
+ input_set_abs_params(icn83xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(icn83xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(icn83xx_ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);
+
+ __set_bit(KEY_MENU, input_dev->keybit);
+ __set_bit(KEY_BACK, input_dev->keybit);
+ __set_bit(KEY_HOME, input_dev->keybit);
+ __set_bit(KEY_SEARCH, input_dev->keybit);
+
+ input_dev->name = CTP_NAME;
+ ret = input_register_device(input_dev);
+ if (ret) {
+ icn83xx_error("Register %s input device failed\n", input_dev->name);
+ input_free_device(input_dev);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ icn83xx_trace("==register_early_suspend =\n");
+ icn83xx_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ icn83xx_ts->early_suspend.suspend = icn83xx_ts_early_suspend;
+ icn83xx_ts->early_suspend.resume = icn83xx_ts_late_resume;
+ register_early_suspend(&icn83xx_ts->early_suspend);
+#endif
+
+ return 0;
+}
+
+#if SUPPORT_DELAYED_WORK
+static void icn_delayedwork_fun(struct work_struct *work)
+{
+ int retry;
+ short fwVersion = 0;
+ short curVersion = 0;
+ icn83xx_trace("====%s begin1111=====. \n", __func__);
+
+#if SUPPORT_FW_UPDATE
+ fwVersion = icn83xx_read_fw_Ver(firmware);
+ curVersion = icn83xx_readVersion();
+ icn83xx_trace("fwVersion : 0x%x\n", fwVersion);
+ icn83xx_trace("current version: 0x%x\n", curVersion);
+
+
+#if FORCE_UPDATA_FW
+ retry = 5;
+ while(retry > 0)
+ {
+ if(R_OK == icn83xx_fw_update(firmware))
+ {
+ break;
+ }
+ retry--;
+ icn83xx_error("icn83xx_fw_update failed.\n");
+ }
+#else
+ if(fwVersion > curVersion)
+ {
+ retry = 5;
+ while(retry > 0)
+ {
+ if(R_OK == icn83xx_fw_update(firmware))
+ {
+ break;
+ }
+ retry--;
+ icn83xx_error("icn83xx_fw_update failed.\n");
+ }
+ }
+#endif
+
+#endif
+
+
+ icn83xx_irq_enable();
+ icn83xx_trace("====%s over1111=====. \n", __func__);
+}
+#endif
+
+
+char FbCap[4][16]={
+ {0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14},
+ {0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12},
+ {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10},
+ {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08},
+};
+
+static int wmt_wakeup_bl_notify(struct notifier_block *nb, unsigned long event,
+ void *dummy)
+{
+ //printk("get notify\n");
+ switch (event) {
+ case BL_CLOSE:
+ l_suspend = 1;
+ //printk("\nclose backlight\n\n");
+ //printk("disable irq\n\n");
+ wmt_gpio_mask_irq(g_param.irqgpio);
+ break;
+ case BL_OPEN:
+ l_suspend = 0;
+ //printk("\nopen backlight\n\n");
+ //printk("enable irq\n\n");
+ wmt_gpio_unmask_irq(g_param.irqgpio);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wmt_bl_notify = {
+ .notifier_call = wmt_wakeup_bl_notify,
+};
+
+static int icn83xx_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct icn83xx_ts_data *icn83xx_ts;
+ short fwVersion = 0;
+ short curVersion = 0;
+ //int average;
+ int err = 0;
+ //char value;
+ int retry;
+
+ icn83xx_trace("====%s begin=====. \n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ {
+ icn83xx_error("I2C check functionality failed.\n");
+ return -ENODEV;
+ }
+
+ icn83xx_ts = kzalloc(sizeof(*icn83xx_ts), GFP_KERNEL);
+ if (!icn83xx_ts)
+ {
+ icn83xx_error("Alloc icn83xx_ts memory failed.\n");
+ return -ENOMEM;
+ }
+
+ this_client = client;
+ i2c_set_clientdata(client, icn83xx_ts);
+
+ icn83xx_ts->work_mode = 0;
+ spin_lock_init(&icn83xx_ts->irq_lock);
+ // icn83xx_ts->irq_lock = SPIN_LOCK_UNLOCKED;
+
+ err = icn83xx_request_io_port(icn83xx_ts);
+ if (err != 0) {
+ icn83xx_error("icn83xx_request_io_port failed.\n");
+ goto fail1;
+ }
+
+ memset(firmware, 0, 128);
+ sprintf(firmware,"/system/etc/firmware/%s.bin",g_param.fw_name);
+
+ icn83xx_ts_reset();
+ err = icn83xx_iic_test();
+ if (err < 0)
+ {
+ icn83xx_error("icn83xx_iic_test failed.\n");
+#if SUPPORT_FW_UPDATE
+
+#if COMPILE_FW_WITH_DRIVER
+ icn83xx_set_fw(sizeof(icn83xx_fw), &icn83xx_fw[0]);
+#endif
+ if(icn83xx_check_progmod() == 0)
+ {
+
+ retry = 5;
+ icn83xx_trace("in prog mode\n");
+ while(retry > 0)
+ {
+ if(R_OK == icn83xx_fw_update(firmware))
+ {
+ break;
+ }
+ retry--;
+ icn83xx_error("icn83xx_fw_update failed.\n");
+ }
+ }
+ else //
+ {
+ icn83xx_error("I2C communication failed.\n");
+ err = -1;
+ goto fail2;
+ }
+
+#endif
+ }
+ else
+ {
+ icn83xx_trace("iic communication ok\n");
+ }
+
+#if SUPPORT_FW_UPDATE
+ fwVersion = icn83xx_read_fw_Ver(firmware);
+ curVersion = icn83xx_readVersion();
+ icn83xx_trace("fwVersion : 0x%x\n", fwVersion);
+ icn83xx_trace("current version: 0x%x\n", curVersion);
+
+ if (g_param.force_download) {
+ retry = 5;
+ while(retry > 0)
+ {
+ if(R_OK == icn83xx_fw_update(firmware))
+ {
+ break;
+ }
+ retry--;
+ icn83xx_error("icn83xx_fw_update failed.\n");
+ }
+ } else {
+ if(fwVersion > curVersion)
+ {
+ retry = 5;
+ while(retry > 0)
+ {
+ if(R_OK == icn83xx_fw_update(firmware))
+ {
+ break;
+ }
+ retry--;
+ icn83xx_error("icn83xx_fw_update failed.\n");
+ }
+ }
+ }
+
+#endif
+
+#if SUPPORT_FW_CALIB
+ err = icn83xx_read_reg(0, &value);
+ if(err > 0)
+ {
+ //auto calib fw
+ average = icn83xx_calib(0, NULL);
+ //fix FbCap
+ // average = icn83xx_calib(0, FbCap[1]);
+ icn83xx_trace("average : %d\n", average);
+ icn83xx_setPeakGroup(250, 150);
+ icn83xx_setDownUp(400, 300);
+ }
+#endif
+
+ INIT_WORK(&icn83xx_ts->pen_event_work, icn83xx_ts_pen_irq_work);
+ icn83xx_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));
+ if (!icn83xx_ts->ts_workqueue) {
+ icn83xx_error("create_singlethread_workqueue failed.\n");
+ err = -ESRCH;
+ goto fail3;
+ }
+
+ err= icn83xx_request_input_dev(icn83xx_ts);
+ if (err < 0)
+ {
+ icn83xx_error("request input dev failed\n");
+ goto fail4;
+ }
+
+#if TOUCH_VIRTUAL_KEYS
+ icn83xx_ts_virtual_keys_init();
+#endif
+ err = icn83xx_request_irq(icn83xx_ts);
+ if (err != 0)
+ {
+ printk("request irq error, use timer\n");
+ icn83xx_ts->use_irq = 0;
+ hrtimer_init(&icn83xx_ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ icn83xx_ts->timer.function = chipone_timer_func;
+ hrtimer_start(&icn83xx_ts->timer, ktime_set(CTP_START_TIMER/1000, (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+#if SUPPORT_SYSFS
+ icn83xx_create_sysfs(client);
+#endif
+
+#if SUPPORT_PROC_FS
+ sema_init(&icn83xx_ts->sem, 1);
+ init_proc_node();
+#endif
+
+ err = device_create_file(&(client->dev), &dev_attr_dbg);
+ if (err) {
+ printk("Can't create attr file");
+ }
+ if (g_param.earlysus_en)
+ register_bl_notifier(&wmt_bl_notify);
+
+#if SUPPORT_DELAYED_WORK
+ INIT_DELAYED_WORK(&icn83xx_ts->icn_delayed_work, icn_delayedwork_fun);
+ schedule_delayed_work(&icn83xx_ts->icn_delayed_work, msecs_to_jiffies(8000));
+#else
+
+ icn83xx_irq_enable();
+#endif
+ icn83xx_trace("==%s over =\n", __func__);
+ return 0;
+
+fail4:
+ input_unregister_device(icn83xx_ts->input_dev);
+ input_free_device(icn83xx_ts->input_dev);
+fail3:
+ cancel_work_sync(&icn83xx_ts->pen_event_work);
+fail2:
+ icn83xx_free_io_port();
+fail1:
+ kfree(icn83xx_ts);
+ return err;
+}
+
+static int __devexit icn83xx_ts_remove(struct i2c_client *client)
+{
+ struct icn83xx_ts_data *icn83xx_ts = i2c_get_clientdata(client);
+ icn83xx_trace("==icn83xx_ts_remove=\n");
+ icn83xx_irq_disable();
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&icn83xx_ts->early_suspend);
+#endif
+
+ if (g_param.earlysus_en)
+ unregister_bl_notifier(&wmt_bl_notify);
+
+#if SUPPORT_PROC_FS
+ uninit_proc_node();
+#endif
+
+ input_unregister_device(icn83xx_ts->input_dev);
+ input_free_device(icn83xx_ts->input_dev);
+ cancel_work_sync(&icn83xx_ts->pen_event_work);
+ destroy_workqueue(icn83xx_ts->ts_workqueue);
+ icn83xx_free_irq(icn83xx_ts);
+ icn83xx_free_io_port();
+ kfree(icn83xx_ts);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 96;
+ char retval[200] = {0},*p=NULL,*s=NULL;
+ int Enable=0;
+
+ // Get u-boot parameter
+ /*ret = wmt_getsyspara("wmt.io.zettouch", retval, &len);
+ if(ret){
+ klog("Read wmt.io.zettouch Failed.\n");
+ } else
+ goto paste;*/
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ printk("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+
+//paste:
+ p = retval;
+ Enable = (p[0] - '0' == 1) ? 1 : 0;
+ if(Enable == 0){
+ printk("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(p,':');p++;
+ s = strchr(p,':');
+ strncpy(g_param.fw_name,p, (s-p));
+ printk("ts_name=%s\n", g_param.fw_name);
+ if (strncmp(g_param.fw_name, "ICN83", 5)) {
+ printk("Wrong firmware name.\n");
+ return -ENODEV;
+ }
+
+ p = s+1;
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d",
+ &(g_param.irqgpio),&(g_param.panelres_x),&(g_param.panelres_y),&(g_param.rstgpio),
+ &(g_param.xyswap),&(g_param.xdir),&(g_param.ydir),&(g_param.force_download));
+
+ if (ret < 8) {
+ printk("Wrong format ts u-boot param(%d)!\nwmt.io.touch=%s\n",ret,retval);
+ return -ENODEV;
+ }
+
+ printk("p.x = %d, p.y = %d, irqgpio=%d, rstgpio=%d,xyswap=%d,xdir=%d,ydir=%d,force_download=%d\n",
+ g_param.panelres_x,g_param.panelres_y,g_param.irqgpio,g_param.rstgpio,
+ g_param.xyswap,g_param.xdir,g_param.ydir,g_param.force_download);
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.touch.earlysus", retval, &len);
+ if(!ret)
+ g_param.earlysus_en = (retval[0] - '0' == 1) ? 1 : 0;
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ g_param.lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops icn83xx_pm_ops = {
+ .suspend = icn83xx_ts_suspend,
+ .resume = icn83xx_ts_resume,
+};
+
+static const struct i2c_device_id icn83xx_ts_id[] = {
+ { CTP_NAME, 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, icn83xx_ts_id);
+
+static struct i2c_driver icn83xx_ts_driver = {
+ .driver = {
+ .name = CTP_NAME,
+ .pm = &icn83xx_pm_ops,
+ },
+ .probe = icn83xx_ts_probe,
+ .remove = __devexit_p(icn83xx_ts_remove),
+ .id_table = icn83xx_ts_id,
+};
+
+static struct i2c_board_info i2c_board_info = {
+ I2C_BOARD_INFO(CTP_NAME, ICN83XX_IIC_ADDR),
+};
+
+static int __init icn83xx_ts_init(void)
+{
+ struct i2c_client *client;
+ struct i2c_adapter *adap;
+ //u8 ts_data[8];
+
+ icn83xx_trace("===========================%s=====================\n", __func__);
+ if(wmt_check_touch_env())
+ return -ENODEV;
+ {//register i2c device
+ adap = i2c_get_adapter(1); //i2c Bus 1
+ if (!adap)
+ return -ENODEV;
+ client = i2c_new_device(adap, &i2c_board_info);
+ i2c_put_adapter(adap);
+ if (!client) {
+ printk("i2c_new_device error\n");
+ return -ENODEV;
+ }
+ }
+ /*{ //check if IC exists
+ if (i2c_read_tsdata(client, ts_data, 8) <= 0) {
+ errlog("Can't find IC!\n");
+ i2c_unregister_device(client);
+ return -ENODEV;
+ }
+ }*/
+ return i2c_add_driver(&icn83xx_ts_driver);
+}
+
+static void __exit icn83xx_ts_exit(void)
+{
+ icn83xx_trace("==icn83xx_ts_exit==\n");
+ i2c_unregister_device(this_client);
+ return i2c_del_driver(&icn83xx_ts_driver);
+}
+
+late_initcall(icn83xx_ts_init);
+module_exit(icn83xx_ts_exit);
+
+MODULE_AUTHOR("<zmtian@chiponeic.com>");
+MODULE_DESCRIPTION("Chipone icn83xx TouchScreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/icn83xx_ts/icn83xx.h b/drivers/input/touchscreen/icn83xx_ts/icn83xx.h
new file mode 100755
index 00000000..46a7cf21
--- /dev/null
+++ b/drivers/input/touchscreen/icn83xx_ts/icn83xx.h
@@ -0,0 +1,434 @@
+/*++
+
+ Copyright (c) 2012-2022 ChipOne Technology (Beijing) Co., Ltd. All Rights Reserved.
+ This PROPRIETARY SOFTWARE is the property of ChipOne Technology (Beijing) Co., Ltd.
+ and may contains trade secrets and/or other confidential information of ChipOne
+ Technology (Beijing) Co., Ltd. This file shall not be disclosed to any third party,
+ in whole or in part, without prior written consent of ChipOne.
+ THIS PROPRIETARY SOFTWARE & ANY RELATED DOCUMENTATION ARE PROVIDED AS IS,
+ WITH ALL FAULTS, & WITHOUT WARRANTY OF ANY KIND. CHIPONE DISCLAIMS ALL EXPRESS OR
+ IMPLIED WARRANTIES.
+
+ File Name: icn83xx.h
+ Abstract:
+ input driver.
+Author: Zhimin Tian
+Date : 01,17,2013
+Version: 1.0
+History :
+ 2012,10,30, V0.1 first version
+
+ --*/
+
+#ifndef __LINUX_ICN83XX_H__
+#define __LINUX_ICN83XX_H__
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ #include <linux/pm.h>
+ #include <linux/earlysuspend.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/async.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/hrtimer.h>
+#include <linux/proc_fs.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+ #include <linux/semaphore.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/spinlock_types.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include "../../../video/backlight/wmt_bl.h"
+
+//-----------------------------------------------------------------------------
+// Pin Declarations
+//-----------------------------------------------------------------------------
+
+#define SUPPORT_ROCKCHIP 0
+
+#if SUPPORT_ROCKCHIP
+#include <linux/irq.h>
+#include <mach/irqs.h>
+//#include <mach/system.h>
+#include <mach/hardware.h>
+//#include <mach/board.h>
+#include <mach/gpio.h>
+
+#define CTP_IRQ_PORT RK30_PIN1_PB7
+#define CTP_IRQ_MODE 0
+#define CTP_RST_PORT RK30_PIN1_PA7
+#define CTP_WAKEUP_PORT 0
+ //1: B protocol
+#define SCREEN_MAX_X (800)
+#define SCREEN_MAX_Y (480)
+#define ICN83XX_I2C_SCL 400*1000
+
+#endif
+
+#define CTP_REPORT_PROTOCOL 1 //0: A protocol
+
+//-----------------------------------------------------------------------------
+// Global CONSTANTS
+//-----------------------------------------------------------------------------
+
+#define TOUCH_VIRTUAL_KEYS 0
+#define SUPPORT_PROC_FS 1
+#define SUPPORT_SYSFS 1
+#define SUPPORT_FW_UPDATE 1
+#define COMPILE_FW_WITH_DRIVER 0
+#define FORCE_UPDATA_FW 0
+#define SUPPORT_FW_CALIB 0
+#define SUPPORT_DELAYED_WORK 0
+
+#define ICN83XX_NAME "chipone-ts"
+#define ICN83XX_PROG_IIC_ADDR (0x60>>1)
+#define ICN83XX_IIC_ADDR (0x80>>1)
+#define CTP_NAME ICN83XX_NAME
+
+#define CTP_RESET_LOW_PERIOD (5)
+#define CTP_RESET_HIGH_PERIOD (100)
+#define CTP_WAKEUP_LOW_PERIOD (20)
+#define CTP_WAKEUP_HIGH_PERIOD (50)
+#define CTP_POLL_TIMER (16) /* ms delay between samples */
+#define CTP_START_TIMER (100) /* ms delay between samples */
+
+#define POINT_NUM 5
+#define POINT_SIZE 7
+
+#define TS_KEY_HOME 102
+#define TS_KEY_MENU 139
+#define TS_KEY_BACK 158
+#define TS_KEY_SEARCH 217
+
+#define ICN_VIRTUAL_BUTTON_HOME 0x02
+#define ICN_VIRTUAL_BUTTON_MENU 0x01
+#define ICN_VIRTUAL_BUTTON_BACK 0x04
+#define ICN_VIRTUAL_BUTTON_SEARCH 0x08
+
+#define IIC_RETRY_NUM 3
+
+//ICN83XX_REG_PMODE
+#define PMODE_ACTIVE 0x00
+#define PMODE_MONITOR 0x01
+#define PMODE_HIBERNATE 0x02
+
+#define B_SIZE 32
+#define ENABLE_BYTE_CHECK
+//#define WAKE_PIN 1
+//-----------------------------------------------------------------------------
+// Macro DEFINITIONS
+//-----------------------------------------------------------------------------
+#define DBG_ICN83XX_TRACE
+//#define DBG_ICN83XX_POINT
+//#define DBG_ICN83XX_INFO
+#define DBG_ICN83XX_ERROR
+#define DBG_FLASH_INFO
+#define DBG_FLASH_ERROR
+#define DBG_OP_INFO
+#define DBG_OP_ERROR
+#define DBG_CALIB_INFO
+#define DBG_CALIB_ERROR
+//#define DBG_PROC_INFO
+#define DBG_PROC_ERROR
+
+
+#ifdef DBG_ICN83XX_TRACE
+#define icn83xx_trace(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn83xx_trace(fmt, args...) //
+#endif
+
+
+#ifdef DBG_ICN83XX_POINT
+#define icn83xx_point_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn83xx_point_info(fmt, args...) //
+#endif
+
+#ifdef DBG_ICN83XX_INFO
+#define icn83xx_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn83xx_info(fmt, args...) //
+#endif
+
+#ifdef DBG_ICN83XX_ERROR
+#define icn83xx_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn83xx_error(fmt, args...) //
+#endif
+
+#ifdef DBG_FLASH_INFO
+#define flash_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define flash_info(fmt, args...) //
+#endif
+
+#ifdef DBG_FLASH_ERROR
+#define flash_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define flash_error(fmt, args...) //
+#endif
+
+
+#ifdef DBG_OP_INFO
+#define op_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define op_info(fmt, args...) //
+#endif
+#ifdef DBG_OP_ERROR
+#define op_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define op_error(fmt, args...) //
+#endif
+
+
+#ifdef DBG_CALIB_INFO
+#define calib_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define calib_info(fmt, args...) //
+#endif
+
+#ifdef DBG_CALIB_ERROR
+#define calib_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define calib_error(fmt, args...) //
+#endif
+
+
+#ifdef DBG_PROC_INFO
+#define proc_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define proc_info(fmt, args...) //
+#endif
+
+#ifdef DBG_PROC_ERROR
+#define proc_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define proc_error(fmt, args...) //
+#endif
+
+#define swap_ab(a,b) {char temp;temp=a;a=b;b=temp;}
+#define U16LOBYTE(var) (*(unsigned char *) &var)
+#define U16HIBYTE(var) (*(unsigned char *)((unsigned char *) &var + 1))
+
+
+
+//-----------------------------------------------------------------------------
+// Struct, Union and Enum DEFINITIONS
+//-----------------------------------------------------------------------------
+typedef struct _POINT_INFO
+{
+ unsigned char u8ID;
+ unsigned short u16PosX; // coordinate X, plus 4 LSBs for precision extension
+ unsigned short u16PosY; // coordinate Y, plus 4 LSBs for precision extension
+ unsigned char u8Pressure;
+ unsigned char u8EventId;
+}POINT_INFO;
+
+struct icn83xx_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct work_struct pen_event_work;
+ struct delayed_work icn_delayed_work;
+ struct workqueue_struct *ts_workqueue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ struct hrtimer timer;
+ spinlock_t irq_lock;
+ struct semaphore sem;
+
+ POINT_INFO point_info[POINT_NUM+1];
+ int point_num;
+ int irq;
+ int irq_is_disable;
+ int use_irq;
+ int work_mode;
+ int screen_max_x;
+ int screen_max_y;
+ int revert_x_flag;
+ int revert_y_flag;
+ int exchange_x_y_flag;
+ int (*init_wakeup_hw)(void);
+};
+
+struct touch_param {
+ char fw_name[32];
+ int irqgpio;
+ int rstgpio;
+ int panelres_x;
+ int panelres_y;
+ int xyswap;
+ int xdir;
+ int ydir;
+ int max_finger_num;
+ int force_download;
+ int earlysus_en;
+ int dbg;
+ int lcd_exchg;
+};
+
+#pragma pack(1)
+typedef struct{
+ unsigned char wr; //write read flag£¬0:R 1:W
+ unsigned char flag; //0:
+ unsigned char circle; //polling cycle
+ unsigned char times; //plling times
+ unsigned char retry; //I2C retry times
+ unsigned int data_len; //data length
+ unsigned char addr_len; //address length
+ unsigned char addr[2]; //address
+ unsigned char* data; //data pointer
+}pack_head;
+#pragma pack()
+
+#define DATA_LENGTH_UINT 512
+#define CMD_HEAD_LENGTH (sizeof(pack_head) - sizeof(unsigned char *))
+#define ICN83XX_ENTRY_NAME "icn83xx_tool"
+enum icn83xx_ts_regs {
+ ICN83XX_REG_PMODE = 0x04, /* Power Consume Mode */
+};
+
+typedef enum
+{
+ R_OK = 100,
+ R_FILE_ERR,
+ R_STATE_ERR,
+ R_ERASE_ERR,
+ R_PROGRAM_ERR,
+ R_VERIFY_ERR,
+}E_UPGRADE_ERR_TYPE;
+
+//-----------------------------------------------------------------------------
+// Global VARIABLES
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Function PROTOTYPES
+//-----------------------------------------------------------------------------
+
+void icn83xx_ts_reset(void);
+int icn83xx_i2c_rxdata(unsigned char addr, char *rxdata, int length);
+int icn83xx_i2c_txdata(unsigned char addr, char *txdata, int length);
+int icn83xx_write_reg(unsigned char addr, char para);
+int icn83xx_read_reg(unsigned char addr, char *pdata);
+int icn83xx_prog_i2c_rxdata(unsigned short addr, char *rxdata, int length);
+int icn83xx_prog_i2c_txdata(unsigned short addr, char *txdata, int length);
+int icn83xx_prog_write_reg(unsigned short addr, char para);
+int icn83xx_prog_read_reg(unsigned short addr, char *pdata);
+#if SUPPORT_FW_UPDATE
+
+int icn83xx_writeInfo(unsigned short addr, char value);
+int icn83xx_readInfo(unsigned short addr, char *value);
+int icn83xx_writeReg(unsigned short addr, char value);
+int icn83xx_readReg(unsigned short addr, char *value);
+int icn83xx_readVersion(void);
+int icn83xx_changemode(char mode);
+int icn83xx_readrawdata(char *buffer, char row, char length);
+int icn83xx_readTP(char row_num, char column_num, char *buffer);
+int icn83xx_scanTP(void);
+void icn83xx_rawdatadump(short *mem, int size, char br);
+void icn83xx_set_fw(int size, unsigned char *buf);
+void icn83xx_memdump(char *mem, int size);
+int icn83xx_checksum(int sum, char *buf, unsigned int size);
+int icn83xx_update_status(int status);
+int icn83xx_get_status(void);
+int icn83xx_open_fw( char *fw);
+int icn83xx_read_fw(int offset, int length, char *buf);
+int icn83xx_close_fw(void);
+int icn83xx_goto_progmode(void);
+int icn83xx_check_progmod(void);
+int icn83xx_uu(void);
+void icn83xx_ll(void);
+int icn83xx_op1(char info, unsigned short offset, unsigned int size);
+int icn83xx_op2(char info, unsigned short offset, unsigned char * buffer, unsigned int size);
+int icn83xx_op3(char info, unsigned short offset, unsigned char * buffer, unsigned int size);
+short icn83xx_read_fw_Ver(char *fw);
+E_UPGRADE_ERR_TYPE icn83xx_fw_update(char *fw);
+#endif
+
+#if SUPPORT_FW_CALIB
+
+int icn83xx_checkrawdata(short *data, char num);
+int icn83xx_readpara(char *TxOrder, char row, char *RxOrder, char column);
+int icn83xx_writepara(char *TxOrder, char row, char *RxOrder, char column);
+int icn83xx_readFB(char *FB, char num);
+int icn83xx_writeFB(char *FB, char num);
+int icn83xx_readDC(char *DC, char num);
+int icn83xx_writeDC(char *DC, char num);
+int icn83xx_readPhaseDelay(char *PD, char row, char length);
+int icn83xx_writePhaseDelay(char *PD, char row, char length);
+int icn83xx_changeDCflag(char flag);
+int icn83xx_readVkmode(char *vkmode, char *vknum);
+int icn83xx_setTarget(short target);
+int icn83xx_setPeakGroup(short peak, short group);
+int icn83xx_setDownUp(short down, short up);
+int icn83xx_average(short *data, char num);
+
+int icn83xx_calib(char index, char *FB);
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/icn83xx_ts/icn83xx_fw.h b/drivers/input/touchscreen/icn83xx_ts/icn83xx_fw.h
new file mode 100755
index 00000000..572bf1f3
--- /dev/null
+++ b/drivers/input/touchscreen/icn83xx_ts/icn83xx_fw.h
@@ -0,0 +1,3 @@
+static unsigned char icn83xx_fw[] = {
+
+}; \ No newline at end of file
diff --git a/drivers/input/touchscreen/icn85xx_ts/Kconfig b/drivers/input/touchscreen/icn85xx_ts/Kconfig
new file mode 100755
index 00000000..593b379b
--- /dev/null
+++ b/drivers/input/touchscreen/icn85xx_ts/Kconfig
@@ -0,0 +1,16 @@
+#
+# ICN85XX capacity touch screen driver configuration
+#
+config TOUCHSCREEN_ICN85XX
+ tristate "ICN85XX I2C Capacitive Touchscreen Input Driver Support"
+ depends on ARCH_WMT
+ default y
+ help
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_icn85xx
+
diff --git a/drivers/input/touchscreen/icn85xx_ts/Makefile b/drivers/input/touchscreen/icn85xx_ts/Makefile
new file mode 100755
index 00000000..4f2c65ce
--- /dev/null
+++ b/drivers/input/touchscreen/icn85xx_ts/Makefile
@@ -0,0 +1,32 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_icn85xx
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := icn85xx.o icn85xx_flash.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers modules.builtin
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers modules.builtin
diff --git a/drivers/input/touchscreen/icn85xx_ts/icn85xx.c b/drivers/input/touchscreen/icn85xx_ts/icn85xx.c
new file mode 100755
index 00000000..371742fb
--- /dev/null
+++ b/drivers/input/touchscreen/icn85xx_ts/icn85xx.c
@@ -0,0 +1,2431 @@
+/*++
+
+ Copyright (c) 2012-2022 ChipOne Technology (Beijing) Co., Ltd. All Rights Reserved.
+ This PROPRIETARY SOFTWARE is the property of ChipOne Technology (Beijing) Co., Ltd.
+ and may contains trade secrets and/or other confidential information of ChipOne
+ Technology (Beijing) Co., Ltd. This file shall not be disclosed to any third party,
+ in whole or in part, without prior written consent of ChipOne.
+ THIS PROPRIETARY SOFTWARE & ANY RELATED DOCUMENTATION ARE PROVIDED AS IS,
+ WITH ALL FAULTS, & WITHOUT WARRANTY OF ANY KIND. CHIPONE DISCLAIMS ALL EXPRESS OR
+ IMPLIED WARRANTIES.
+
+ File Name: icn85xx.c
+ Abstract:
+ input driver.
+ Author: Zhimin Tian
+ Date : 08,14,2013
+ Version: 1.0
+ History :
+ 2012,10,30, V0.1 first version
+ --*/
+
+#include "icn85xx.h"
+#include "icn85xx_fw.h"
+//#include "icn85xx_00_ht_0528.h"
+//#include "icn85xx_02_lh_0528.h"
+
+#if COMPILE_FW_WITH_DRIVER
+ static char firmware[128] = "icn85xx_firmware";
+#else
+ #if SUPPORT_SENSOR_ID
+ static char firmware[128] = {0};
+ #else
+ //static char firmware[128] = {"/misc/modules/ICN8505.BIN"};
+ //static char firmware[128] = {"/system/etc/firmware/ICN8505.bin"};
+ static char firmware[128] = {"ICN8505.bin"};
+ #endif
+#endif
+
+#if SUPPORT_SENSOR_ID
+ char cursensor_id,tarsensor_id,id_match;
+ char invalid_id = 0;
+
+ struct sensor_id {
+ char value;
+ const char bin_name[128];
+ unsigned char *fw_name;
+ int size;
+ };
+
+static struct sensor_id sensor_id_table[] = {
+ { 0x00, "/misc/modules/ICN8505_00_name1.BIN",fw_00_ht_0528,sizeof(fw_00_ht_0528)},//default bin or fw
+ { 0x02, "/misc/modules/ICN8505_02_name3.BIN",fw_02_lh_0528,sizeof(fw_02_lh_0528)},
+
+ // if you want support other sensor id value ,please add here
+ };
+#endif
+struct i2c_client *this_client;
+short log_basedata[COL_NUM][ROW_NUM] = {{0,0}};
+short log_rawdata[COL_NUM][ROW_NUM] = {{0,0}};
+short log_diffdata[COL_NUM][ROW_NUM] = {{0,0}};
+unsigned int log_on_off = 0;
+static struct touch_param g_param;
+static struct wake_lock downloadWakeLock;
+static struct task_struct *resume_download_task;
+static int bl_is_delay = 0;
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+ //yank added
+ void icn85xx_charge_mode(void)
+ {
+ printk("yank---%s\n",__func__);
+ icn85xx_write_reg(ICN85xx_REG_PMODE, 0x55);
+ }
+ EXPORT_SYMBOL(icn85xx_charge_mode);
+
+ void icn85xx_discharge_mode(void)
+ {
+ printk("yank---%s\n",__func__);
+ icn85xx_write_reg(ICN85xx_REG_PMODE, 0x66);
+ }
+ EXPORT_SYMBOL(icn85xx_discharge_mode);
+
+
+static enum hrtimer_restart chipone_timer_func(struct hrtimer *timer);
+#if SUPPORT_SYSFS
+static ssize_t icn85xx_show_update(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t icn85xx_store_update(struct device* cd, struct device_attribute *attr, const char* buf, size_t len);
+static ssize_t icn85xx_show_process(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t icn85xx_store_process(struct device* cd, struct device_attribute *attr,const char* buf, size_t len);
+
+static DEVICE_ATTR(update, S_IRUGO | S_IWUSR, icn85xx_show_update, icn85xx_store_update);
+static DEVICE_ATTR(process, S_IRUGO | S_IWUSR, icn85xx_show_process, icn85xx_store_process);
+
+static ssize_t icn85xx_show_process(struct device* cd,struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+ sprintf(buf, "icn85xx process\n");
+ ret = strlen(buf) + 1;
+ return ret;
+}
+
+static ssize_t icn85xx_store_process(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+ unsigned long on_off = simple_strtoul(buf, NULL, 10);
+
+ log_on_off = on_off;
+ memset(&log_basedata[0][0], 0, COL_NUM*ROW_NUM*2);
+ if(on_off == 0)
+ {
+ icn85xx_ts->work_mode = 0;
+ }
+ else if((on_off == 1) || (on_off == 2) || (on_off == 3))
+ {
+ if((icn85xx_ts->work_mode == 0) && (icn85xx_ts->use_irq == 1))
+ {
+ hrtimer_init(&icn85xx_ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ icn85xx_ts->timer.function = chipone_timer_func;
+ hrtimer_start(&icn85xx_ts->timer, ktime_set(CTP_START_TIMER/1000, (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ icn85xx_ts->work_mode = on_off;
+ }
+ else if(on_off == 10)
+ {
+ icn85xx_ts->work_mode = 4;
+ mdelay(10);
+ printk("update baseline\n");
+ icn85xx_write_reg(4, 0x30);
+ icn85xx_ts->work_mode = 0;
+ }
+ else
+ {
+ icn85xx_ts->work_mode = 0;
+ }
+
+
+ return len;
+}
+
+static ssize_t icn85xx_show_update(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+ sprintf(buf, firmware);
+ ret = strlen(buf) + 1;
+ printk("firmware: %s, ret: %d\n", firmware, ret);
+
+ return ret;
+}
+
+static ssize_t icn85xx_store_update(struct device* cd, struct device_attribute *attr, const char* buf, size_t len)
+{
+ printk("len: %d, update: %s\n", len, buf);
+ memset(firmware, 0, 128);
+ memcpy(firmware, buf, len-1);
+ if(R_OK == icn85xx_fw_update(firmware))
+ {
+ printk("update ok\n");
+ }
+ else
+ {
+ printk("update error\n");
+ }
+ return len;
+}
+
+static int icn85xx_create_sysfs(struct i2c_client *client)
+{
+ int err;
+ struct device *dev = &(client->dev);
+ icn85xx_trace("%s: \n",__func__);
+ err = device_create_file(dev, &dev_attr_update);
+ err = device_create_file(dev, &dev_attr_process);
+ return err;
+}
+
+static void icn85xx_remove_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &(client->dev);
+ icn85xx_trace("%s: \n",__func__);
+ device_remove_file(dev, &dev_attr_update);
+ device_remove_file(dev, &dev_attr_process);
+}
+#endif
+
+#if SUPPORT_PROC_FS
+
+pack_head cmd_head;
+static struct proc_dir_entry *icn85xx_proc_entry;
+int DATA_LENGTH = 0;
+
+STRUCT_PANEL_PARA_H g_structPanelPara;
+
+static int icn85xx_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data)
+{
+ int ret = 0;
+ int i;
+ unsigned short addr;
+ char retvalue;
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+ proc_info("%s \n",__func__);
+ if(down_interruptible(&icn85xx_ts->sem))
+ {
+ return -1;
+ }
+ ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ else
+ {
+ ret = CMD_HEAD_LENGTH;
+ }
+
+ proc_info("wr :0x%02x.\n", cmd_head.wr);
+ proc_info("flag:0x%02x.\n", cmd_head.flag);
+ proc_info("circle :%d.\n", (int)cmd_head.circle);
+ proc_info("times :%d.\n", (int)cmd_head.times);
+ proc_info("retry :%d.\n", (int)cmd_head.retry);
+ proc_info("data len:%d.\n", (int)cmd_head.data_len);
+ proc_info("addr len:%d.\n", (int)cmd_head.addr_len);
+ proc_info("addr:0x%02x%02x.\n", cmd_head.addr[0], cmd_head.addr[1]);
+ proc_info("len:%d.\n", (int)len);
+ proc_info("data:0x%02x%02x.\n", buff[CMD_HEAD_LENGTH], buff[CMD_HEAD_LENGTH+1]);
+ if (1 == cmd_head.wr) // write para
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ //need copy to g_structPanelPara
+
+ memcpy(&g_structPanelPara, &cmd_head.data[0], cmd_head.data_len);
+ //write para to tp
+ for(i=0; i<cmd_head.data_len; )
+ {
+ int size = ((i+64) > cmd_head.data_len)?(cmd_head.data_len-i):64;
+ ret = icn85xx_i2c_txdata(0x8000+i, &cmd_head.data[i], size);
+ if (ret < 0) {
+ proc_error("write para failed!\n");
+ goto write_out;
+ }
+ i = i + 64;
+ }
+
+ ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+ icn85xx_ts->work_mode = 5; //reinit
+ printk("reinit tp\n");
+ icn85xx_write_reg(0, 1);
+ mdelay(100);
+ icn85xx_write_reg(0, 0);
+ icn85xx_ts->work_mode = 0;
+ goto write_out;
+
+ }
+ else if(3 == cmd_head.wr) //set update file
+ {
+ proc_info("cmd_head_.wr == 3 \n");
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+ memset(firmware, 0, 128);
+ memcpy(firmware, &cmd_head.data[0], cmd_head.data_len);
+ proc_info("firmware : %s\n", firmware);
+ }
+ else if(5 == cmd_head.wr) //start update
+ {
+ proc_info("cmd_head_.wr == 5 \n");
+ icn85xx_update_status(1);
+ ret = kernel_thread(icn85xx_fw_update,firmware,CLONE_KERNEL);
+ icn85xx_trace("the kernel_thread result is:%d\n", ret);
+ }
+ else if(11 == cmd_head.wr) //write hostcomm
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ proc_error("copy_from_user failed.\n");
+ goto write_out;
+ }
+ addr = (cmd_head.addr[1]<<8) | cmd_head.addr[0];
+ icn85xx_write_reg(addr, cmd_head.data[0]);
+ }
+ else if(13 == cmd_head.wr) //adc enable
+ {
+ proc_info("cmd_head_.wr == 13 \n");
+ icn85xx_ts->work_mode = 4;
+ mdelay(10);
+ //set col
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u8ColNum), 1);
+ //u8RXOrder[0] = u8RXOrder[cmd_head.addr[0]];
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u8RXOrder[0]), g_structPanelPara.u8RXOrder[cmd_head.addr[0]]);
+ //set row
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u8RowNum), 1);
+ //u8TXOrder[0] = u8TXOrder[cmd_head.addr[1]];
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u8TXOrder[0]), g_structPanelPara.u8TXOrder[cmd_head.addr[1]]);
+ //scan mode
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u8ScanMode), 0);
+ //bit
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u16BitFreq), 0xD0);
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u16BitFreq)+1, 0x07);
+ //freq
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u16FreqCycleNum[0]), 0x64);
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u16FreqCycleNum[0])+1, 0x00);
+ //pga
+ icn85xx_write_reg(0x8000+STRUCT_OFFSET(STRUCT_PANEL_PARA_H, u8PgaGain), 0x0);
+
+ //config mode
+ icn85xx_write_reg(0, 0x2);
+
+ mdelay(1);
+ icn85xx_read_reg(2, &retvalue);
+ printk("retvalue0: %d\n", retvalue);
+ while(retvalue != 1)
+ {
+ printk("retvalue: %d\n", retvalue);
+ mdelay(1);
+ icn85xx_read_reg(2, &retvalue);
+ }
+
+ if(icn85xx_goto_progmode() != 0)
+ {
+ printk("icn85xx_goto_progmode() != 0 error\n");
+ goto write_out;
+ }
+
+ icn85xx_prog_write_reg(0x040870, 1);
+
+ }
+
+write_out:
+ up(&icn85xx_ts->sem);
+ proc_info("icn85xx_tool_write write_out \n");
+ return len;
+
+}
+
+static int icn85xx_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data )
+{
+ int i, j;
+ int ret = 0;
+
+ char row, column, retvalue;
+ unsigned short addr;
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+ if(down_interruptible(&icn85xx_ts->sem))
+ {
+ return -1;
+ }
+ proc_info("%s: count:%d, off:%d, cmd_head.data_len: %d\n",__func__, count,(int)off,(int)cmd_head.data_len);
+ if (cmd_head.wr % 2)
+ {
+ ret = 0;
+ proc_info("cmd_head_.wr == 1111111 \n");
+ goto read_out;
+ }
+ else if (0 == cmd_head.wr) //read para
+ {
+ //read para
+ proc_info("cmd_head_.wr == 0 \n");
+ ret = icn85xx_i2c_rxdata(0x8000, &page[0], cmd_head.data_len);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+ memcpy(&g_structPanelPara, &page[0], sizeof(g_structPanelPara));
+ goto read_out;
+
+ }
+ else if(2 == cmd_head.wr) //get update status
+ {
+ proc_info("cmd_head_.wr == 2 \n");
+ page[0] = icn85xx_get_status();
+ proc_info("status: %d\n", page[0]);
+ }
+ else if(4 == cmd_head.wr) //read rawdata
+ {
+ //icn85xx_read_reg(0x8004, &row);
+ //icn85xx_read_reg(0x8005, &column);
+ proc_info("cmd_head_.wr == 4 \n");
+ row = cmd_head.addr[1];
+ column = cmd_head.addr[0];
+ //scan tp rawdata
+ icn85xx_write_reg(4, 0x20);
+ mdelay(1);
+ for(i=0; i<1000; i++)
+ {
+ mdelay(1);
+ icn85xx_read_reg(2, &retvalue);
+ if(retvalue == 1)
+ break;
+ }
+
+ for(i=0; i<row; i++)
+ {
+ ret = icn85xx_i2c_rxdata(0x2000 + i*(COL_NUM)*2,(char *) &log_rawdata[i][0], column*2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+ //icn85xx_rawdatadump(&log_rawdata[i][0], column, COL_NUM);
+ memcpy(&page[column*2*i], &log_rawdata[i][0], column*2);
+
+ }
+
+ //finish scan tp rawdata
+ icn85xx_write_reg(2, 0x0);
+ icn85xx_write_reg(4, 0x21);
+ goto read_out;
+ }
+ else if(6 == cmd_head.wr) //read diffdata
+ {
+ proc_info("cmd_head_.wr == 6 \n");
+ row = cmd_head.addr[1];
+ column = cmd_head.addr[0];
+
+ //scan tp rawdata
+ icn85xx_write_reg(4, 0x20);
+ mdelay(1);
+
+ for(i=0; i<1000; i++)
+ {
+ mdelay(1);
+ icn85xx_read_reg(2, &retvalue);
+ if(retvalue == 1)
+ break;
+ }
+
+ for(i=0; i<row; i++)
+ {
+ ret = icn85xx_i2c_rxdata(0x3000 + (i+1)*(COL_NUM+2)*2 + 2,(char *) &log_diffdata[i][0], column*2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+ //icn85xx_rawdatadump(&log_diffdata[i][0], column, COL_NUM);
+ memcpy(&page[column*2*i], &log_diffdata[i][0], column*2);
+ }
+ //finish scan tp rawdata
+ icn85xx_write_reg(2, 0x0);
+ icn85xx_write_reg(4, 0x21);
+
+ goto read_out;
+ }
+ else if(8 == cmd_head.wr) //change TxVol, read diff
+ {
+ proc_info("cmd_head_.wr == 8 \n");
+ row = cmd_head.addr[1];
+ column = cmd_head.addr[0];
+ //scan tp rawdata
+ icn85xx_write_reg(4, 0x20);
+ mdelay(1);
+ for(i=0; i<1000; i++)
+ {
+ mdelay(1);
+ icn85xx_read_reg(2, &retvalue);
+ if(retvalue == 1)
+ break;
+ }
+
+ for(i=0; i<row; i++)
+ {
+ ret = icn85xx_i2c_rxdata(0x2000 + i*(COL_NUM)*2,(char *) &log_rawdata[i][0], column*2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+
+ }
+
+ //finish scan tp rawdata
+ icn85xx_write_reg(2, 0x0);
+ icn85xx_write_reg(4, 0x21);
+
+ icn85xx_write_reg(4, 0x12);
+
+ //scan tp rawdata
+ icn85xx_write_reg(4, 0x20);
+ mdelay(1);
+ for(i=0; i<1000; i++)
+ {
+ mdelay(1);
+ icn85xx_read_reg(2, &retvalue);
+ if(retvalue == 1)
+ break;
+ }
+
+ for(i=0; i<row; i++)
+ {
+ ret = icn85xx_i2c_rxdata(0x2000 + i*(COL_NUM)*2,(char *) &log_diffdata[i][0], column*2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+ for(j=0; j<column; j++)
+ {
+ *(short *)&page[2*(column*i +j)] = log_rawdata[i][j] - log_diffdata[i][j];
+ }
+
+ }
+
+ //finish scan tp rawdata
+ icn85xx_write_reg(2, 0x0);
+ icn85xx_write_reg(4, 0x21);
+
+ icn85xx_write_reg(4, 0x10);
+
+ goto read_out;
+ }
+ else if(10 == cmd_head.wr) //read adc data
+ {
+ if(cmd_head.flag == 0)
+ {
+ icn85xx_prog_write_reg(0x040874, 0);
+ }
+ icn85xx_prog_i2c_rxdata(2500*cmd_head.flag ,&page[0], cmd_head.data_len);
+ //icn85xx_rawdatadump(&page[0], 1024, 16);
+ if(cmd_head.flag == 9)
+ {
+ //reload code
+ if(icn85xx_ts->ictype == ICN85XX_WITHOUT_FLASH)
+ {
+ if(R_OK == icn85xx_fw_update(firmware))
+ {
+ icn85xx_ts->code_loaded_flag = 1;
+ icn85xx_trace("ICN85XX_WITHOUT_FLASH, reload code ok\n");
+ }
+ else
+ {
+ icn85xx_ts->code_loaded_flag = 0;
+ icn85xx_trace("ICN85XX_WITHOUT_FLASH, reload code error\n");
+ }
+ }
+ else
+ {
+ icn85xx_bootfrom_flash();
+ msleep(50);
+ }
+ icn85xx_ts->work_mode = 0;
+ }
+ }
+ else if(12 == cmd_head.wr) //read hostcomm
+ {
+ proc_info("cmd_head_.wr == 12 \n");
+ addr = (cmd_head.addr[1]<<8) | cmd_head.addr[0];
+ icn85xx_read_reg(addr, &retvalue);
+ page[0] = retvalue;
+ }
+ else if(14 == cmd_head.wr) //read adc status
+ {
+ proc_info("cmd_head_.wr == 14 \n");
+ icn85xx_prog_read_reg(0x4085E, &retvalue);
+ page[0] = retvalue;
+ printk("0x4085E: 0x%x\n", retvalue);
+ }
+read_out:
+ up(&icn85xx_ts->sem);
+ proc_info("%s out: %d, cmd_head.data_len: %d\n\n",__func__, count, cmd_head.data_len);
+ return cmd_head.data_len;
+}
+
+void init_proc_node(void)
+{
+ int i;
+ memset(&cmd_head, 0, sizeof(cmd_head));
+ cmd_head.data = NULL;
+
+ i = 5;
+ while ((!cmd_head.data) && i)
+ {
+ cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
+ if (NULL != cmd_head.data)
+ {
+ break;
+ }
+ i--;
+ }
+ if (i)
+ {
+ //DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH;
+ DATA_LENGTH = i * DATA_LENGTH_UINT;
+ icn85xx_trace("alloc memory size:%d.\n", DATA_LENGTH);
+ }
+ else
+ {
+ proc_error("alloc for memory failed.\n");
+ return ;
+ }
+
+ icn85xx_proc_entry = create_proc_entry(ICN85xx_ENTRY_NAME, 0666, NULL);
+ if (icn85xx_proc_entry == NULL)
+ {
+ proc_error("Couldn't create proc entry!\n");
+ return ;
+ }
+ else
+ {
+ icn85xx_trace("Create proc entry success!\n");
+ icn85xx_proc_entry->write_proc = icn85xx_tool_write;
+ icn85xx_proc_entry->read_proc = icn85xx_tool_read;
+ }
+
+ return ;
+}
+
+void uninit_proc_node(void)
+{
+ kfree(cmd_head.data);
+ cmd_head.data = NULL;
+ remove_proc_entry(ICN85xx_ENTRY_NAME, NULL);
+}
+
+#endif
+
+
+#if TOUCH_VIRTUAL_KEYS
+static ssize_t virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf,
+ __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":100:1030:50:60"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":280:1030:50:60"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":470:1030:50:60"
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":900:1030:50:60"
+ "\n");
+}
+
+static struct kobj_attribute virtual_keys_attr = {
+ .attr = {
+ .name = "virtualkeys.chipone-ts",
+ .mode = S_IRUGO,
+ },
+ .show = &virtual_keys_show,
+};
+
+static struct attribute *properties_attrs[] = {
+ &virtual_keys_attr.attr,
+ NULL
+};
+
+static struct attribute_group properties_attr_group = {
+ .attrs = properties_attrs,
+};
+
+static void icn85xx_ts_virtual_keys_init(void)
+{
+ int ret;
+ struct kobject *properties_kobj;
+ properties_kobj = kobject_create_and_add("board_properties", NULL);
+ if (properties_kobj)
+ ret = sysfs_create_group(properties_kobj,
+ &properties_attr_group);
+ if (!properties_kobj || ret)
+ pr_err("failed to create board_properties\n");
+}
+#endif
+
+
+
+
+/* ---------------------------------------------------------------------
+*
+* Chipone panel related driver
+*
+*
+----------------------------------------------------------------------*/
+/***********************************************************************************************
+Name : icn85xx_ts_wakeup
+Input : void
+Output : ret
+function : this function is used to wakeup tp
+***********************************************************************************************/
+void icn85xx_ts_wakeup(void)
+{
+
+
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_ts_reset
+Input : void
+Output : ret
+function : this function is used to reset tp, you should not delete it
+***********************************************************************************************/
+void icn85xx_ts_reset(void)
+{
+ //set reset func
+
+ int rst = g_param.rstgpio;
+ gpio_direction_output(rst,0);
+ msleep(50);
+ icn85xx_info("[%s]:>>>>>>>>>>>>>>>>>CTP_RST_PORT = %d;msleep(50);\n",__func__,gpio_get_value(rst));
+ gpio_direction_output(rst,1);
+ msleep(70);
+ icn85xx_info("[%s]:>>>>>>>>>>>>>>>>>CTP_RST_PORT = %d;msleep(70);\n",__func__,gpio_get_value(rst));
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_irq_disable
+Input : void
+Output : ret
+function : this function is used to disable irq
+***********************************************************************************************/
+void icn85xx_irq_disable(void)
+{
+ unsigned long irqflags;
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+
+ spin_lock_irqsave(&icn85xx_ts->irq_lock, irqflags);
+ if (!icn85xx_ts->irq_is_disable)
+ {
+ icn85xx_ts->irq_is_disable = 1;
+ wmt_gpio_mask_irq(g_param.irqgpio);
+ //disable_irq_nosync(icn85xx_ts->irq);
+ //disable_irq(icn85xx_ts->irq);
+ }
+ spin_unlock_irqrestore(&icn85xx_ts->irq_lock, irqflags);
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_irq_enable
+Input : void
+Output : ret
+function : this function is used to enable irq
+***********************************************************************************************/
+void icn85xx_irq_enable(void)
+{
+ unsigned long irqflags = 0;
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+
+ spin_lock_irqsave(&icn85xx_ts->irq_lock, irqflags);
+ if (icn85xx_ts->irq_is_disable)
+ {
+ wmt_gpio_unmask_irq(g_param.irqgpio);
+ //enable_irq(icn85xx_ts->irq);
+ icn85xx_ts->irq_is_disable = 0;
+ }
+ spin_unlock_irqrestore(&icn85xx_ts->irq_lock, irqflags);
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_prog_i2c_rxdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : read data from icn85xx, prog mode
+***********************************************************************************************/
+int icn85xx_prog_i2c_rxdata(unsigned int addr, char *rxdata, int length)
+{
+ int ret = -1;
+ int retries = 0;
+#if 0
+ struct i2c_msg msgs[] = {
+ {
+ .addr = ICN85XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN85XX_I2C_SCL,
+#endif
+ },
+ };
+
+ icn85xx_prog_i2c_txdata(addr, NULL, 0);
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msgs, 1);
+ if(ret == 1)break;
+ retries++;
+ }
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn85xx_error("%s i2c read error: %d\n", __func__, ret);
+// icn85xx_ts_reset();
+ }
+#else
+ unsigned char tmp_buf[3];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = ICN85XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = 0,
+ .len = 3,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN85XX_I2C_SCL,
+#endif
+ },
+ {
+ .addr = ICN85XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN85XX_I2C_SCL,
+#endif
+ },
+ };
+
+ tmp_buf[0] = (unsigned char)(addr>>16);
+ tmp_buf[1] = (unsigned char)(addr>>8);
+ tmp_buf[2] = (unsigned char)(addr);
+
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn85xx_error("%s i2c read error: %d\n", __func__, ret);
+// icn85xx_ts_reset();
+ }
+#endif
+ return ret;
+}
+/***********************************************************************************************
+Name : icn85xx_prog_i2c_txdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : send data to icn85xx , prog mode
+***********************************************************************************************/
+int icn85xx_prog_i2c_txdata(unsigned int addr, char *txdata, int length)
+{
+ int ret = -1;
+ char tmp_buf[128];
+ int retries = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = ICN85XX_PROG_IIC_ADDR,//this_client->addr,
+ .flags = 0,
+ .len = length + 3,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN85XX_I2C_SCL,
+#endif
+ },
+ };
+
+ if (length > 125)
+ {
+ icn85xx_error("%s too big datalen = %d!\n", __func__, length);
+ return -1;
+ }
+
+ tmp_buf[0] = (unsigned char)(addr>>16);
+ tmp_buf[1] = (unsigned char)(addr>>8);
+ tmp_buf[2] = (unsigned char)(addr);
+
+
+ if (length != 0 && txdata != NULL)
+ {
+ memcpy(&tmp_buf[3], txdata, length);
+ }
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msg, 1);
+ if(ret == 1)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn85xx_error("%s i2c write error: %d\n", __func__, ret);
+// icn85xx_ts_reset();
+ }
+ return ret;
+}
+/***********************************************************************************************
+Name : icn85xx_prog_write_reg
+Input : addr -- address
+ para -- parameter
+Output :
+function : write register of icn85xx, prog mode
+***********************************************************************************************/
+int icn85xx_prog_write_reg(unsigned int addr, char para)
+{
+ char buf[3];
+ int ret = -1;
+
+ buf[0] = para;
+ ret = icn85xx_prog_i2c_txdata(addr, buf, 1);
+ if (ret < 0) {
+ icn85xx_error("%s write reg failed! %#x ret: %d\n", __func__, buf[0], ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_prog_read_reg
+Input : addr
+ pdata
+Output :
+function : read register of icn85xx, prog mode
+***********************************************************************************************/
+int icn85xx_prog_read_reg(unsigned int addr, char *pdata)
+{
+ int ret = -1;
+ ret = icn85xx_prog_i2c_rxdata(addr, pdata, 1);
+ return ret;
+}
+
+/***********************************************************************************************
+Name : icn85xx_i2c_rxdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : read data from icn85xx, normal mode
+***********************************************************************************************/
+int icn85xx_i2c_rxdata(unsigned short addr, char *rxdata, int length)
+{
+ int ret = -1;
+ int retries = 0;
+ unsigned char tmp_buf[2];
+#if 0
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN85XX_I2C_SCL,
+#endif
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN85XX_I2C_SCL,
+#endif
+ },
+ };
+
+ tmp_buf[0] = (unsigned char)(addr>>8);
+ tmp_buf[1] = (unsigned char)(addr);
+
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+ if(ret == 2)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn85xx_error("%s i2c read error: %d\n", __func__, ret);
+// icn85xx_ts_reset();
+ }
+#else
+
+ tmp_buf[0] = (unsigned char)(addr>>8);
+ tmp_buf[1] = (unsigned char)(addr);
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ // ret = i2c_transfer(this_client->adapter, msgs, 2);
+ ret = i2c_master_send(this_client, tmp_buf, 2);
+ if (ret < 0)
+ return ret;
+ ret = i2c_master_recv(this_client, rxdata, length);
+ if (ret < 0)
+ return ret;
+ if(ret == length)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn85xx_error("%s i2c read error: %d\n", __func__, ret);
+// icn85xx_ts_reset();
+ }
+#endif
+
+ return ret;
+}
+/***********************************************************************************************
+Name : icn85xx_i2c_txdata
+Input : addr
+ *rxdata
+ length
+Output : ret
+function : send data to icn85xx , normal mode
+***********************************************************************************************/
+int icn85xx_i2c_txdata(unsigned short addr, char *txdata, int length)
+{
+ int ret = -1;
+ unsigned char tmp_buf[128];
+ int retries = 0;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length + 2,
+ .buf = tmp_buf,
+#if SUPPORT_ROCKCHIP
+ .scl_rate = ICN85XX_I2C_SCL,
+#endif
+ },
+ };
+
+ if (length > 125)
+ {
+ icn85xx_error("%s too big datalen = %d!\n", __func__, length);
+ return -1;
+ }
+
+ tmp_buf[0] = (unsigned char)(addr>>8);
+ tmp_buf[1] = (unsigned char)(addr);
+
+ if (length != 0 && txdata != NULL)
+ {
+ memcpy(&tmp_buf[2], txdata, length);
+ }
+
+ while(retries < IIC_RETRY_NUM)
+ {
+ ret = i2c_transfer(this_client->adapter, msg, 1);
+ if(ret == 1)break;
+ retries++;
+ }
+
+ if (retries >= IIC_RETRY_NUM)
+ {
+ icn85xx_error("%s i2c write error: %d\n", __func__, ret);
+// icn85xx_ts_reset();
+ }
+
+ return ret;
+}
+
+/***********************************************************************************************
+Name : icn85xx_write_reg
+Input : addr -- address
+ para -- parameter
+Output :
+function : write register of icn85xx, normal mode
+***********************************************************************************************/
+int icn85xx_write_reg(unsigned short addr, char para)
+{
+ char buf[3];
+ int ret = -1;
+
+ buf[0] = para;
+ ret = icn85xx_i2c_txdata(addr, buf, 1);
+ if (ret < 0) {
+ icn85xx_error("write reg failed! %#x ret: %d\n", buf[0], ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_read_reg
+Input : addr
+ pdata
+Output :
+function : read register of icn85xx, normal mode
+***********************************************************************************************/
+int icn85xx_read_reg(unsigned short addr, char *pdata)
+{
+ int ret = -1;
+ ret = icn85xx_i2c_rxdata(addr, pdata, 1);
+ if(ret < 0)
+ {
+ icn85xx_error("addr: 0x%x: 0x%x\n", addr, *pdata);
+ }
+ return ret;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_log
+Input : 0: rawdata, 1: diff data
+Output : err type
+function : calibrate param
+***********************************************************************************************/
+static void icn85xx_log(char diff)
+{
+ char row = 0;
+ char column = 0;
+ int i, j, ret;
+ char retvalue = 0;
+
+ icn85xx_read_reg(0x8004, &row);
+ icn85xx_read_reg(0x8005, &column);
+
+ //scan tp rawdata
+ icn85xx_write_reg(4, 0x20);
+ mdelay(1);
+ for(i=0; i<1000; i++)
+ {
+ mdelay(1);
+ icn85xx_read_reg(2, &retvalue);
+ if(retvalue == 1)
+ break;
+ }
+ if(diff == 0)
+ {
+ for(i=0; i<row; i++)
+ {
+ ret = icn85xx_i2c_rxdata(0x2000 + i*COL_NUM*2, (char *)&log_rawdata[i][0], column*2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+ icn85xx_rawdatadump(&log_rawdata[i][0], column, COL_NUM);
+
+ }
+ }
+ if(diff == 1)
+ {
+ for(i=0; i<row; i++)
+ {
+ ret = icn85xx_i2c_rxdata(0x3000 + (i+1)*(COL_NUM+2)*2 + 2, (char *)&log_diffdata[i][0], column*2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+ icn85xx_rawdatadump(&log_diffdata[i][0], column, COL_NUM);
+
+ }
+ }
+ else if(diff == 2)
+ {
+ for(i=0; i<row; i++)
+ {
+ ret = icn85xx_i2c_rxdata(0x2000 + i*COL_NUM*2, (char *)&log_rawdata[i][0], column*2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ }
+ if((log_basedata[0][0] != 0) || (log_basedata[0][1] != 0))
+ {
+ for(j=0; j<column; j++)
+ {
+ log_rawdata[i][j] = log_basedata[i][j] - log_rawdata[i][j];
+ }
+ }
+ icn85xx_rawdatadump(&log_rawdata[i][0], column, COL_NUM);
+
+ }
+ if((log_basedata[0][0] == 0) && (log_basedata[0][1] == 0))
+ {
+ memcpy(&log_basedata[0][0], &log_rawdata[0][0], COL_NUM*ROW_NUM*2);
+ }
+
+
+ }
+
+ //finish scan tp rawdata
+ icn85xx_write_reg(2, 0x0);
+ icn85xx_write_reg(4, 0x21);
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_iic_test
+Input : void
+Output :
+function : 0 success,
+***********************************************************************************************/
+static int icn85xx_iic_test(void)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+ int ret = -1;
+ char value = 0;
+ int retry = 0;
+ int flashid;
+ icn85xx_ts->ictype = 0;
+ icn85xx_trace("====%s begin=====. \n", __func__);
+
+ while(retry++ < 3)
+ {
+ ret = icn85xx_read_reg(0xa, &value);
+ if(ret > 0)
+ {
+ if(value == 0x85)
+ {
+ icn85xx_ts->ictype = ICN85XX_WITH_FLASH;
+ return ret;
+ }
+ }
+
+ icn85xx_info("iic test error! retry = %d\n", retry);
+ msleep(3);
+ }
+ icn85xx_goto_progmode();
+ msleep(10);
+ retry = 0;
+ while(retry++ < 3)
+ {
+ ret = icn85xx_prog_i2c_rxdata(0x040002, &value, 1);
+ icn85xx_info("icn85xx_check_progmod: 0x%x\n", value);
+ if(ret > 0)
+ {
+ if(value == 0x85)
+ {
+ flashid = icn85xx_read_flashid();
+ if((MD25D40_ID1 == flashid) || (MD25D40_ID2 == flashid)
+ ||(MD25D20_ID1 == flashid) || (MD25D20_ID1 == flashid)
+ ||(GD25Q10_ID == flashid) || (MX25L512E_ID == flashid))
+ {
+ icn85xx_ts->ictype = ICN85XX_WITH_FLASH;
+ }
+ else
+ {
+ icn85xx_ts->ictype = ICN85XX_WITHOUT_FLASH;
+ }
+ return ret;
+ }
+ }
+ icn85xx_error("iic2 test error! %d\n", retry);
+ msleep(3);
+ }
+
+ return ret;
+}
+
+#if !CTP_REPORT_PROTOCOL
+/***********************************************************************************************
+Name : icn85xx_ts_release
+Input : void
+Output :
+function : touch release
+***********************************************************************************************/
+static void icn85xx_ts_release(void)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+ icn85xx_info("==icn85xx_ts_release ==\n");
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_sync(icn85xx_ts->input_dev);
+}
+
+/***********************************************************************************************
+Name : icn85xx_report_value_A
+Input : void
+Output :
+function : reprot touch ponit
+***********************************************************************************************/
+static void icn85xx_report_value_A(void)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+ char buf[POINT_NUM*POINT_SIZE+3]={0};
+ int ret = -1;
+ int i;
+#if TOUCH_VIRTUAL_KEYS
+ unsigned char button;
+ static unsigned char button_last;
+#endif
+
+ icn85xx_info("==icn85xx_report_value_A ==\n");
+
+ ret = icn85xx_i2c_rxdata(0x1000, buf, POINT_NUM*POINT_SIZE+2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ return ;
+ }
+#if TOUCH_VIRTUAL_KEYS
+ button = buf[0];
+ icn85xx_info("%s: button=%d\n",__func__, button);
+
+ if((button_last != 0) && (button == 0))
+ {
+ icn85xx_ts_release();
+ button_last = button;
+ return ;
+ }
+ if(button != 0)
+ {
+ switch(button)
+ {
+ case ICN_VIRTUAL_BUTTON_HOME:
+ icn85xx_info("ICN_VIRTUAL_BUTTON_HOME down\n");
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 200);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_X, 280);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_Y, 1030);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn85xx_ts->input_dev);
+ input_sync(icn85xx_ts->input_dev);
+ break;
+ case ICN_VIRTUAL_BUTTON_BACK:
+ icn85xx_info("ICN_VIRTUAL_BUTTON_BACK down\n");
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 200);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_X, 470);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_Y, 1030);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn85xx_ts->input_dev);
+ input_sync(icn85xx_ts->input_dev);
+ break;
+ case ICN_VIRTUAL_BUTTON_MENU:
+ icn85xx_info("ICN_VIRTUAL_BUTTON_MENU down\n");
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 200);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_X, 100);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_Y, 1030);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn85xx_ts->input_dev);
+ input_sync(icn85xx_ts->input_dev);
+ break;
+ default:
+ icn85xx_info("other gesture\n");
+ break;
+ }
+ button_last = button;
+ return ;
+ }
+#endif
+
+ icn85xx_ts->point_num = buf[1];
+ if (icn85xx_ts->point_num == 0) {
+ icn85xx_ts_release();
+ return ;
+ }
+ for(i=0;i<icn85xx_ts->point_num;i++){
+ if(buf[8 + POINT_SIZE*i] != 4)
+ {
+ break ;
+ }
+ else
+ {
+
+ }
+ }
+
+ if(i == icn85xx_ts->point_num) {
+ icn85xx_ts_release();
+ return ;
+ }
+
+ for(i=0; i<icn85xx_ts->point_num; i++)
+ {
+ icn85xx_ts->point_info[i].u8ID = buf[2 + POINT_SIZE*i];
+ icn85xx_ts->point_info[i].u16PosX = (buf[4 + POINT_SIZE*i]<<8) + buf[3 + POINT_SIZE*i];
+ icn85xx_ts->point_info[i].u16PosY = (buf[6 + POINT_SIZE*i]<<8) + buf[5 + POINT_SIZE*i];
+ icn85xx_ts->point_info[i].u8Pressure = 20;//buf[7 + POINT_SIZE*i];
+ icn85xx_ts->point_info[i].u8EventId = buf[8 + POINT_SIZE*i];
+
+ if(1 == icn85xx_ts->revert_x_flag)
+ {
+ icn85xx_ts->point_info[i].u16PosX = icn85xx_ts->screen_max_x- icn85xx_ts->point_info[i].u16PosX;
+ }
+ if(1 == icn85xx_ts->revert_y_flag)
+ {
+ icn85xx_ts->point_info[i].u16PosY = icn85xx_ts->screen_max_y- icn85xx_ts->point_info[i].u16PosY;
+ }
+
+ icn85xx_info("u8ID %d\n", icn85xx_ts->point_info[i].u8ID);
+ icn85xx_info("u16PosX %d\n", icn85xx_ts->point_info[i].u16PosX);
+ icn85xx_info("u16PosY %d\n", icn85xx_ts->point_info[i].u16PosY);
+ icn85xx_info("u8Pressure %d\n", icn85xx_ts->point_info[i].u8Pressure);
+ icn85xx_info("u8EventId %d\n", icn85xx_ts->point_info[i].u8EventId);
+
+
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_TRACKING_ID, icn85xx_ts->point_info[i].u8ID);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, icn85xx_ts->point_info[i].u8Pressure);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_X, icn85xx_ts->point_info[i].u16PosX);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_Y, icn85xx_ts->point_info[i].u16PosY);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 1);
+ input_mt_sync(icn85xx_ts->input_dev);
+ icn85xx_point_info("point: %d ===x = %d,y = %d, press = %d ====\n",i, icn85xx_ts->point_info[i].u16PosX,icn85xx_ts->point_info[i].u16PosY, icn85xx_ts->point_info[i].u8Pressure);
+ }
+
+ input_sync(icn85xx_ts->input_dev);
+
+}
+#endif
+/***********************************************************************************************
+Name : icn85xx_report_value_B
+Input : void
+Output :
+function : reprot touch ponit
+***********************************************************************************************/
+#if CTP_REPORT_PROTOCOL
+static void icn85xx_report_value_B(void)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+ char buf[POINT_NUM*POINT_SIZE+3]={0};
+ static unsigned char finger_last[POINT_NUM + 1]={0};
+ unsigned char finger_current[POINT_NUM + 1] = {0};
+ unsigned int position = 0;
+ int temp = 0;
+ int ret = -1;
+ int x,y;
+ icn85xx_info("==icn85xx_report_value_B ==\n");
+
+
+
+ ret = icn85xx_i2c_rxdata(0x1000, buf, POINT_NUM*POINT_SIZE+2);
+ if (ret < 0) {
+ icn85xx_error("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ return ;
+ }
+ icn85xx_ts->point_num = buf[1];
+ if (icn85xx_ts->point_num > POINT_NUM)
+ {
+ return ;
+ }
+
+ if(icn85xx_ts->point_num > 0)
+ {
+ for(position = 0; position<icn85xx_ts->point_num; position++)
+ {
+ temp = buf[2 + POINT_SIZE*position] + 1;
+ finger_current[temp] = 1;
+ icn85xx_ts->point_info[temp].u8ID = buf[2 + POINT_SIZE*position];
+ icn85xx_ts->point_info[temp].u16PosX = (buf[4 + POINT_SIZE*position]<<8) + buf[3 + POINT_SIZE*position];
+ icn85xx_ts->point_info[temp].u16PosY = (buf[6 + POINT_SIZE*position]<<8) + buf[5 + POINT_SIZE*position];
+ icn85xx_ts->point_info[temp].u8Pressure = buf[7 + POINT_SIZE*position];
+ icn85xx_ts->point_info[temp].u8EventId = buf[8 + POINT_SIZE*position];
+
+ if(icn85xx_ts->point_info[temp].u8EventId == 4)
+ finger_current[temp] = 0;
+
+ if(1 == icn85xx_ts->revert_x_flag)
+ {
+ icn85xx_ts->point_info[temp].u16PosX = icn85xx_ts->screen_max_x- icn85xx_ts->point_info[temp].u16PosX;
+ }
+ if(1 == icn85xx_ts->revert_y_flag)
+ {
+ icn85xx_ts->point_info[temp].u16PosY = icn85xx_ts->screen_max_y- icn85xx_ts->point_info[temp].u16PosY;
+ }
+ icn85xx_info("temp %d\n", temp);
+ icn85xx_info("u8ID %d\n", icn85xx_ts->point_info[temp].u8ID);
+ icn85xx_info("u16PosX %d\n", icn85xx_ts->point_info[temp].u16PosX);
+ icn85xx_info("u16PosY %d\n", icn85xx_ts->point_info[temp].u16PosY);
+ icn85xx_info("u8Pressure %d\n", icn85xx_ts->point_info[temp].u8Pressure);
+ icn85xx_info("u8EventId %d\n", icn85xx_ts->point_info[temp].u8EventId);
+ //icn85xx_info("u8Pressure %d\n", icn85xx_ts->point_info[temp].u8Pressure*16);
+ }
+ }
+ else
+ {
+ for(position = 1; position < POINT_NUM+1; position++)
+ {
+ finger_current[position] = 0;
+ }
+ icn85xx_info("no touch\n");
+ }
+
+ for(position = 1; position < POINT_NUM + 1; position++)
+ {
+ if((finger_current[position] == 0) && (finger_last[position] != 0))
+ {
+ input_mt_slot(icn85xx_ts->input_dev, position-1);
+ input_mt_report_slot_state(icn85xx_ts->input_dev, MT_TOOL_FINGER, false);
+ icn85xx_point_info("one touch up: %d\n", position);
+ }
+ else if(finger_current[position])
+ {
+ if (g_param.xyswap == 0)
+ {
+ x = icn85xx_ts->point_info[position].u16PosX;
+ y = icn85xx_ts->point_info[position].u16PosY;
+ } else {
+ y = icn85xx_ts->point_info[position].u16PosX;
+ x = icn85xx_ts->point_info[position].u16PosY;
+ }
+ if (g_param.xdir == -1)
+ {
+ x = g_param.panelres_x - x;
+ }
+ if (g_param.ydir == -1)
+ {
+ y = g_param.panelres_y - y;
+ }
+
+ if (g_param.lcd_exchg) {
+ int tmp;
+ tmp = x;
+ x = y;
+ y = g_param.panelres_x - tmp;
+ }
+
+ input_mt_slot(icn85xx_ts->input_dev, position-1);
+ input_mt_report_slot_state(icn85xx_ts->input_dev, MT_TOOL_FINGER, true);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 1);
+ //input_report_abs(icn85xx_ts->input_dev, ABS_MT_PRESSURE, icn85xx_ts->point_info[position].u8Pressure);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_PRESSURE, 200);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(icn85xx_ts->input_dev, ABS_MT_POSITION_Y, y);
+ //icn85xx_point_info("===position: %d, x = %d,y = %d, press = %d ====\n", position, icn85xx_ts->point_info[position].u16PosX,icn85xx_ts->point_info[position].u16PosY, icn85xx_ts->point_info[position].u8Pressure);
+ icn85xx_point_info("raw%d(%d,%d), rpt%d(%d,%d)\n", position, icn85xx_ts->point_info[position].u16PosX, icn85xx_ts->point_info[position].u16PosY, position, x, y);
+ }
+
+ }
+ input_sync(icn85xx_ts->input_dev);
+
+ for(position = 1; position < POINT_NUM + 1; position++)
+ {
+ finger_last[position] = finger_current[position];
+ }
+
+}
+#endif
+
+/***********************************************************************************************
+Name : icn85xx_ts_pen_irq_work
+Input : void
+Output :
+function : work_struct
+***********************************************************************************************/
+static void icn85xx_ts_pen_irq_work(struct work_struct *work)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(this_client);
+#if SUPPORT_PROC_FS
+ if(down_interruptible(&icn85xx_ts->sem))
+ {
+ return ;
+ }
+#endif
+
+ if(icn85xx_ts->work_mode == 0)
+ {
+#if CTP_REPORT_PROTOCOL
+ icn85xx_report_value_B();
+#else
+ icn85xx_report_value_A();
+#endif
+
+
+ if(icn85xx_ts->use_irq)
+ {
+ icn85xx_irq_enable();
+ }
+ if(log_on_off == 4)
+ {
+ printk("normal raw data\n");
+ icn85xx_log(0); //raw data
+ }
+ else if(log_on_off == 5)
+ {
+ printk("normal diff data\n");
+ icn85xx_log(1); //diff data
+ }
+ else if(log_on_off == 6)
+ {
+ printk("normal raw2diff\n");
+ icn85xx_log(2); //diff data
+ }
+
+ }
+ else if(icn85xx_ts->work_mode == 1)
+ {
+ printk("raw data\n");
+ icn85xx_log(0); //raw data
+ }
+ else if(icn85xx_ts->work_mode == 2)
+ {
+ printk("diff data\n");
+ icn85xx_log(1); //diff data
+ }
+ else if(icn85xx_ts->work_mode == 3)
+ {
+ printk("raw2diff data\n");
+ icn85xx_log(2); //diff data
+ }
+ else if(icn85xx_ts->work_mode == 4) //idle
+ {
+ ;
+ }
+ else if(icn85xx_ts->work_mode == 5)//write para, reinit
+ {
+ printk("reinit tp\n");
+ icn85xx_write_reg(0, 1);
+ mdelay(100);
+ icn85xx_write_reg(0, 0);
+ icn85xx_ts->work_mode = 0;
+ }
+
+#if SUPPORT_PROC_FS
+ up(&icn85xx_ts->sem);
+#endif
+
+
+}
+/***********************************************************************************************
+Name : chipone_timer_func
+Input : void
+Output :
+function : Timer interrupt service routine.
+***********************************************************************************************/
+static enum hrtimer_restart chipone_timer_func(struct hrtimer *timer)
+{
+ struct icn85xx_ts_data *icn85xx_ts = container_of(timer, struct icn85xx_ts_data, timer);
+ queue_work(icn85xx_ts->ts_workqueue, &icn85xx_ts->pen_event_work);
+ //icn85xx_info("chipone_timer_func\n");
+ if(icn85xx_ts->use_irq == 1)
+ {
+ if((icn85xx_ts->work_mode == 1) || (icn85xx_ts->work_mode == 2) || (icn85xx_ts->work_mode == 3))
+ {
+ hrtimer_start(&icn85xx_ts->timer, ktime_set(CTP_POLL_TIMER/1000, (CTP_POLL_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ }
+ else
+ {
+ hrtimer_start(&icn85xx_ts->timer, ktime_set(CTP_POLL_TIMER/1000, (CTP_POLL_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ return HRTIMER_NORESTART;
+}
+/***********************************************************************************************
+Name : icn85xx_ts_interrupt
+Input : void
+Output :
+function : interrupt service routine
+***********************************************************************************************/
+static irqreturn_t icn85xx_ts_interrupt(int irq, void *dev_id)
+{
+ struct icn85xx_ts_data *icn85xx_ts = dev_id;
+ int irqindex = g_param.irqgpio;
+
+ icn85xx_info("==========------icn85xx_ts TS Interrupt-----============\n");
+
+ if (gpio_irqstatus(irqindex)) {
+ wmt_gpio_ack_irq(irqindex);
+ if (is_gpio_irqenable(irqindex)) {
+ icn85xx_irq_disable();
+ if(icn85xx_ts->work_mode != 0) {
+ icn85xx_irq_enable();
+ return IRQ_HANDLED;
+ }
+ if (!work_pending(&icn85xx_ts->pen_event_work)) {
+ icn85xx_info("Enter work\n");
+ queue_work(icn85xx_ts->ts_workqueue, &icn85xx_ts->pen_event_work);
+ }
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+
+ /*if(icn85xx_ts->use_irq)
+ icn85xx_irq_disable();
+ if (!work_pending(&icn85xx_ts->pen_event_work))
+ {
+ queue_work(icn85xx_ts->ts_workqueue, &icn85xx_ts->pen_event_work);
+
+ }
+
+ return IRQ_HANDLED;*/
+}
+
+/***********************************************************************************************
+Name : icn85xx_ts_suspend
+Input : void
+Output :
+function : tp enter sleep mode
+***********************************************************************************************/
+static int icn85xx_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(client);
+ icn85xx_trace("icn85xx_ts_suspend\n");
+ if (icn85xx_ts->use_irq) {
+ icn85xx_irq_disable();
+ } else {
+ hrtimer_cancel(&icn85xx_ts->timer);
+ }
+ cancel_work_sync(&icn85xx_ts->pen_event_work);
+ flush_workqueue(icn85xx_ts->ts_workqueue);
+ //}
+
+ //reset flag if ic is flashless when power off
+ if(icn85xx_ts->ictype == ICN85XX_WITHOUT_FLASH)
+ {
+ //icn85xx_ts->code_loaded_flag = 0;
+ }
+#if SUSPEND_POWER_OFF
+
+#else
+ icn85xx_write_reg(ICN85xx_REG_PMODE, PMODE_HIBERNATE);
+#endif
+
+ return 0;
+
+}
+
+int resume_download_thread(void *arg)
+{
+ int retry = 1;
+ int need_update_fw = false;
+ int ret = -1;
+ unsigned char value;
+ struct icn85xx_ts_data *icn85xx_ts = (struct icn85xx_ts_data*)arg;
+ wake_lock(&downloadWakeLock);
+ while (retry-- && !need_update_fw) {
+ icn85xx_ts_reset();
+ icn85xx_bootfrom_sram();
+ msleep(50);
+ ret = icn85xx_read_reg(0xa, &value);
+ if (ret > 0) {
+ need_update_fw = false;
+ break;
+ }
+ }
+ if (retry < 0) need_update_fw = true;
+
+ if (need_update_fw) {
+ if(R_OK == icn85xx_fw_update(firmware)) {
+ icn85xx_ts->code_loaded_flag = 1;
+ icn85xx_trace("ICN85XX_WITHOUT_FLASH, reload code ok\n");
+ }
+ else {
+ icn85xx_ts->code_loaded_flag = 0;
+ icn85xx_trace("ICN85XX_WITHOUT_FLASH, reload code error\n");
+ }
+ }
+
+ if (icn85xx_ts->use_irq) {
+ icn85xx_irq_enable();
+ } else {
+ hrtimer_start(&icn85xx_ts->timer, ktime_set(CTP_START_TIMER/1000, \
+ (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ wake_unlock(&downloadWakeLock);
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn85xx_ts_resume
+Input : void
+Output :
+function : wakeup tp or reset tp
+***********************************************************************************************/
+static int icn85xx_ts_resume(struct i2c_client *client)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(client);
+ int i;
+ icn85xx_trace("==icn85xx_ts_resume== \n");
+
+ //report touch release
+#if CTP_REPORT_PROTOCOL
+ for(i = 0; i < POINT_NUM; i++)
+ {
+ input_mt_slot(icn85xx_ts->input_dev, i);
+ input_mt_report_slot_state(icn85xx_ts->input_dev, MT_TOOL_FINGER, false);
+ }
+#else
+ icn85xx_ts_release();
+#endif
+
+
+ if(icn85xx_ts->ictype == ICN85XX_WITHOUT_FLASH) {
+ if (bl_is_delay) {
+ resume_download_task = kthread_create(resume_download_thread, icn85xx_ts, "resume_download");
+ if(IS_ERR(resume_download_task)) {
+ icn85xx_error("cread thread failed\n");
+ }
+ wake_up_process(resume_download_task);
+ } else
+ resume_download_thread(icn85xx_ts);
+ } else {
+ icn85xx_write_reg(ICN85xx_REG_PMODE, 0xff);
+ icn85xx_ts_reset();
+ if (icn85xx_ts->use_irq) {
+ icn85xx_irq_enable();
+ } else {
+ hrtimer_start(&icn85xx_ts->timer, ktime_set(CTP_START_TIMER/1000, \
+ (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+ }
+
+ // icn85xx_write_reg(ICN85xx_REG_PMODE, 0x00);
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn85xx_request_io_port
+Input : void
+Output :
+function : 0 success,
+***********************************************************************************************/
+static int icn85xx_request_io_port(struct icn85xx_ts_data *icn85xx_ts)
+{
+
+#if SUPPORT_ROCKCHIP
+ icn85xx_ts->screen_max_x = SCREEN_MAX_X;
+ icn85xx_ts->screen_max_y = SCREEN_MAX_Y;
+ icn85xx_ts->irq = CTP_IRQ_PORT; //maybe need changed
+#endif
+ icn85xx_ts->irq = IRQ_GPIO;
+
+ if (gpio_request(g_param.rstgpio, "ts_rst") < 0) {
+ printk("gpio(%d) touchscreen reset request fail\n", g_param.rstgpio);
+ return -EIO;
+ }
+ gpio_direction_output(g_param.rstgpio, 1);
+
+ if (gpio_request(g_param.irqgpio, "ts_irq") < 0) {
+ printk("gpio(%d) touchscreen interrupt request fail\n", g_param.irqgpio);
+ gpio_free(g_param.rstgpio);
+ return -EIO;
+ }
+ wmt_gpio_setpull(g_param.irqgpio, WMT_GPIO_PULL_UP);
+ gpio_direction_input(g_param.irqgpio);
+
+
+ return 0;
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_free_io_port
+Input : void
+Output :
+function : 0 success,
+***********************************************************************************************/
+static void icn85xx_free_io_port(void)
+{
+ gpio_free(g_param.rstgpio);
+ gpio_free(g_param.irqgpio);
+ return;
+}
+
+/***********************************************************************************************
+Name : icn85xx_request_irq
+Input : void
+Output :
+function : 0 success,
+***********************************************************************************************/
+static int icn85xx_request_irq(struct icn85xx_ts_data *icn85xx_ts)
+{
+ int err = -1;
+
+
+ wmt_gpio_set_irq_type(g_param.irqgpio, IRQ_TYPE_EDGE_FALLING);
+ err = request_irq(icn85xx_ts->irq, icn85xx_ts_interrupt, IRQF_SHARED, "icn85xx_ts", icn85xx_ts);
+ if (err < 0)
+ {
+ icn85xx_error("icn85xx_ts_probe: request irq failed\n");
+ return err;
+ }
+ else
+ {
+ icn85xx_irq_disable();
+ icn85xx_ts->use_irq = 1;
+ }
+
+#if SUPPORT_ROCKCHIP
+ err = gpio_request(icn85xx_ts->irq, "TS_INT"); //Request IO
+ if (err < 0)
+ {
+ icn85xx_error("Failed to request GPIO:%d, ERRNO:%d\n", (int)icn85xx_ts->irq, err);
+ return err;
+ }
+ gpio_direction_input(icn85xx_ts->irq);
+ err = request_irq(icn85xx_ts->irq, icn85xx_ts_interrupt, IRQ_TYPE_EDGE_FALLING, "icn85xx_ts", icn85xx_ts);
+ if (err < 0)
+ {
+ icn85xx_ts->use_irq = 0;
+ icn85xx_error("icn85xx_ts_probe: request irq failed\n");
+ return err;
+ }
+ else
+ {
+ icn85xx_irq_disable();
+ icn85xx_ts->use_irq = 1;
+ }
+#endif
+
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_free_irq
+Input : void
+Output :
+function : 0 success,
+***********************************************************************************************/
+static void icn85xx_free_irq(struct icn85xx_ts_data *icn85xx_ts)
+{
+ if (icn85xx_ts)
+ {
+ if (icn85xx_ts->use_irq)
+ {
+ free_irq(icn85xx_ts->irq, icn85xx_ts);
+ }
+ else
+ {
+ hrtimer_cancel(&icn85xx_ts->timer);
+ }
+ }
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_request_input_dev
+Input : void
+Output :
+function : 0 success,
+***********************************************************************************************/
+static int icn85xx_request_input_dev(struct icn85xx_ts_data *icn85xx_ts)
+{
+ int ret = -1;
+ struct input_dev *input_dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ icn85xx_error("failed to allocate input device\n");
+ return -ENOMEM;
+ }
+ icn85xx_ts->input_dev = input_dev;
+
+ icn85xx_ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
+#if CTP_REPORT_PROTOCOL
+ __set_bit(INPUT_PROP_DIRECT, icn85xx_ts->input_dev->propbit);
+ input_mt_init_slots(icn85xx_ts->input_dev, POINT_NUM*2);
+#else
+ set_bit(ABS_MT_TOUCH_MAJOR, icn85xx_ts->input_dev->absbit);
+ set_bit(ABS_MT_POSITION_X, icn85xx_ts->input_dev->absbit);
+ set_bit(ABS_MT_POSITION_Y, icn85xx_ts->input_dev->absbit);
+ set_bit(ABS_MT_WIDTH_MAJOR, icn85xx_ts->input_dev->absbit);
+#endif
+ if (g_param.lcd_exchg) {
+ input_set_abs_params(icn85xx_ts->input_dev, ABS_MT_POSITION_X, 0, g_param.panelres_y, 0, 0);
+ input_set_abs_params(icn85xx_ts->input_dev, ABS_MT_POSITION_Y, 0, g_param.panelres_x, 0, 0);
+ } else {
+ input_set_abs_params(icn85xx_ts->input_dev, ABS_MT_POSITION_X, 0, g_param.panelres_x, 0, 0);
+ input_set_abs_params(icn85xx_ts->input_dev, ABS_MT_POSITION_Y, 0, g_param.panelres_y, 0, 0);
+ }
+ input_set_abs_params(icn85xx_ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(icn85xx_ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(icn85xx_ts->input_dev, ABS_MT_TRACKING_ID, 0, POINT_NUM*2, 0, 0);
+
+ __set_bit(KEY_MENU, input_dev->keybit);
+ __set_bit(KEY_BACK, input_dev->keybit);
+ __set_bit(KEY_HOME, input_dev->keybit);
+ __set_bit(KEY_SEARCH, input_dev->keybit);
+
+ input_dev->name = CTP_NAME;
+ ret = input_register_device(input_dev);
+ if (ret) {
+ icn85xx_error("Register %s input device failed\n", input_dev->name);
+ input_free_device(input_dev);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ icn85xx_trace("==register_early_suspend =\n");
+ icn85xx_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ icn85xx_ts->early_suspend.suspend = icn85xx_ts_suspend;
+ icn85xx_ts->early_suspend.resume = icn85xx_ts_resume;
+ register_early_suspend(&icn85xx_ts->early_suspend);
+#endif
+
+ return 0;
+}
+#if SUPPORT_SENSOR_ID
+static void read_sensor_id(void)
+{
+ int i,ret;
+ //icn85xx_trace("scan sensor id value begin sensor_id_num = %d\n",(sizeof(sensor_id_table)/sizeof(sensor_id_table[0])));
+ ret = icn85xx_read_reg(0x10, &cursensor_id);
+ if(ret > 0)
+ {
+ icn85xx_trace("cursensor_id= 0x%x\n", cursensor_id);
+ }
+ else
+ {
+ icn85xx_error("icn85xx read cursensor_id failed.\n");
+ cursensor_id = -1;
+ }
+
+ ret = icn85xx_read_reg(0x1e, &tarsensor_id);
+ if(ret > 0)
+ {
+ icn85xx_trace("tarsensor_id= 0x%x\n", tarsensor_id);
+ tarsensor_id = -1;
+ }
+ else
+ {
+ icn85xx_error("icn85xx read tarsensor_id failed.\n");
+ }
+ ret = icn85xx_read_reg(0x1f, &id_match);
+ if(ret > 0)
+ {
+ icn85xx_trace("match_flag= 0x%x\n", id_match); // 1: match; 0:not match
+ }
+ else
+ {
+ icn85xx_error("icn85xx read id_match failed.\n");
+ id_match = -1;
+ }
+ // scan sensor id value
+ icn85xx_trace("begin to scan id table,find correct fw or bin. sensor_id_num = %d\n",(sizeof(sensor_id_table)/sizeof(sensor_id_table[0])));
+
+ for(i = 0;i < (sizeof(sensor_id_table)/sizeof(sensor_id_table[0])); i++) // not change tp
+ {
+ if (cursensor_id == sensor_id_table[i].value)
+ {
+ #if COMPILE_FW_WITH_DRIVER
+ icn85xx_set_fw(sensor_id_table[i].size, sensor_id_table[i].fw_name);
+ #else
+ strcpy(firmware,sensor_id_table[i].bin_name);
+ icn85xx_trace("icn85xx matched firmware = %s\n", firmware);
+ #endif
+ icn85xx_trace("icn85xx matched id = 0x%x\n", sensor_id_table[i].value);
+ invalid_id = 1;
+ break;
+ }
+ else
+ {
+ invalid_id = 0;
+ icn85xx_trace("icn85xx not matched id%d= 0x%x\n", i,sensor_id_table[i].value);
+ //icn85xx_trace("not match sensor_id_table[%d].value= 0x%x,bin_name = %s\n",i,sensor_id_table[i].value,sensor_id_table[i].bin_name);
+ }
+ }
+
+}
+static void compare_sensor_id(void)
+{
+ int retry = 5;
+
+ read_sensor_id(); // select sensor id
+
+ if(0 == invalid_id) //not compare sensor id,update default fw or bin
+ {
+ icn85xx_trace("not compare sensor id table,update default: invalid_id= %d, cursensor_id= %d\n", invalid_id,cursensor_id);
+ #if COMPILE_FW_WITH_DRIVER
+ icn85xx_set_fw(sensor_id_table[0].size, sensor_id_table[0].fw_name);
+ #else
+ strcpy(firmware,sensor_id_table[0].bin_name);
+ icn85xx_trace("match default firmware = %s\n", firmware);
+ #endif
+
+ while(retry > 0)
+ {
+ if(R_OK == icn85xx_fw_update(firmware))
+ {
+ icn85xx_trace("icn85xx upgrade default firmware ok\n");
+ break;
+ }
+ retry--;
+ icn85xx_error("icn85xx_fw_update default firmware failed.\n");
+ }
+ }
+
+ if ((1 == invalid_id)&&(0 == id_match)) // tp is changed,update current fw or bin
+ {
+ icn85xx_trace("icn85xx detect tp is changed!!! invalid_id= %d,id_match= %d,\n", invalid_id,id_match);
+ while(retry > 0)
+ {
+ if(R_OK == icn85xx_fw_update(firmware))
+ {
+ icn85xx_trace("icn85xx upgrade cursensor id firmware ok\n");
+ break;
+ }
+ retry--;
+ icn85xx_error("icn85xx_fw_update current id firmware failed.\n");
+ }
+ }
+}
+#endif
+
+static void icn85xx_update(struct icn85xx_ts_data *icn85xx_ts)
+{
+ short fwVersion = 0;
+ short curVersion = 0;
+ int retry = 0;
+
+ if(icn85xx_ts->ictype == ICN85XX_WITHOUT_FLASH)
+ {
+ #if (COMPILE_FW_WITH_DRIVER && !SUPPORT_SENSOR_ID)
+ icn85xx_set_fw(sizeof(icn85xx_fw), &icn85xx_fw[0]);
+ #endif
+
+ #if SUPPORT_SENSOR_ID
+ while(0 == invalid_id ) //reselect sensor id
+ {
+ compare_sensor_id(); // select sensor id
+ icn85xx_trace("invalid_id= %d\n", invalid_id);
+ }
+ #else
+ if(R_OK == icn85xx_fw_update(firmware))
+ {
+ icn85xx_ts->code_loaded_flag = 1;
+ icn85xx_trace("ICN85XX_WITHOUT_FLASH, update default fw ok\n");
+ }
+ else
+ {
+ icn85xx_ts->code_loaded_flag = 0;
+ icn85xx_trace("ICN85XX_WITHOUT_FLASH, update error\n");
+ }
+ #endif
+
+ }
+ else if(icn85xx_ts->ictype == ICN85XX_WITH_FLASH)
+ {
+ #if (COMPILE_FW_WITH_DRIVER && !SUPPORT_SENSOR_ID)
+ icn85xx_set_fw(sizeof(icn85xx_fw), &icn85xx_fw[0]);
+ #endif
+
+ #if SUPPORT_SENSOR_ID
+ while(0 == invalid_id ) //reselect sensor id
+ {
+ compare_sensor_id(); // select sensor id
+ if( 1 == invalid_id)
+ {
+ icn85xx_trace("select sensor id ok. begin compare fwVersion with curversion\n");
+ }
+ }
+ #endif
+
+ fwVersion = icn85xx_read_fw_Ver(firmware);
+ curVersion = icn85xx_readVersion();
+ icn85xx_trace("fwVersion : 0x%x\n", fwVersion);
+ icn85xx_trace("current version: 0x%x\n", curVersion);
+
+ #if FORCE_UPDATA_FW
+ retry = 5;
+ while(retry > 0)
+ {
+ if(icn85xx_goto_progmode() != 0)
+ {
+ printk("icn85xx_goto_progmode() != 0 error\n");
+ return -1;
+ }
+ icn85xx_read_flashid();
+ if(R_OK == icn85xx_fw_update(firmware))
+ {
+ break;
+ }
+ retry--;
+ icn85xx_error("icn85xx_fw_update failed.\n");
+ }
+
+ #else
+ if(fwVersion > curVersion)
+ {
+ retry = 5;
+ while(retry > 0)
+ {
+ if(R_OK == icn85xx_fw_update(firmware))
+ {
+ break;
+ }
+ retry--;
+ icn85xx_error("icn85xx_fw_update failed.\n");
+ }
+ }
+ #endif
+ }
+}
+static int icn85xx_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct icn85xx_ts_data *icn85xx_ts;
+ int err = 0;
+
+ icn85xx_trace("====%s begin=====. \n", __func__);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ {
+ icn85xx_error("I2C check functionality failed.\n");
+ return -ENODEV;
+ }
+
+ icn85xx_ts = kzalloc(sizeof(*icn85xx_ts), GFP_KERNEL);
+ if (!icn85xx_ts)
+ {
+ icn85xx_error("Alloc icn85xx_ts memory failed.\n");
+ return -ENOMEM;
+ }
+ memset(icn85xx_ts, 0, sizeof(*icn85xx_ts));
+
+ this_client = client;
+ this_client->addr = client->addr;
+ i2c_set_clientdata(client, icn85xx_ts);
+
+ icn85xx_ts->work_mode = 0;
+ spin_lock_init(&icn85xx_ts->irq_lock);
+// icn85xx_ts->irq_lock = SPIN_LOCK_UNLOCKED;
+
+ icn85xx_request_io_port(icn85xx_ts);
+ if (err != 0) {
+ icn85xx_error("icn85xx_request_io_port failed.\n");
+ goto fail1;
+ }
+ memset(firmware, 0, 128);
+ sprintf(firmware,"%s.bin",g_param.fw_name);
+
+ icn85xx_ts_reset();
+
+ err = icn85xx_iic_test();
+ if (err <= 0)
+ {
+ icn85xx_error("icn85xx_iic_test failed.\n");
+ goto fail2;
+
+ }
+ else
+ {
+ icn85xx_trace("iic communication ok: 0x%x\n", icn85xx_ts->ictype);
+ }
+
+ icn85xx_update(icn85xx_ts);
+
+ err= icn85xx_request_input_dev(icn85xx_ts);
+ if (err < 0)
+ {
+ icn85xx_error("request input dev failed\n");
+ goto fail3;
+ }
+
+
+
+#if TOUCH_VIRTUAL_KEYS
+ icn85xx_ts_virtual_keys_init();
+#endif
+ err = icn85xx_request_irq(icn85xx_ts);
+ if (err != 0)
+ {
+ icn85xx_error("request irq error, use timer\n");
+ icn85xx_ts->use_irq = 0;
+ hrtimer_init(&icn85xx_ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ icn85xx_ts->timer.function = chipone_timer_func;
+ hrtimer_start(&icn85xx_ts->timer, ktime_set(CTP_START_TIMER/1000, (CTP_START_TIMER%1000)*1000000), HRTIMER_MODE_REL);
+ }
+#if SUPPORT_SYSFS
+ icn85xx_create_sysfs(client);
+#endif
+
+#if SUPPORT_PROC_FS
+ sema_init(&icn85xx_ts->sem, 1);
+ init_proc_node();
+#endif
+
+ INIT_WORK(&icn85xx_ts->pen_event_work, icn85xx_ts_pen_irq_work);
+ icn85xx_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));
+ if (!icn85xx_ts->ts_workqueue) {
+ icn85xx_error("create_singlethread_workqueue failed.\n");
+ err = -ESRCH;
+ goto fail4;
+ }
+
+ if(icn85xx_ts->use_irq)
+ icn85xx_irq_enable();
+ icn85xx_trace("==%s over =\n", __func__);
+ return 0;
+fail4:
+ cancel_work_sync(&icn85xx_ts->pen_event_work);
+fail3:
+ input_unregister_device(icn85xx_ts->input_dev);
+ input_free_device(icn85xx_ts->input_dev);
+fail2:
+ icn85xx_free_io_port();
+fail1:
+ kfree(icn85xx_ts);
+ icn85xx_free_fw();
+ return err;
+}
+
+static int __devexit icn85xx_ts_remove(struct i2c_client *client)
+{
+ struct icn85xx_ts_data *icn85xx_ts = i2c_get_clientdata(client);
+ icn85xx_trace("==icn85xx_ts_remove=\n");
+ if(icn85xx_ts->use_irq)
+ icn85xx_irq_disable();
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&icn85xx_ts->early_suspend);
+#endif
+
+#if SUPPORT_PROC_FS
+ uninit_proc_node();
+#endif
+
+#if SUPPORT_SYSFS
+ icn85xx_remove_sysfs(client);
+#endif
+ input_unregister_device(icn85xx_ts->input_dev);
+ input_free_device(icn85xx_ts->input_dev);
+ cancel_work_sync(&icn85xx_ts->pen_event_work);
+ destroy_workqueue(icn85xx_ts->ts_workqueue);
+ icn85xx_free_irq(icn85xx_ts);
+ icn85xx_free_io_port();
+ icn85xx_free_fw();
+ kfree(icn85xx_ts);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 96;
+ char retval[200] = {0},*p=NULL,*s=NULL;
+ int Enable=0;
+
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ printk("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+
+//paste:
+ p = retval;
+ Enable = (p[0] - '0' == 1) ? 1 : 0;
+ if(Enable == 0){
+ printk("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(p,':');p++;
+ s = strchr(p,':');
+ strncpy(g_param.fw_name,p, (s-p));
+ printk("ts_name=%s\n", g_param.fw_name);
+ if (strncmp(g_param.fw_name, "ICN85", 5)) {
+ printk("Wrong firmware name.\n");
+ return -ENODEV;
+ }
+
+ p = s+1;
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d",
+ &(g_param.irqgpio),&(g_param.panelres_x),&(g_param.panelres_y),&(g_param.rstgpio),
+ &(g_param.xyswap),&(g_param.xdir),&(g_param.ydir));
+
+ if (ret < 7) {
+ printk("Wrong format ts u-boot param(%d)!\nwmt.io.touch=%s\n",ret,retval);
+ return -ENODEV;
+ }
+
+ printk("p.x = %d, p.y = %d, irqgpio=%d, rstgpio=%d,xyswap=%d,xdir=%d,ydir=%d\n",
+ g_param.panelres_x,g_param.panelres_y,g_param.irqgpio,g_param.rstgpio,
+ g_param.xyswap,g_param.xdir,g_param.ydir);
+
+ /*memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.touch.earlysus", retval, &len);
+ if(!ret)
+ g_param.earlysus_en = (retval[0] - '0' == 1) ? 1 : 0;*/
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ g_param.lcd_exchg = 1;
+ }
+
+ ret = wmt_getsyspara("wmt.backlight.delay", retval, &len);
+ if(ret) {
+ bl_is_delay = 0;
+ } else
+ bl_is_delay = 1;
+
+ return 0;
+}
+
+static const struct i2c_device_id icn85xx_ts_id[] = {
+ { CTP_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, icn85xx_ts_id);
+
+static struct i2c_driver icn85xx_ts_driver = {
+ .probe = icn85xx_ts_probe,
+ .remove = __devexit_p(icn85xx_ts_remove),
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = icn85xx_ts_suspend,
+ .resume = icn85xx_ts_resume,
+#endif
+ .id_table = icn85xx_ts_id,
+ .driver = {
+ .name = CTP_NAME,
+ .owner = THIS_MODULE,
+ },
+
+};
+
+
+static struct i2c_board_info i2c_board_info = {
+ I2C_BOARD_INFO(CTP_NAME, ICN85XX_IIC_ADDR),
+};
+
+
+static int __init icn85xx_ts_init(void)
+{
+ struct i2c_client *client;
+ struct i2c_adapter *adap;
+ icn85xx_trace("===========================%s=====================\n", __func__);
+
+ if(wmt_check_touch_env())
+ return -ENODEV;
+
+ {//register i2c device
+ adap = i2c_get_adapter(1); //i2c Bus 1
+ if (!adap)
+ return -ENODEV;
+ client = i2c_new_device(adap, &i2c_board_info);
+ i2c_put_adapter(adap);
+ if (!client) {
+ printk("i2c_new_device error\n");
+ return -ENODEV;
+ }
+ }
+
+ return i2c_add_driver(&icn85xx_ts_driver);
+}
+
+static void __exit icn85xx_ts_exit(void)
+{
+ icn85xx_trace("==icn85xx_ts_exit==\n");
+ i2c_unregister_device(this_client);
+ i2c_del_driver(&icn85xx_ts_driver);
+}
+late_initcall(icn85xx_ts_init);
+//module_init(icn85xx_ts_init);
+module_exit(icn85xx_ts_exit);
+
+MODULE_AUTHOR("<zmtian@chiponeic.com>");
+MODULE_DESCRIPTION("Chipone icn85xx TouchScreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/icn85xx_ts/icn85xx.h b/drivers/input/touchscreen/icn85xx_ts/icn85xx.h
new file mode 100755
index 00000000..262b76fb
--- /dev/null
+++ b/drivers/input/touchscreen/icn85xx_ts/icn85xx.h
@@ -0,0 +1,500 @@
+/*++
+
+ Copyright (c) 2012-2022 ChipOne Technology (Beijing) Co., Ltd. All Rights Reserved.
+ This PROPRIETARY SOFTWARE is the property of ChipOne Technology (Beijing) Co., Ltd.
+ and may contains trade secrets and/or other confidential information of ChipOne
+ Technology (Beijing) Co., Ltd. This file shall not be disclosed to any third party,
+ in whole or in part, without prior written consent of ChipOne.
+ THIS PROPRIETARY SOFTWARE & ANY RELATED DOCUMENTATION ARE PROVIDED AS IS,
+ WITH ALL FAULTS, & WITHOUT WARRANTY OF ANY KIND. CHIPONE DISCLAIMS ALL EXPRESS OR
+ IMPLIED WARRANTIES.
+
+ File Name: icn85xx.h
+ Abstract:
+ input driver.
+Author: Zhimin Tian
+Date : 08,14,2013
+Version: 1.0
+History :
+ 2012,10,30, V0.1 first version
+
+ --*/
+
+#ifndef __LINUX_ICN85XX_H__
+#define __LINUX_ICN85XX_H__
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ #include <linux/pm.h>
+ #include <linux/earlysuspend.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/async.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/hrtimer.h>
+#include <linux/proc_fs.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+ #include <linux/semaphore.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/spinlock_types.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/kthread.h>
+#include <linux/wakelock.h>
+#include <linux/firmware.h>
+
+
+//-----------------------------------------------------------------------------
+// Pin Declarations
+//-----------------------------------------------------------------------------
+
+#define SUPPORT_ALLWINNER_A13 0
+#define SUPPORT_ROCKCHIP 0
+#define SUPPORT_SPREADTRUM 0
+
+
+
+
+#if SUPPORT_ROCKCHIP
+#include <linux/irq.h>
+#include <mach/irqs.h>
+#include <mach/system.h>
+#include <mach/hardware.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+
+
+#define CTP_IRQ_MODE 0
+
+
+ #define CTP_RST_PORT RK30_PIN2_PB1//RK30_PIN1_PB1
+ #define CTP_IRQ_PORT RK30_PIN1_PA1
+
+
+
+#define CTP_WAKEUP_PORT 0
+#define CTP_REPORT_PROTOCOL 1 //0: A protocol
+ //1: B protocol
+#define SCREEN_MAX_X (600)
+#define SCREEN_MAX_Y (1024)
+//#define SCREEN_MAX_X (480)
+//#define SCREEN_MAX_Y (800)
+#define ICN85XX_I2C_SCL 400*1000
+
+#endif
+
+
+#include <linux/irq.h>
+#include <mach/irqs.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#define CTP_RST_PORT 4//RK30_PIN1_PB1
+#define CTP_IRQ_PORT 7
+#define CTP_REPORT_PROTOCOL 1 //0: A protocol
+#define SCREEN_MAX_X (1024)
+#define SCREEN_MAX_Y (600)
+#define ICN85XX_IIC_ADDR (0x90>>1)
+
+//-----------------------------------------------------------------------------
+// Global CONSTANTS
+//-----------------------------------------------------------------------------
+#define COL_NUM 24
+#define ROW_NUM 36
+
+#define MD25D40_ID1 0x514013
+#define MD25D40_ID2 0xC84013
+#define MD25D20_ID1 0x514012
+#define MD25D20_ID2 0xC84012
+#define GD25Q10_ID 0xC84011
+#define MX25L512E_ID 0xC22010
+
+#define ICN85XX_WITHOUT_FLASH 0x11
+#define ICN85XX_WITH_FLASH 0x22
+
+#define FLASH_TOTAL_SIZE 0x00010000
+#define FLASH_PAGE_SIZE 0x1000
+#define FLASH_AHB_BASE_ADDR 0x00100000
+#define FLASH_PATCH_PARA_BASE_ADDR (FLASH_TOTAL_SIZE - FLASH_PAGE_SIZE) // allocate 1 page for patch para, 0xff00
+#define FLASH_CODE_INFO_BASE_ADDR (FLASH_PATCH_PARA_BASE_ADDR - FLASH_PAGE_SIZE) // 0xfe00,allocate 1 page for system para
+#define FLASH_CRC_ADDR (FLASH_AHB_BASE_ADDR + FLASH_CODE_INFO_BASE_ADDR + 0x00) // 0xfe00
+#define FLASH_CODE_LENGTH_ADDR (FLASH_AHB_BASE_ADDR + FLASH_CODE_INFO_BASE_ADDR + 0x04) // 0xfe04
+
+
+//tp config
+#define TOUCH_VIRTUAL_KEYS 0
+#define SUPPORT_PROC_FS 1
+#define SUPPORT_SYSFS 1
+#define COMPILE_FW_WITH_DRIVER 0
+#define FORCE_UPDATA_FW 0
+#define SUSPEND_POWER_OFF 1
+#define SUPPORT_SENSOR_ID 0
+
+#define ICN85XX_NAME "icn85xx"
+#define ICN85XX_PROG_IIC_ADDR (0x30)
+#define CTP_NAME ICN85XX_NAME
+
+#define CTP_RESET_LOW_PERIOD (5)
+#define CTP_RESET_HIGH_PERIOD (100)
+#define CTP_WAKEUP_LOW_PERIOD (20)
+#define CTP_WAKEUP_HIGH_PERIOD (50)
+#define CTP_POLL_TIMER (16) /* ms delay between samples */
+#define CTP_START_TIMER (100) /* ms delay between samples */
+
+#define POINT_NUM 5
+#define POINT_SIZE 7
+
+#define TS_KEY_HOME 102
+#define TS_KEY_MENU 139
+#define TS_KEY_BACK 158
+#define TS_KEY_SEARCH 217
+
+#define ICN_VIRTUAL_BUTTON_HOME 0x02
+#define ICN_VIRTUAL_BUTTON_MENU 0x01
+#define ICN_VIRTUAL_BUTTON_BACK 0x04
+#define ICN_VIRTUAL_BUTTON_SEARCH 0x08
+
+#define IIC_RETRY_NUM 3
+
+//ICN85xx_REG_PMODE
+#define PMODE_ACTIVE 0x00
+#define PMODE_MONITOR 0x01
+#define PMODE_HIBERNATE 0x02
+
+#define B_SIZE 32
+//#define ENABLE_BYTE_CHECK
+
+//-----------------------------------------------------------------------------
+// Macro DEFINITIONS
+//-----------------------------------------------------------------------------
+#define DBG_ICN85xx_TRACE
+//#define DBG_ICN85xx_POINT
+//#define DBG_ICN85xx_INFO
+#define DBG_ICN85xx_ERROR
+//#define DBG_FLASH_INFO
+#define DBG_FLASH_ERROR
+#define DBG_OP_INFO
+#define DBG_OP_ERROR
+#define DBG_CALIB_INFO
+#define DBG_CALIB_ERROR
+//#define DBG_PROC_INFO
+#define DBG_PROC_ERROR
+
+
+#ifdef DBG_ICN85xx_TRACE
+#define icn85xx_trace(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn85xx_trace(fmt, args...) //
+#endif
+
+
+#ifdef DBG_ICN85xx_POINT
+#define icn85xx_point_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn85xx_point_info(fmt, args...) //
+#endif
+
+#ifdef DBG_ICN85xx_INFO
+#define icn85xx_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn85xx_info(fmt, args...) //
+#endif
+
+#ifdef DBG_ICN85xx_ERROR
+#define icn85xx_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define icn85xx_error(fmt, args...) //
+#endif
+
+#ifdef DBG_FLASH_INFO
+#define flash_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define flash_info(fmt, args...) //
+#endif
+
+#ifdef DBG_FLASH_ERROR
+#define flash_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define flash_error(fmt, args...) //
+#endif
+
+
+#ifdef DBG_OP_INFO
+#define op_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define op_info(fmt, args...) //
+#endif
+#ifdef DBG_OP_ERROR
+#define op_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define op_error(fmt, args...) //
+#endif
+
+
+#ifdef DBG_CALIB_INFO
+#define calib_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define calib_info(fmt, args...) //
+#endif
+
+#ifdef DBG_CALIB_ERROR
+#define calib_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define calib_error(fmt, args...) //
+#endif
+
+
+#ifdef DBG_PROC_INFO
+#define proc_info(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define proc_info(fmt, args...) //
+#endif
+
+#ifdef DBG_PROC_ERROR
+#define proc_error(fmt, args...) \
+ do{ \
+ printk(fmt, ##args); \
+ }while(0)
+#else
+#define proc_error(fmt, args...) //
+#endif
+
+#define swap_ab(a,b) {char temp;temp=a;a=b;b=temp;}
+#define U16LOBYTE(var) (*(unsigned char *) &var)
+#define U16HIBYTE(var) (*(unsigned char *)((unsigned char *) &var + 1))
+
+#define STRUCT_OFFSET(StructName,MemberName) ((int)(&(((StructName*)0)->MemberName)))
+
+
+//-----------------------------------------------------------------------------
+// Struct, Union and Enum DEFINITIONS
+//-----------------------------------------------------------------------------
+typedef struct _POINT_INFO
+{
+ unsigned char u8ID;
+ unsigned short u16PosX; // coordinate X, plus 4 LSBs for precision extension
+ unsigned short u16PosY; // coordinate Y, plus 4 LSBs for precision extension
+ unsigned char u8Pressure;
+ unsigned char u8EventId;
+}POINT_INFO;
+
+struct icn85xx_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct work_struct pen_event_work;
+ struct workqueue_struct *ts_workqueue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ struct hrtimer timer;
+ spinlock_t irq_lock;
+ struct semaphore sem;
+ int ictype;
+ int code_loaded_flag;
+ POINT_INFO point_info[POINT_NUM+1];
+ int point_num;
+ int irq;
+ int irq_is_disable;
+ int use_irq;
+ int work_mode;
+ int screen_max_x;
+ int screen_max_y;
+ int revert_x_flag;
+ int revert_y_flag;
+ int exchange_x_y_flag;
+};
+
+struct touch_param {
+ char fw_name[32];
+ int irqgpio;
+ int rstgpio;
+ int panelres_x;
+ int panelres_y;
+ int xyswap;
+ int xdir;
+ int ydir;
+ int max_finger_num;
+ int force_download;
+ int earlysus_en;
+ int dbg;
+ int lcd_exchg;
+};
+
+#pragma pack(1)
+typedef struct{
+ unsigned char wr; //write read flag£¬0:R 1:W
+ unsigned char flag; //0:
+ unsigned char circle; //polling cycle
+ unsigned char times; //plling times
+ unsigned char retry; //I2C retry times
+ unsigned int data_len; //data length
+ unsigned char addr_len; //address length
+ unsigned char addr[2]; //address
+ unsigned char* data; //data pointer
+}pack_head;
+
+typedef struct _STRUCT_PANEL_PARA
+{
+ unsigned short u16ResX; // Row of resolution
+ unsigned short u16ResY; // Col of resolution
+
+ unsigned char u8RowNum; // Row total number (Tp + vk)
+ unsigned char u8ColNum; // Column total number (Tp + vk)
+ unsigned char u8TXOrder[36]; // TX Order, start from zero
+ unsigned char u8RXOrder[24]; // TX Order, start from zero
+
+ unsigned char u8NumVKey; // Virtual Key setting
+ unsigned char u8VKeyMode;
+ unsigned char u8TpVkOrder[4];
+ unsigned char u8VKDownThreshold;
+ unsigned char u8VKUpThreshold;
+
+ unsigned char u8MaxTouchNum; // max touch support
+
+ unsigned char u8ScanMode; // scan mode
+ unsigned short u16BitFreq;
+ unsigned short u16FreqCycleNum[2];
+ unsigned char u8MultiDrvNum;
+ unsigned char u8WindowType;
+
+ unsigned char u8FreHopMode; // freq hopping
+ unsigned short u16FreHopBitFreq[5]; // Bit Freq
+ unsigned short u16FreqHopCycleNum[5]; // Cycle Num
+ unsigned short u16FreHopThreshold; // Threshold of Freq Hop
+
+ unsigned char u8ShiftNum; // rawdata level
+ unsigned char u8DrvOutPutR;
+ unsigned char u8PgaC;
+ unsigned char u8RxVcmi;
+ unsigned char u8DacGain;
+ unsigned char u8PgaGain;
+ unsigned char u8PgaR;
+ unsigned char u8SpaceHolder[200];
+}STRUCT_PANEL_PARA_H;
+
+#pragma pack()
+
+#define DATA_LENGTH_UINT 512
+#define CMD_HEAD_LENGTH (sizeof(pack_head) - sizeof(unsigned char *))
+#define ICN85xx_ENTRY_NAME "icn85xx_tool"
+
+
+enum icn85xx_ts_regs {
+ ICN85xx_REG_PMODE = 0x04, /* Power Consume Mode */
+};
+
+typedef enum
+{
+ R_OK = 100,
+ R_FILE_ERR,
+ R_STATE_ERR,
+ R_ERASE_ERR,
+ R_PROGRAM_ERR,
+ R_VERIFY_ERR,
+}E_UPGRADE_ERR_TYPE;
+
+//-----------------------------------------------------------------------------
+// Global VARIABLES
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Function PROTOTYPES
+//-----------------------------------------------------------------------------
+
+void icn85xx_ts_reset(void);
+int icn85xx_i2c_rxdata(unsigned short addr, char *rxdata, int length);
+int icn85xx_i2c_txdata(unsigned short addr, char *txdata, int length);
+int icn85xx_write_reg(unsigned short addr, char para);
+int icn85xx_read_reg(unsigned short addr, char *pdata);
+int icn85xx_prog_i2c_rxdata(unsigned int addr, char *rxdata, int length);
+int icn85xx_prog_i2c_txdata(unsigned int addr, char *txdata, int length);
+int icn85xx_prog_write_reg(unsigned int addr, char para);
+int icn85xx_prog_read_reg(unsigned int addr, char *pdata);
+
+int icn85xx_readVersion(void);
+void icn85xx_rawdatadump(short *mem, int size, char br);
+void icn85xx_set_fw(int size, unsigned char *buf);
+void icn85xx_memdump(char *mem, int size);
+int icn85xx_checksum(int sum, char *buf, unsigned int size);
+int icn85xx_update_status(int status);
+int icn85xx_get_status(void);
+int icn85xx_open_fw( char *fw);
+void icn85xx_free_fw(void);
+int icn85xx_read_fw(int offset, int length, char *buf);
+int icn85xx_close_fw(void);
+int icn85xx_goto_progmode(void);
+int icn85xx_check_progmod(void);
+int icn85xx_read_flashid(void);
+int icn85xx_erase_flash(void);
+int icn85xx_prog_buffer(unsigned int flash_addr,unsigned int sram_addr,unsigned int copy_length,unsigned char program_type);
+int icn85xx_prog_data(unsigned int flash_addr, unsigned int data);
+void icn85xx_read_flash(unsigned int sram_address,unsigned int flash_address,unsigned long copy_length,unsigned char i2c_wire_num);
+int icn85xx_fw_download(unsigned int offset, unsigned char * buffer, unsigned int size);
+int icn85xx_bootfrom_flash(void);
+int icn85xx_bootfrom_sram(void);
+int icn85xx_crc_enable(unsigned char enable);
+unsigned int icn85xx_crc_calc(unsigned crc_in, char *buf, int len);
+
+short icn85xx_read_fw_Ver(char *fw);
+//E_UPGRADE_ERR_TYPE icn85xx_fw_update(char *fw);
+int icn85xx_fw_update(void *arg);
+#endif
+
diff --git a/drivers/input/touchscreen/icn85xx_ts/icn85xx_flash.c b/drivers/input/touchscreen/icn85xx_ts/icn85xx_flash.c
new file mode 100755
index 00000000..a17cf176
--- /dev/null
+++ b/drivers/input/touchscreen/icn85xx_ts/icn85xx_flash.c
@@ -0,0 +1,1050 @@
+/*++
+
+ Copyright (c) 2012-2022 ChipOne Technology (Beijing) Co., Ltd. All Rights Reserved.
+ This PROPRIETARY SOFTWARE is the property of ChipOne Technology (Beijing) Co., Ltd.
+ and may contains trade secrets and/or other confidential information of ChipOne
+ Technology (Beijing) Co., Ltd. This file shall not be disclosed to any third party,
+ in whole or in part, without prior written consent of ChipOne.
+ THIS PROPRIETARY SOFTWARE & ANY RELATED DOCUMENTATION ARE PROVIDED AS IS,
+ WITH ALL FAULTS, & WITHOUT WARRANTY OF ANY KIND. CHIPONE DISCLAIMS ALL EXPRESS OR
+ IMPLIED WARRANTIES.
+
+ File Name: icn85xx_flash.c
+ Abstract:
+ flash operation, read write etc.
+ Author: Zhimin Tian
+ Date : 08 14,2013
+ Version: 0.1[.revision]
+ History :
+ Change logs.
+ --*/
+#include "icn85xx.h"
+
+unsigned char* firmdata = NULL;
+int g_status = R_OK;
+static char fw_mode = 0;
+static int fw_size = 0;
+static unsigned char *fw_buf;
+static char boot_mode = ICN85XX_WITH_FLASH;
+void icn85xx_rawdatadump(short *mem, int size, char br)
+{
+ int i;
+ for(i=0;i<size; i++)
+ {
+ if((i!=0)&&(i%br == 0))
+ printk("\n");
+ printk(" %5d", mem[i]);
+ }
+ printk("\n");
+}
+
+void icn85xx_memdump(char *mem, int size)
+{
+ int i;
+ for(i=0;i<size; i++)
+ {
+ if(i%16 == 0)
+ printk("\n");
+ printk(" 0x%2x", mem[i]);
+ }
+ printk("\n");
+}
+
+int icn85xx_checksum(int sum, char *buf, unsigned int size)
+{
+ int i;
+ for(i=0; i<size; i++)
+ {
+ sum = sum + buf[i];
+ }
+ return sum;
+}
+
+
+int icn85xx_update_status(int status)
+{
+// flash_info("icn85xx_update_status: %d\n", status);
+ g_status = status;
+ return 0;
+}
+
+int icn85xx_get_status(void)
+{
+ return g_status;
+}
+
+void icn85xx_set_fw(int size, unsigned char *buf)
+{
+ fw_size = size;
+ fw_buf = buf;
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_open_fw
+Input : *fw
+
+Output : file size
+function : open the fw file, and return total size
+***********************************************************************************************/
+extern struct i2c_client *this_client;
+int icn85xx_open_fw( char *fw)
+{
+ static int file_size = 0;
+ const struct firmware *fw_entry;
+
+ if (firmdata)
+ return file_size;
+
+ if(request_firmware(&fw_entry, fw, &(this_client->dev))!=0) {
+ flash_error(KERN_ERR "cat't request firmware\n");
+ return -1;
+ }
+ if (fw_entry->size <= 0) {
+ flash_error(KERN_ERR "load firmware error\n");
+ release_firmware(fw_entry);
+ return -1;
+ }
+
+ firmdata = kzalloc(fw_entry->size + 1, GFP_KERNEL);
+ memcpy(firmdata, fw_entry->data, fw_entry->size);
+ file_size = fw_entry->size;
+ release_firmware(fw_entry);
+ return file_size;
+}
+
+void icn85xx_free_fw(void)
+{
+ if (firmdata) {
+ kfree(firmdata);
+ firmdata = NULL;
+ }
+}
+
+/***********************************************************************************************
+Name : icn85xx_read_fw
+Input : offset
+ length, read length
+ buf, return buffer
+Output :
+function : read data to buffer
+***********************************************************************************************/
+int icn85xx_read_fw(int offset, int length, char *buf)
+{
+ if(fw_mode == 1)
+ {
+ memcpy(buf, fw_buf+offset, length);
+ }
+ else
+ {
+ memcpy(buf, firmdata + offset, length);
+ }
+// icn85xx_memdump(buf, length);
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_close_fw
+Input :
+Output :
+function : close file
+***********************************************************************************************/
+int icn85xx_close_fw(void)
+{
+ if(fw_mode == 0)
+ {
+ //filp_close(fp, NULL);
+ }
+
+ return 0;
+}
+/***********************************************************************************************
+Name : icn85xx_readVersion
+Input : void
+Output :
+function : return version
+***********************************************************************************************/
+int icn85xx_readVersion(void)
+{
+ int err = 0;
+ char tmp[2];
+ short CurVersion;
+ err = icn85xx_i2c_rxdata(0x000c, tmp, 2);
+ if (err < 0) {
+ flash_error("%s failed: %d\n", __func__, err);
+ return 0;
+ }
+ CurVersion = (tmp[0]<<8) | tmp[1];
+ return CurVersion;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_goto_progmode
+Input :
+Output :
+function : change MCU to progmod
+***********************************************************************************************/
+int icn85xx_goto_progmode(void)
+{
+ int ret = -1;
+ int retry = 3;
+ unsigned char ucTemp;
+
+// flash_info("icn85xx_goto_progmode\n");
+ while(retry > 0)
+ {
+ ucTemp = 0x5a;
+ ret = icn85xx_prog_i2c_txdata(0xcc3355, &ucTemp,1);
+ mdelay(2);
+ ucTemp = 01;
+ ret = icn85xx_prog_i2c_txdata(0x040400, &ucTemp,1);
+ mdelay(2);
+ ret = icn85xx_check_progmod();
+ if(ret == 0)
+ return ret;
+
+ retry--;
+ mdelay(2);
+ }
+ printk("icn85xx_goto_progmode over\n");
+ if(retry == 0)
+ return -1;
+
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn85xx_check_progmod
+Input :
+Output :
+function : check if MCU at progmode or not
+***********************************************************************************************/
+int icn85xx_check_progmod(void)
+{
+ int ret;
+ unsigned char ucTemp = 0x0;
+ ret = icn85xx_prog_i2c_rxdata(0x040002, &ucTemp, 1);
+// flash_info("icn85xx_check_progmod: 0x%x\n", ucTemp);
+ if(ret < 0)
+ {
+ flash_error("icn85xx_check_progmod error, ret: %d\n", ret);
+ return ret;
+ }
+ if(ucTemp == 0x85)
+ return 0;
+ else
+ return -1;
+
+}
+
+unsigned char FlashState(unsigned char State_Index)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+
+ ucTemp[2]=0x08;
+ ucTemp[1]=0x10;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x4062c,ucTemp,3);
+
+ if(State_Index==0)
+ {
+ ucTemp[0]=0x05;
+ }
+ else if(State_Index==1)
+ {
+ ucTemp[0]=0x35;
+ }
+ icn85xx_prog_i2c_txdata(0x40630,ucTemp,1);
+
+ ucTemp[1]=0x00;
+ ucTemp[0]=0x01;
+ icn85xx_prog_i2c_txdata(0x40640,ucTemp,2);
+
+ ucTemp[0]=1;
+ icn85xx_prog_i2c_txdata(0x40644,ucTemp,1);
+ while(ucTemp[0])
+ {
+ icn85xx_prog_i2c_rxdata(0x40644,ucTemp,1);
+ }
+
+ icn85xx_prog_i2c_rxdata(0x40648,ucTemp,1);
+ return (unsigned char)(ucTemp[0]);
+}
+
+int icn85xx_read_flashid(void)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+ int flashid=0;
+
+ ucTemp[2]=0x08;
+ ucTemp[1]=0x10;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x4062c,ucTemp,3);
+
+ ucTemp[0]=0x9f;
+
+ icn85xx_prog_i2c_txdata(0x40630,ucTemp,1);
+
+ ucTemp[1]=0x00;
+ ucTemp[0]=0x03;
+ icn85xx_prog_i2c_txdata(0x40640,ucTemp,2);
+
+ ucTemp[0]=1;
+ icn85xx_prog_i2c_txdata(0x40644,ucTemp,1);
+ while(ucTemp[0])
+ {
+ icn85xx_prog_i2c_rxdata(0x40644,ucTemp,1);
+ }
+
+ icn85xx_prog_i2c_rxdata(0x40648,(char *)&flashid,4);
+ flashid=flashid&0x00ffffff;
+
+ if((MD25D40_ID1 == flashid) || (MD25D40_ID2 == flashid)
+ ||(MD25D20_ID1 == flashid) || (MD25D20_ID1 == flashid)
+ ||(GD25Q10_ID == flashid) || (MX25L512E_ID == flashid))
+ {
+ boot_mode = ICN85XX_WITH_FLASH;
+ //printk("ICN85XX_WITH_FLASH\n");
+ }
+ else
+ {
+ boot_mode = ICN85XX_WITHOUT_FLASH;
+ //printk("ICN85XX_WITHOUT_FLASH\n");
+ }
+
+ printk("flashid: 0x%x\n", flashid);
+ return flashid;
+}
+
+
+void FlashWriteEnable(void)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+
+ ucTemp[2]=0x00;
+ ucTemp[1]=0x10;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x4062c,ucTemp,3);
+
+ ucTemp[0]=0x06;
+ icn85xx_prog_i2c_txdata(0x40630,ucTemp,1);
+
+ ucTemp[0]=0x00;
+ ucTemp[1]=0x00;
+ icn85xx_prog_i2c_txdata(0x40640,ucTemp,2);
+
+ ucTemp[0]=1;
+ icn85xx_prog_i2c_txdata(0x40644,ucTemp,1);
+ while(ucTemp[0])
+ {
+ icn85xx_prog_i2c_rxdata(0x40644,ucTemp,1);
+ }
+
+ ucTemp[0]=FlashState(0);
+ while( (ucTemp[0]&0x02)!=0x02)
+ {
+ ucTemp[0]=FlashState(0);
+ }
+}
+
+#ifndef QUAD_OUTPUT_ENABLE
+void ClearFlashState(void)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+ icn85xx_prog_i2c_rxdata(0x40603,ucTemp,1);
+ ucTemp[0]=(ucTemp[0]|0x20);
+ icn85xx_prog_i2c_txdata(0x40603, ucTemp, 1 );
+
+ FlashWriteEnable();
+ ////////////////////////////write comd to flash
+ ucTemp[2]=0x00;
+ ucTemp[1]=0x10;
+ ucTemp[0]=0x10;
+ icn85xx_prog_i2c_txdata(0x4062c,ucTemp,3);
+
+ ucTemp[0]=0x01;
+ icn85xx_prog_i2c_txdata(0x40630,ucTemp,1);
+
+ ucTemp[0]=0x00;
+ ucTemp[1]=0x00;
+ icn85xx_prog_i2c_txdata(0x40640,ucTemp,2);
+
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x40638,ucTemp,1);
+
+ ucTemp[0]=1;
+ icn85xx_prog_i2c_txdata(0x40644,ucTemp,1);
+ while(ucTemp[0])
+ {
+ icn85xx_prog_i2c_rxdata(0x40644,ucTemp,1);
+ }
+ while(FlashState(0)&0x01);
+
+}
+#else
+void ClearFlashState(void)
+{
+}
+#endif
+
+
+void EarseFlash(unsigned char erase_index,ulong flash_addr)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+ FlashWriteEnable();
+ if(erase_index==0) //erase the chip
+ {
+ ucTemp[0]=0xc7;
+ icn85xx_prog_i2c_txdata(0x40630, ucTemp, 1 );
+ ucTemp[2]=0x00;
+ ucTemp[1]=0x10;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x4062c, ucTemp, 3 );
+ }
+ else if(erase_index==1) //erase 32k space of the flash
+ {
+ ucTemp[0]=0x52;
+ icn85xx_prog_i2c_txdata(0x40630, ucTemp, 1);
+ ucTemp[2]=0x00;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x4062c, ucTemp, 3);
+ }
+ else if(erase_index==2) //erase 64k space of the flash
+ {
+ ucTemp[0]=0xd8;
+ icn85xx_prog_i2c_txdata(0x40630, ucTemp,1);
+ ucTemp[2]=0x00;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x4062c, ucTemp, 3);
+ }
+ else if(erase_index==3)
+ {
+ ucTemp[0]=0x20;
+ icn85xx_prog_i2c_txdata(0x40630, ucTemp, 1);
+ ucTemp[2]=0x00;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x4062c, ucTemp, 3);
+ }
+ ucTemp[2]=(unsigned char)(flash_addr>>16);
+ ucTemp[1]=(unsigned char)(flash_addr>>8);
+ ucTemp[0]=(unsigned char)(flash_addr);
+ icn85xx_prog_i2c_txdata(0x40634, ucTemp, 3);
+
+ ucTemp[1]=0x00;
+ ucTemp[0]=0x00;
+ icn85xx_prog_i2c_txdata(0x40640, ucTemp, 2 );
+
+ ucTemp[0]=1;
+ icn85xx_prog_i2c_txdata(0x40644, ucTemp, 1);
+ while(ucTemp[0])
+ {
+ icn85xx_prog_i2c_rxdata(0x40644,ucTemp,1);
+ }
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_erase_flash
+Input :
+Output :
+function : erase flash
+***********************************************************************************************/
+int icn85xx_erase_flash(void)
+{
+ ClearFlashState();
+ while(FlashState(0)&0x01);
+ FlashWriteEnable();
+ EarseFlash(1,0);
+ while((FlashState(0)&0x01));
+ FlashWriteEnable();
+ EarseFlash(3,0x8000); //?which block
+ while((FlashState(0)&0x01));
+ FlashWriteEnable();
+ EarseFlash(3,0x9000);
+ while((FlashState(0)&0x01));
+ FlashWriteEnable();
+ EarseFlash(3,0xe000);
+ while((FlashState(0)&0x01));
+ return 0;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_prog_buffer
+Input :
+Output :
+function : progm flash
+***********************************************************************************************/
+int icn85xx_prog_buffer(unsigned int flash_addr,unsigned int sram_addr,unsigned int copy_length,unsigned char program_type)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+ unsigned char prog_state=0;
+
+ unsigned int i=0;
+ unsigned char program_commond=0;
+ if(program_type == 0)
+ {
+ program_commond = 0x02;
+ }
+ else if(program_type == 1)
+ {
+ program_commond = 0xf2;
+ }
+ else
+ {
+ program_commond = 0x02;
+ }
+
+
+ for(i=0; i<copy_length; )
+ {
+ prog_state=(FlashState(0)&0x01);
+ while(prog_state)
+ {
+ prog_state=(FlashState(0)&0x01);
+ }
+ FlashWriteEnable();
+
+ ucTemp[2]=0;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0;
+ icn85xx_prog_i2c_txdata(0x4062c, ucTemp, 3);
+
+ ucTemp[2]=(unsigned char)(flash_addr>>16);
+ ucTemp[1]=(unsigned char)(flash_addr>>8);
+ ucTemp[0]=(unsigned char)(flash_addr);
+ icn85xx_prog_i2c_txdata(0x40634, ucTemp, 3);
+
+ ucTemp[2]=(unsigned char)(sram_addr>>16);
+ ucTemp[1]=(unsigned char)(sram_addr>>8);
+ ucTemp[0]=(unsigned char)(sram_addr);
+ icn85xx_prog_i2c_txdata(0x4063c, ucTemp, 3);
+
+ if(i+256<=copy_length)
+ {
+ ucTemp[1]=0x01;
+ ucTemp[0]=0x00;
+ }
+ else
+ {
+ ucTemp[1]=(unsigned char)((copy_length-i)>>8);
+ ucTemp[0]=(unsigned char)(copy_length-i);
+ }
+ icn85xx_prog_i2c_txdata(0x40640, ucTemp,2);
+
+ ucTemp[0]=program_commond;
+ icn85xx_prog_i2c_txdata(0x40630, ucTemp,1);
+
+ ucTemp[0]=0x01;
+ icn85xx_prog_i2c_txdata(0x40644, ucTemp,1);
+
+ flash_addr+=256;
+ sram_addr+=256;
+ i+=256;
+ while(ucTemp[0])
+ {
+ icn85xx_prog_i2c_rxdata(0x40644,ucTemp,1);
+ }
+
+ }
+
+ prog_state=(FlashState(0)&0x01);
+ while(prog_state)
+ {
+ prog_state=(FlashState(0)&0x01);
+ }
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn85xx_prog_data
+Input :
+Output :
+function : write int data to flash
+***********************************************************************************************/
+int icn85xx_prog_data(unsigned int flash_addr, unsigned int data)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+
+ ucTemp[3]=(unsigned char)(data>>24);
+ ucTemp[2]=(unsigned char)(data>>16);
+ ucTemp[1]=(unsigned char)(data>>8);
+ ucTemp[0]=(unsigned char)(data);
+
+ icn85xx_prog_i2c_txdata(0x7f00, ucTemp,4);
+ icn85xx_prog_buffer(flash_addr , 0x7f00, 0x04, 0);
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn85xx_read_flash
+Input :
+Output :
+function : read data from flash to sram
+***********************************************************************************************/
+void icn85xx_read_flash(unsigned int sram_address,unsigned int flash_address,unsigned long copy_length,unsigned char i2c_wire_num)
+{
+ unsigned char ucTemp[4] = {0,0,0,0};
+
+ if(i2c_wire_num==1)
+ {
+ ucTemp[2]=0x18;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0x00;
+ }
+ else if(i2c_wire_num==2)
+ {
+ ucTemp[2]=0x1a;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0x01;
+ }
+ else if(i2c_wire_num==4)
+ {
+ ucTemp[2]=0x19;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0x01;
+ }
+ else
+ {
+ ucTemp[2]=0x18;
+ ucTemp[1]=0x13;
+ ucTemp[0]=0x01;
+ }
+ icn85xx_prog_i2c_txdata(0x4062c, ucTemp,3);
+
+ if(i2c_wire_num==1)
+ {
+ ucTemp[0]=0x03;
+ }
+ else if(i2c_wire_num==2)
+ {
+ ucTemp[0]=0x3b;
+ }
+ else if(i2c_wire_num==4)
+ {
+ ucTemp[0]=0x6b;
+ }
+ else
+ {
+ ucTemp[0]=0x0b;
+ }
+ icn85xx_prog_i2c_txdata(0x40630, ucTemp,1);
+
+ ucTemp[2]=(unsigned char)(flash_address>>16);
+ ucTemp[1]=(unsigned char)(flash_address>>8);
+ ucTemp[0]=(unsigned char)(flash_address);
+ icn85xx_prog_i2c_txdata(0x40634, ucTemp,3);
+
+ ucTemp[2]=(unsigned char)(sram_address>>16);
+ ucTemp[1]=(unsigned char)(sram_address>>8);
+ ucTemp[0]=(unsigned char)(sram_address);
+ icn85xx_prog_i2c_txdata(0x4063c, ucTemp,3);
+
+ ucTemp[1]=(unsigned char)(copy_length>>8);
+ ucTemp[0]=(unsigned char)(copy_length);
+ icn85xx_prog_i2c_txdata(0x40640, ucTemp,2);
+
+ ucTemp[0]=0x01;
+
+ icn85xx_prog_i2c_txdata(0x40644, ucTemp,1);
+ while(ucTemp[0])
+ {
+ icn85xx_prog_i2c_rxdata(0x40644,ucTemp,1);
+ }
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_read_fw_Ver
+Input : fw
+Output :
+function : read fw version
+***********************************************************************************************/
+
+short icn85xx_read_fw_Ver(char *fw)
+{
+ short FWversion;
+ char tmp[2];
+ int file_size;
+ file_size = icn85xx_open_fw(fw);
+ if(file_size < 0)
+ {
+ return -1;
+ }
+ icn85xx_read_fw(0x100, 2, &tmp[0]);
+
+ icn85xx_close_fw();
+ FWversion = (tmp[1]<<8)|tmp[0];
+// flash_info("FWversion: 0x%x\n", FWversion);
+ return FWversion;
+
+
+}
+
+/***********************************************************************************************
+Name : icn85xx_fw_download
+Input :
+Output :
+function : download code to sram
+***********************************************************************************************/
+int icn85xx_fw_download(unsigned int offset, unsigned char * buffer, unsigned int size)
+{
+#ifdef ENABLE_BYTE_CHECK
+ int i;
+ char testb[B_SIZE];
+#endif
+
+ icn85xx_prog_i2c_txdata(offset,buffer,size);
+#ifdef ENABLE_BYTE_CHECK
+ icn85xx_prog_i2c_rxdata(offset,testb,size);
+ for(i = 0; i < size; i++)
+ {
+ if(buffer[i] != testb[i])
+ {
+ flash_error("buffer[%d]:%x testb[%d]:%x\n",i,buffer[i],i,testb[i]);
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+/***********************************************************************************************
+Name : icn85xx_bootfrom_flash
+Input :
+Output :
+function :
+***********************************************************************************************/
+int icn85xx_bootfrom_flash(void)
+{
+ int ret = -1;
+ unsigned char ucTemp = 0x00;
+ flash_info("icn85xx_bootfrom_flash\n");
+
+ ucTemp=0x00;
+ ret = icn85xx_prog_i2c_txdata(0x40004, &ucTemp, 1 ); //nend flash sfcontrol clear
+ if (ret < 0) {
+ flash_error("%s failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+/***********************************************************************************************
+Name : icn85xx_bootfrom_sram
+Input :
+Output :
+function :
+***********************************************************************************************/
+int icn85xx_bootfrom_sram(void)
+{
+ int ret = -1;
+ unsigned char ucTemp = 0x03;
+ unsigned long addr = 0x40400;
+ flash_info("icn85xx_bootfrom_sram\n");
+ ret = icn85xx_prog_i2c_txdata(addr, &ucTemp, 1 ); //change bootmode from sram
+ return ret;
+}
+/***********************************************************************************************
+Name : icn85xx_crc_calc
+Input :
+Output :
+function :
+***********************************************************************************************/
+/*
+unsigned int icn85xx_crc_calc(unsigned crc_in, char *buf, int len)
+{
+ int pos;
+ unsigned int crc_result;
+ unsigned char in_data_8b;
+ unsigned int crc_reg_32b;
+ unsigned char i;
+ unsigned char xor_flag;
+
+ crc_result = crc_in;
+ for(pos=0;pos<len;pos++)
+ {
+ in_data_8b = *(buf+pos);
+ crc_reg_32b = crc_result ;
+ for(i=0; i<32; i++)
+ {
+ if(crc_reg_32b & 0x00000001)
+ {
+ crc_reg_32b ^= 0x04C11DB7;
+ crc_reg_32b >>= 1;
+ crc_reg_32b |= 0x80000000;
+ }
+ else
+ {
+ crc_reg_32b >>= 1;
+ }
+ }
+
+ for(i=0; i<40; i++)
+ {
+ xor_flag = (crc_reg_32b>>31);
+ crc_reg_32b = (crc_reg_32b<<1) + (in_data_8b>>7);
+ in_data_8b = (in_data_8b<<1);
+ if(xor_flag)
+ {
+ crc_reg_32b = crc_reg_32b ^ 0x04C11DB7;
+ }
+ }
+ crc_result = crc_reg_32b;
+ }
+ return crc_result;
+}
+*/
+
+
+/*
+ This polynomial (0x04c11db7) is used at: AUTODIN II, Ethernet, & FDDI
+*/
+static unsigned int crc32table[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+ 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+ 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+ 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+ 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+ 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+ 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+ 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+ 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+ 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+ 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+ 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+ 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+ 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+ 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+ 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+ 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+unsigned int icn85xx_crc_calc(unsigned crc_in, char *buf, int len)
+{
+ int i;
+ unsigned int crc = crc_in;
+ for(i = 0; i < len; i++)
+ crc = (crc << 8) ^ crc32table[((crc >> 24) ^ *buf++) & 0xFF];
+ return crc;
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_crc_enable
+Input :
+Output :
+function :control crc control
+***********************************************************************************************/
+int icn85xx_crc_enable(unsigned char enable)
+{
+ unsigned char ucTemp;
+ int ret = 0;
+ if(enable==1)
+ {
+ ucTemp = 1;
+ ret = icn85xx_prog_i2c_txdata(0x40028, &ucTemp, 1 );
+ }
+ else if(enable==0)
+ {
+ ucTemp = 0;
+ ret = icn85xx_prog_i2c_txdata(0x40028, &ucTemp, 1 );
+ }
+ return ret;
+}
+/***********************************************************************************************
+Name : icn85xx_crc_check
+Input :
+Output :
+function :chec crc right or not
+***********************************************************************************************/
+int icn85xx_crc_check(unsigned int crc, unsigned int len)
+{
+ int ret;
+ unsigned int crc_len;
+ unsigned int crc_result;
+ unsigned char ucTemp[4] = {0,0,0,0};
+
+ ret= icn85xx_prog_i2c_rxdata(0x4002c, ucTemp, 4 );
+ crc_result = ucTemp[3]<<24 | ucTemp[2]<<16 | ucTemp[1] << 8 | ucTemp[0];
+// flash_info("crc_result: 0x%x\n", crc_result);
+
+ ret = icn85xx_prog_i2c_rxdata(0x40034, ucTemp, 2);
+ crc_len = ucTemp[1] << 8 | ucTemp[0];
+// flash_info("crc_len: %d\n", crc_len);
+
+ if((crc_result == crc) && (crc_len == len))
+ return 0;
+ else
+ {
+ flash_info("crc_fw: 0x%x\n", crc);
+ flash_info("crc_result: 0x%x\n", crc_result);
+ flash_info("crc_len: %d\n", crc_len);
+ return -1;
+ }
+
+}
+
+
+/***********************************************************************************************
+Name : icn85xx_fw_update
+Input : fw
+Output :
+function : upgrade fw
+***********************************************************************************************/
+int icn85xx_fw_update(void *arg)
+{
+ int file_size, last_length;
+ int j, num;
+ char temp_buf[B_SIZE];
+ unsigned int crc_fw;
+ char *fw = (char *)arg;
+
+ file_size = icn85xx_open_fw(fw);
+ if(file_size < 0)
+ {
+ icn85xx_update_status(R_FILE_ERR);
+ return R_FILE_ERR;
+ }
+ if(icn85xx_goto_progmode() != 0)
+ {
+ flash_error("icn85xx_goto_progmode() != 0 error\n");
+ return R_STATE_ERR;
+ }
+ msleep(1);
+ icn85xx_crc_enable(1);
+
+ num = file_size/B_SIZE;
+ crc_fw = 0;
+ for(j=0; j < num; j++)
+ {
+ icn85xx_read_fw(j*B_SIZE, B_SIZE, temp_buf);
+ crc_fw = icn85xx_crc_calc(crc_fw, temp_buf, B_SIZE);
+ if(icn85xx_fw_download(j*B_SIZE, temp_buf, B_SIZE) != 0)
+ {
+ flash_error("error j:%d\n",j);
+ icn85xx_update_status(R_PROGRAM_ERR);
+ icn85xx_close_fw();
+ return R_PROGRAM_ERR;
+ }
+ icn85xx_update_status(5+(int)(60*j/num));
+ }
+ last_length = file_size - B_SIZE*j;
+ if(last_length > 0)
+ {
+ icn85xx_read_fw(j*B_SIZE, last_length, temp_buf);
+ crc_fw = icn85xx_crc_calc(crc_fw, temp_buf, last_length);
+ if(icn85xx_fw_download(j*B_SIZE, temp_buf, last_length) != 0)
+ {
+ flash_error("error last length\n");
+ icn85xx_update_status(R_PROGRAM_ERR);
+ icn85xx_close_fw();
+ return R_PROGRAM_ERR;
+ }
+ }
+ icn85xx_close_fw();
+ icn85xx_update_status(65);
+// flash_info("crc_fw: 0x%x\n", crc_fw);
+// msleep(1);
+ icn85xx_crc_enable(0);
+ if(icn85xx_crc_check(crc_fw, file_size) != 0)
+ {
+ flash_info("down fw error, crc error\n");
+ return R_PROGRAM_ERR;
+ }
+ else
+ {
+ //flash_info("downoad fw ok, crc ok\n");
+ }
+ icn85xx_update_status(70);
+
+ if(ICN85XX_WITH_FLASH == boot_mode)
+ {
+ icn85xx_erase_flash();
+ // return R_PROGRAM_ERR;
+ icn85xx_update_status(75);
+
+ FlashWriteEnable();
+
+ icn85xx_prog_buffer( 0, 0, file_size,0);
+
+ icn85xx_update_status(85);
+
+ while((FlashState(0)&0x01));
+ FlashWriteEnable();
+
+ icn85xx_prog_data(FLASH_CRC_ADDR, crc_fw);
+ icn85xx_prog_data(FLASH_CRC_ADDR+4, file_size);
+
+ icn85xx_update_status(90);
+
+
+ icn85xx_crc_enable(1);
+ icn85xx_read_flash( 0, 0, file_size, 2);
+ icn85xx_crc_enable(0);
+ if(icn85xx_crc_check(crc_fw, file_size) != 0)
+ {
+ flash_info("read flash data error, crc error\n");
+ return R_PROGRAM_ERR;
+ }
+ else
+ {
+ flash_info("read flash data ok, crc ok\n");
+ }
+ while((FlashState(0)&0x01));
+ icn85xx_update_status(95);
+
+ //if(icn85xx_bootfrom_flash() == 0)
+ if(icn85xx_bootfrom_sram() == 0) //code already in ram
+ {
+ flash_error("icn85xx_bootfrom_flash error\n");
+ icn85xx_update_status(R_STATE_ERR);
+ return R_STATE_ERR;
+ }
+ }
+ else if(ICN85XX_WITHOUT_FLASH == boot_mode)
+ {
+ if(icn85xx_bootfrom_sram() == 0)
+ {
+ flash_error("icn85xx_bootfrom_sram error\n");
+ icn85xx_update_status(R_STATE_ERR);
+ return R_STATE_ERR;
+ }
+ }
+ msleep(50);
+ icn85xx_update_status(R_OK);
+ flash_info("icn85xx upgrade ok\n");
+ return R_OK;
+}
diff --git a/drivers/input/touchscreen/icn85xx_ts/icn85xx_fw.h b/drivers/input/touchscreen/icn85xx_ts/icn85xx_fw.h
new file mode 100755
index 00000000..757408c2
--- /dev/null
+++ b/drivers/input/touchscreen/icn85xx_ts/icn85xx_fw.h
@@ -0,0 +1,2517 @@
+const unsigned char icn85xx_fw[40235] = {
+ 0xb0,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0x36,0x16,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xda,0x15,0x00,0x00,0xf0,0x13,0x00,0x00,0x6e,0x13,0x00,0x00,0xc4,0x14,0x00,0x00,
+ 0xfc,0x13,0x00,0x00,0xb8,0x12,0x00,0x00,0x98,0x11,0x00,0x00,0x96,0x10,0x00,0x00,
+ 0xfc,0x14,0x00,0x00,0xd0,0x14,0x00,0x00,0x82,0x15,0x00,0x00,0x28,0x15,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0x38,0x16,0x00,0x00,0x3a,0x16,0x00,0x00,
+ 0x3e,0x16,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0x3c,0x16,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,
+ 0xe4,0x07,0x00,0x00,0xe4,0x07,0x00,0x00,0x40,0x16,0x00,0x00,0x56,0x16,0x00,0x00,
+ 0x00,0x27,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x05,0x00,0x08,0x00,0x0c,0x00,
+ 0x0f,0x00,0x13,0x00,0x17,0x00,0x1a,0x00,0x1e,0x00,0x22,0x00,0x26,0x00,0x2a,0x00,
+ 0x2e,0x00,0x32,0x00,0x37,0x00,0x3c,0x00,0x40,0x00,0x46,0x00,0x4b,0x00,0x50,0x00,
+ 0x56,0x00,0x5d,0x00,0x63,0x00,0x6b,0x00,0x73,0x00,0x7b,0x00,0x84,0x00,0x8e,0x00,
+ 0x99,0x00,0xa6,0x00,0xb4,0x00,0xc4,0x00,0xd6,0x00,0xeb,0x00,0x04,0x01,0x22,0x01,
+ 0x47,0x01,0x75,0x01,0xb1,0x01,0x02,0x02,0x77,0x02,0x2e,0x03,0x77,0x04,0x74,0x07,
+ 0x60,0x16,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7e,0x7e,0x7e,0x7e,
+ 0x7e,0x7d,0x7d,0x7d,0x7d,0x7c,0x7c,0x7c,0x7b,0x7b,0x7a,0x7a,0x79,0x79,0x78,0x78,
+ 0x77,0x77,0x76,0x76,0x75,0x75,0x74,0x73,0x73,0x72,0x71,0x71,0x70,0x6f,0x6e,0x6d,
+ 0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60,0x5f,
+ 0x5e,0x5d,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x53,0x52,0x51,0x50,0x4f,0x4d,0x4c,
+ 0x4b,0x4a,0x48,0x47,0x46,0x44,0x43,0x42,0x40,0x3f,0x3e,0x3c,0x3b,0x3a,0x38,0x37,
+ 0x35,0x34,0x32,0x31,0x30,0x2e,0x2d,0x2b,0x2a,0x28,0x27,0x25,0x24,0x22,0x21,0x1f,
+ 0x1e,0x1c,0x1b,0x19,0x17,0x16,0x14,0x13,0x11,0x10,0x0e,0x0d,0x0b,0x09,0x08,0x06,
+ 0x05,0x03,0x02,0x00,0xaa,0xa5,0x55,0x5a,0x5a,0xa5,0x66,0x6a,0x58,0x02,0x00,0x04,
+ 0x10,0x0a,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x0f,0x0d,0x0b,0x09,0x07,0x05,
+ 0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x14,0x0e,0x0c,0x0a,0x08,0x06,0x04,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
+ 0x16,0x00,0x00,0x00,0x1e,0x14,0x05,0x01,0xa0,0x0f,0x28,0x00,0x96,0x00,0x09,0x01,
+ 0x00,0x34,0x21,0x40,0x1f,0x58,0x1b,0xa8,0x16,0x88,0x13,0x3a,0x00,0x32,0x00,0x2d,
+ 0x00,0x2a,0x00,0x14,0x00,0x58,0x02,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x14,
+ 0x28,0x3c,0x50,0xc8,0x00,0x03,0x01,0x00,0x01,0x01,0x10,0x01,0x14,0x00,0x28,0x00,
+ 0x58,0x02,0x00,0xea,0x00,0x01,0x06,0x1e,0x0a,0x00,0x10,0x27,0x00,0x00,0x00,0x80,
+ 0x01,0x50,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x02,
+ 0x00,0x02,0x01,0x00,0x02,0x80,0x01,0x06,0x00,0x02,0x00,0x01,0x01,0x0a,0x10,0x20,
+ 0x03,0xc4,0xff,0x3c,0x00,0x64,0x01,0x01,0x01,0x00,0x1e,0x00,0x64,0x01,0x00,0x01,
+ 0x00,0x00,0x64,0x0a,0x00,0x01,0x14,0x01,0x07,0x00,0xd0,0x07,0x14,0x14,0x14,0x14,
+ 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,
+ 0x14,0x14,0x14,0x14,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x32,0x00,0x05,0x01,0x00,0x00,0x01,0x03,
+ 0x05,0x07,0x46,0x46,0x0f,0x01,0x00,0x00,0x01,0x32,0xe8,0x03,0x00,0x03,0x00,0x00,
+ 0x00,0x40,0x00,0x20,0x00,0x20,0x00,0xe0,0x00,0xe0,0x00,0x00,0x00,0xf0,0x00,0xf0,
+ 0x00,0xf0,0x00,0x10,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0xe8,
+ 0x00,0xf8,0x00,0x08,0x00,0xf8,0x00,0x08,0x00,0xf8,0x00,0xf0,0x00,0xf0,0x00,0x00,
+ 0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0xf0,0xab,0xfa,0x00,0xf0,0xab,0xfa,0x55,0x05,
+ 0x55,0x05,0x00,0xf0,0x55,0x05,0xab,0xfa,0xd8,0xf0,0x94,0xf7,0x36,0xf4,0xe5,0xf5,
+ 0x0d,0x05,0x79,0xed,0x43,0xf9,0x5e,0x03,0x51,0xfe,0x74,0xf9,0x2f,0xf2,0x46,0xff,
+ 0xe9,0xfa,0x5d,0xfc,0x8c,0x06,0xd1,0xed,0xba,0x00,0x17,0x05,0xa3,0x03,0xde,0xf4,
+ 0x37,0xfd,0x4d,0xef,0xd3,0xfb,0xf4,0x06,0xbd,0xe9,0x6f,0xfa,0x9b,0xfe,0xa6,0xf7,
+ 0xe9,0x0d,0x7a,0xf3,0x9a,0xf9,0x33,0xf7,0x66,0xfa,0xcd,0xf4,0x9a,0x05,0x33,0xf7,
+ 0x66,0x06,0xcd,0x00,0x9a,0xfd,0x33,0xfb,0x66,0x02,0xcd,0x00,0x55,0xf5,0x55,0xf5,
+ 0x55,0xf5,0x55,0xf5,0x00,0x00,0xff,0xff,0x55,0xf5,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x55,0xf5,0x00,0x00,0x00,0x00,0x2a,0x05,0x81,0xfa,0x9e,0xf8,0x66,0xf8,0x97,0xf8,
+ 0xb7,0x03,0xe6,0xf9,0x8f,0x02,0x27,0xf7,0x74,0xfd,0x30,0x03,0xe4,0x01,0x64,0xfd,
+ 0x7b,0xff,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x00,
+ 0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0xf8,0x00,0xf8,
+ 0x51,0x01,0xad,0xfc,0xaa,0xf9,0x2b,0xfb,0x6a,0xfa,0xcb,0xfa,0xf0,0xff,0xb3,0x02,
+ 0xa7,0xf6,0xad,0xfc,0x54,0x04,0x2b,0xfb,0x15,0x05,0xcb,0xfa,0xf0,0xff,0xb3,0x02,
+ 0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0xf8,
+ 0x00,0xf8,0x5b,0xff,0xf2,0xfa,0xbb,0xfb,0x07,0xf8,0x31,0xf9,0xc4,0x01,0x90,0xfa,
+ 0x59,0xfa,0x20,0x00,0xd1,0x02,0xcf,0xfb,0xf3,0x03,0x15,0xfe,0xb5,0x02,0x25,0xfc,
+ 0x28,0xfd,0xff,0x02,0x4a,0x02,0x9a,0xf9,0x9a,0xf9,0x00,0x00,0x9a,0xf9,0x00,0x00,
+ 0x9a,0xf9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9a,0xf9,0x9a,0xf9,0x00,0x00,
+ 0x9a,0xf9,0x9a,0xf9,0x00,0x00,0x00,0x00,0x9a,0xf9,0x9a,0xf9,0x13,0xfe,0x0f,0xfc,
+ 0xd8,0xfa,0x7b,0xfb,0xb4,0xfc,0x50,0x02,0x2b,0xfb,0x14,0x02,0xc4,0xf9,0xf4,0x01,
+ 0x89,0x03,0x34,0x00,0xa9,0x00,0x41,0xfa,0x08,0xfd,0xe5,0x01,0xee,0xfc,0xb0,0xf9,
+ 0xf4,0x01,0x68,0x01,0x01,0x01,0x01,0xff,0xff,0x01,0xff,0xff,0xff,0x01,0xff,0xff,
+ 0x01,0x01,0xff,0xff,0xff,0x01,0xff,0x01,0xff,0xff,0xff,0x01,0x01,0xff,0x01,0xff,
+ 0xff,0xff,0xff,0x01,0x01,0xff,0x01,0xff,0xff,0xff,0xff,0xff,0x01,0xff,0x01,0x01,
+ 0x01,0xff,0xff,0xff,0xff,0xff,0x01,0xff,0x01,0x01,0x01,0x01,0xff,0xff,0xff,0x01,
+ 0xff,0xff,0x01,0x01,0x01,0xff,0xff,0xff,0xff,0xff,0x01,0xff,0x01,0x01,0xff,0xff,
+ 0x01,0x01,0xff,0xff,0xff,0xff,0x01,0x01,0xff,0x01,0xff,0x01,0xff,0x01,0x01,0x01,
+ 0xff,0xff,0xff,0xff,0x01,0xff,0x01,0xff,0xff,0x01,0x01,0xff,0x01,0xff,0xff,0x01,
+ 0x01,0x01,0xff,0x01,0x01,0xff,0xff,0x01,0xff,0x01,0xff,0xff,0x01,0xff,0xff,0xff,
+ 0xff,0xff,0x01,0x01,0xff,0xff,0x01,0xff,0x01,0xff,0x01,0x01,0xff,0xff,0xff,0x01,
+ 0xff,0x01,0x01,0x01,0xff,0xff,0x01,0x01,0x01,0xff,0x01,0xff,0xff,0x01,0xff,0xff,
+ 0xff,0xff,0x01,0xff,0xff,0x01,0x01,0xff,0x01,0xff,0x01,0xff,0xff,0x01,0x01,0xff,
+ 0xff,0x01,0xff,0x01,0xff,0x01,0x01,0x01,0x01,0xff,0xff,0x01,0xff,0xff,0x01,0x01,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x01,0xff,0x01,0xff,0x01,0x01,0x01,0x01,0xff,
+ 0xff,0x01,0xff,0xff,0x01,0x01,0x00,0x00,0x01,0x02,0x04,0x08,0x00,0x01,0x02,0x02,
+ 0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x05,0x05,0x05,0x05,
+ 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x06,
+ 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
+ 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x07,0x07,0x07,
+ 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
+ 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
+ 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
+ 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
+ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x45,0x6e,0x74,0x65,
+ 0x72,0x20,0x44,0x75,0x6d,0x6d,0x79,0x20,0x45,0x78,0x63,0x65,0x70,0x74,0x69,0x6f,
+ 0x6e,0x20,0x48,0x61,0x6e,0x64,0x6c,0x65,0x72,0x21,0x20,0x00,0x6d,0x65,0x6d,0x6f,
+ 0x72,0x79,0x20,0x64,0x75,0x6d,0x70,0x3a,0x25,0x78,0x2c,0x25,0x64,0x0a,0x00,0x00,
+ 0x0a,0x00,0x00,0x00,0x25,0x78,0x20,0x00,0x25,0x32,0x64,0x3a,0x25,0x34,0x64,0x2c,
+ 0x25,0x34,0x64,0x2c,0x25,0x32,0x64,0x2c,0x25,0x32,0x64,0x7c,0x00,0x00,0x00,0x00,
+ 0x0a,0x00,0x00,0x00,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,
+ 0x43,0x44,0x45,0x46,0x00,0x00,0x00,0x00,0x41,0x64,0x64,0x72,0x3a,0x30,0x78,0x25,
+ 0x78,0x0a,0x00,0x00,0x25,0x35,0x64,0x2c,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,
+ 0x52,0x61,0x77,0x3a,0x54,0x78,0x3a,0x25,0x32,0x64,0x2c,0x52,0x78,0x3a,0x25,0x32,
+ 0x64,0x0a,0x00,0x00,0x42,0x61,0x73,0x65,0x3a,0x54,0x78,0x3a,0x25,0x32,0x64,0x2c,
+ 0x52,0x78,0x3a,0x25,0x32,0x64,0x0a,0x00,0x62,0x65,0x66,0x6f,0x72,0x65,0x20,0x64,
+ 0x63,0x0a,0x00,0x00,0x44,0x69,0x66,0x3a,0x54,0x78,0x3a,0x25,0x32,0x64,0x2c,0x52,
+ 0x78,0x3a,0x25,0x32,0x64,0x0a,0x00,0x00,0x61,0x66,0x74,0x65,0x72,0x20,0x64,0x63,
+ 0x66,0x69,0x6c,0x74,0x65,0x72,0x0a,0x00,0x54,0x6f,0x74,0x61,0x6c,0x20,0x4e,0x75,
+ 0x6d,0x3a,0x25,0x32,0x64,0x0a,0x00,0x00,0x30,0x78,0x25,0x34,0x78,0x2c,0x30,0x78,
+ 0x25,0x34,0x78,0x2c,0x25,0x64,0x2c,0x25,0x64,0x7c,0x00,0x00,0x75,0x38,0x50,0x6c,
+ 0x61,0x6d,0x53,0x75,0x6d,0x3a,0x25,0x64,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xb0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x0a,0x71,0x10,0x12,0x02,0x60,0x03,0x60,0x01,0x60,0x04,0x60,0x05,0x60,0x06,0x60,
+ 0x07,0x60,0x08,0x60,0x09,0x60,0x0a,0x60,0x0b,0x60,0x0c,0x60,0x0d,0x60,0x0e,0x60,
+ 0x0f,0x60,0x02,0x7f,0x03,0x7f,0xff,0xf7,0xfc,0xaf,0x00,0x00,0xf2,0x07,0x00,0x00,
+ 0xec,0x88,0x00,0x00,0x70,0x24,0x00,0x9f,0x0d,0x72,0x0d,0x7f,0x00,0x8f,0x70,0x20,
+ 0xcf,0x00,0x70,0x24,0x00,0x9f,0x0b,0x77,0x0c,0x73,0x37,0x0f,0x04,0xe8,0x72,0x12,
+ 0x0b,0x74,0x74,0x05,0x0b,0x7f,0x0b,0x74,0x0c,0x77,0x74,0x0f,0x04,0xe8,0x72,0x12,
+ 0x03,0x60,0x74,0x05,0x0a,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,0x8c,0x06,0x00,0x00,
+ 0x7a,0x25,0x00,0x00,0x00,0x9e,0x00,0x00,0x60,0x89,0x00,0x00,0x04,0x9e,0x00,0x00,
+ 0x8c,0x82,0x00,0x00,0x52,0xaa,0x00,0x00,0x10,0x9e,0x00,0x00,0x22,0x83,0x00,0x00,
+ 0x70,0x24,0x00,0x9f,0x12,0x60,0x69,0x7f,0x6a,0x77,0x07,0xa7,0x07,0x2a,0x29,0xe0,
+ 0x69,0x76,0x06,0x84,0x69,0x76,0x06,0x83,0x69,0x75,0x05,0x86,0xc6,0x30,0xd6,0x30,
+ 0x05,0x96,0x67,0x75,0x68,0x76,0x06,0x95,0x36,0x60,0x67,0x75,0x05,0xb6,0x67,0x75,
+ 0x05,0x97,0x67,0x75,0x05,0x97,0x35,0x12,0x65,0x01,0x66,0x77,0x07,0xd5,0x66,0x77,
+ 0x07,0xb6,0x66,0x77,0x07,0x94,0x16,0x60,0x66,0x77,0x07,0xb6,0x66,0x77,0x07,0xb6,
+ 0x76,0x12,0x06,0xa7,0x07,0x2a,0xfd,0xe7,0x62,0x76,0x06,0xb7,0x36,0x60,0x54,0x77,
+ 0x07,0xb6,0x00,0x8f,0x70,0x20,0xcf,0x00,0xcf,0x00,0x46,0x60,0x5f,0x77,0x07,0x96,
+ 0x26,0x60,0x5e,0x77,0x07,0xb6,0xcf,0x00,0x70,0x24,0x00,0x9f,0x23,0x12,0x43,0x01,
+ 0x5c,0x77,0x07,0xa7,0x07,0x2a,0x0e,0xe0,0x03,0x2a,0x5a,0x77,0x05,0xe8,0x07,0xa3,
+ 0x32,0x60,0x03,0x2a,0x05,0xe0,0x05,0xf0,0x07,0xa7,0x32,0x60,0x07,0x2a,0x01,0xe0,
+ 0x13,0x60,0x55,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,0x54,0x76,0x06,0x87,0x07,0x34,
+ 0x17,0x34,0x06,0x97,0x53,0x76,0x06,0x87,0xe7,0x35,0x06,0x97,0xcf,0x00,0x4f,0x76,
+ 0x06,0x87,0x07,0x30,0x17,0x30,0x06,0x97,0x4e,0x76,0x06,0x87,0xe7,0x31,0x06,0x97,
+ 0xcf,0x00,0x70,0x24,0x00,0x9f,0x4b,0x77,0x07,0xa2,0x4b,0x77,0x72,0x03,0x4b,0x73,
+ 0x4c,0x7f,0x4c,0x77,0x07,0x92,0x00,0x8f,0x70,0x20,0xcf,0x00,0x70,0x24,0x00,0x9f,
+ 0x23,0x12,0x43,0x01,0x36,0x12,0x36,0x25,0x46,0x01,0x47,0x77,0x67,0x0c,0x01,0xe0,
+ 0x03,0x65,0x42,0x72,0x46,0x7f,0x40,0x77,0x72,0x03,0x40,0x73,0x41,0x7f,0x44,0x77,
+ 0x07,0x92,0x00,0x8f,0x70,0x20,0xcf,0x00,0x43,0x76,0x06,0x87,0x07,0x34,0x17,0x34,
+ 0x06,0x97,0x37,0x76,0x06,0x87,0x40,0x75,0x57,0x1e,0x06,0x97,0x16,0x60,0x3f,0x77,
+ 0x37,0xb6,0xcf,0x00,0x3c,0x76,0x06,0x87,0x07,0x30,0x17,0x30,0x06,0x97,0x30,0x76,
+ 0x06,0x87,0xf7,0x31,0x06,0x97,0xcf,0x00,0x70,0x24,0x00,0x9f,0x39,0x77,0x07,0xa2,
+ 0x39,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,0x70,0x24,0x00,0x9f,0x02,0x60,0x36,0x7f,
+ 0x32,0x60,0x36,0x7f,0x32,0x60,0x13,0x60,0x36,0x7f,0x02,0x60,0x36,0x7f,0x36,0x7f,
+ 0x37,0x7f,0x37,0x7f,0x06,0x60,0x37,0x77,0x07,0xd6,0x37,0x7f,0x38,0x7f,0x00,0x8f,
+ 0x70,0x20,0xcf,0x00,0x03,0x01,0x07,0x60,0x02,0xf0,0x06,0xb1,0x07,0x20,0x26,0x12,
+ 0x76,0x1c,0x47,0x0f,0xfa,0xe7,0xcf,0x00,0x63,0x01,0x07,0x60,0x03,0xf0,0x02,0xd3,
+ 0x07,0x20,0x12,0x20,0x47,0x0f,0xfb,0xe7,0xcf,0x00,0x00,0x00,0xbc,0x1d,0x00,0x00,
+ 0x00,0x04,0x04,0x00,0x00,0xe0,0x10,0x00,0x04,0xe0,0x10,0x00,0x00,0x06,0x04,0x00,
+ 0x00,0x13,0x18,0x00,0x2c,0x06,0x04,0x00,0x30,0x06,0x04,0x00,0x34,0x06,0x04,0x00,
+ 0x3c,0x06,0x04,0x00,0x40,0x06,0x04,0x00,0x2a,0x00,0x04,0x00,0x30,0x00,0x04,0x00,
+ 0x28,0x00,0x04,0x00,0x44,0x06,0x04,0x00,0x0c,0x00,0x04,0x00,0x60,0x01,0x04,0x00,
+ 0x3b,0xaa,0x00,0x00,0xc2,0x9c,0x00,0x00,0x0c,0x21,0x00,0x00,0x00,0x02,0x04,0x00,
+ 0x10,0x00,0x00,0xe0,0xc3,0x9c,0x00,0x00,0x80,0xc3,0xc9,0x01,0xe8,0x03,0x00,0x00,
+ 0xd0,0x83,0x00,0x00,0x04,0x02,0x04,0x00,0xb4,0x00,0x00,0x00,0x3e,0x84,0x00,0x00,
+ 0x14,0x02,0x04,0x00,0x10,0x02,0x04,0x00,0x00,0x00,0x00,0x80,0xb0,0xa8,0x00,0x00,
+ 0xc6,0x9c,0x00,0x00,0x2c,0x09,0x00,0x00,0xa8,0x20,0x00,0x00,0xc2,0x20,0x00,0x00,
+ 0xec,0x20,0x00,0x00,0xb8,0x08,0x00,0x00,0x2c,0x10,0x00,0x00,0x12,0x09,0x00,0x00,
+ 0x88,0x09,0x00,0x00,0x10,0x9e,0x00,0x00,0x34,0x16,0x00,0x00,0x7e,0x23,0x00,0x00,
+ 0x70,0x25,0x7a,0x00,0x2c,0x12,0x3b,0x12,0x4a,0x12,0x4a,0x01,0x6e,0x72,0xc3,0x12,
+ 0xb4,0x12,0x6d,0x7f,0x0d,0x60,0x0b,0xf0,0xd2,0x12,0xa3,0x12,0x6c,0x7f,0x02,0x2a,
+ 0x02,0xe0,0x6b,0x72,0x69,0x7f,0x6b,0x72,0x0e,0xa3,0x67,0x7f,0x0d,0x20,0xce,0x12,
+ 0xde,0x1c,0xbd,0x0f,0xf1,0xe7,0x66,0x72,0x64,0x7f,0x6a,0x00,0x70,0x21,0xcf,0x00,
+ 0x07,0x60,0x02,0xf0,0x00,0x12,0x07,0x20,0x27,0x0f,0xfc,0xe7,0xcf,0x00,0xf0,0x25,
+ 0x78,0x00,0x70,0x24,0x61,0x77,0x07,0xa6,0x61,0x75,0x55,0xa7,0x06,0x2a,0x05,0xe0,
+ 0x07,0x2a,0x4a,0xe8,0x55,0xb6,0x55,0xa7,0x47,0xf0,0x07,0x2a,0x16,0xe0,0x5c,0x75,
+ 0x05,0xa7,0x07,0x2a,0x5c,0x77,0x0f,0xe0,0x25,0xa5,0x05,0x2a,0x0c,0xe0,0x07,0xc5,
+ 0x05,0x20,0x65,0x01,0x07,0xd5,0x35,0x3e,0x56,0x0c,0x36,0xe0,0x17,0x60,0x53,0x76,
+ 0x56,0xb7,0x56,0xa7,0x31,0xf0,0x06,0x60,0x2e,0xf0,0x50,0x75,0x55,0xa7,0x17,0x2a,
+ 0x0e,0xe0,0x4f,0x77,0x07,0xa6,0x06,0x2a,0x03,0xe0,0x27,0xa7,0x07,0x2a,0x24,0xe8,
+ 0x06,0x60,0x4c,0x77,0x07,0xd6,0x49,0x77,0x57,0xb6,0x57,0xa7,0x1d,0xf0,0x47,0x76,
+ 0x56,0xa7,0x27,0x2a,0x19,0xe0,0x48,0x7e,0x0e,0xca,0x48,0x7d,0x0d,0xc9,0x48,0x7c,
+ 0x0c,0xc8,0x48,0x7b,0x0b,0xc7,0x00,0x97,0x02,0x60,0x47,0x7f,0x42,0x66,0x47,0x7f,
+ 0x0e,0xda,0x0d,0xd9,0x0c,0xd8,0x00,0x85,0x0b,0xd5,0x45,0x7f,0x07,0x60,0x3b,0x76,
+ 0x56,0xb7,0x06,0x60,0x3c,0x77,0x07,0xd6,0x70,0x20,0x68,0x00,0xf0,0x21,0xcf,0x00,
+ 0xe2,0x01,0x95,0x2c,0x52,0x16,0x3f,0x77,0x27,0x0c,0x3f,0x77,0x07,0xe0,0x72,0x14,
+ 0x97,0x32,0x72,0x1c,0x02,0xa2,0xf5,0x67,0x52,0x1c,0x17,0xf0,0x86,0x2c,0x26,0x0c,
+ 0x07,0xe0,0x72,0x1c,0x3a,0x77,0x72,0x1c,0x02,0xa7,0xf2,0x67,0x72,0x05,0x0d,0xf0,
+ 0xf6,0x67,0x26,0x0c,0x06,0xe0,0x27,0x05,0x85,0x32,0x57,0x1c,0x07,0xa2,0x62,0x14,
+ 0x04,0xf0,0x72,0x1c,0x02,0xa2,0xf7,0x67,0x72,0x1c,0x42,0x01,0xcf,0x00,0xf0,0x24,
+ 0x7d,0x00,0x2d,0x12,0x6d,0x01,0x63,0x01,0x0d,0x2a,0x3e,0x12,0x7e,0x01,0x26,0xe8,
+ 0xe7,0x12,0xe7,0x1c,0xe7,0x1c,0x72,0x12,0x52,0x3c,0x72,0x1c,0x7d,0x01,0xe2,0x1c,
+ 0xd3,0x12,0x27,0x7f,0x25,0x12,0xe5,0x01,0x27,0x77,0x02,0x60,0xd6,0x62,0x07,0xc4,
+ 0x45,0x0d,0x05,0xe0,0x02,0x20,0x42,0x01,0x17,0x20,0x62,0x0f,0xf8,0xe7,0x0d,0x22,
+ 0x04,0xe0,0x0e,0x22,0x13,0xe8,0x20,0x77,0x03,0xf0,0xa7,0x65,0x0e,0x22,0x03,0xe0,
+ 0x27,0x05,0x72,0x12,0x01,0xf0,0x72,0x1c,0x42,0x01,0x08,0xf0,0xd2,0x62,0x0e,0x22,
+ 0x05,0xe8,0x03,0x2a,0x02,0xe8,0x19,0x72,0x01,0xf0,0x32,0x12,0x6d,0x00,0xf0,0x20,
+ 0xcf,0x00,0x00,0x00,0xac,0x06,0x00,0x00,0x7a,0x25,0x00,0x00,0x08,0x84,0x00,0x00,
+ 0xc0,0x06,0x00,0x00,0xc4,0x06,0x00,0x00,0xc7,0x9c,0x00,0x00,0x20,0xaa,0x00,0x00,
+ 0x70,0xa8,0x00,0x00,0x10,0x9e,0x00,0x00,0x1c,0x04,0x04,0x00,0x20,0x04,0x04,0x00,
+ 0x40,0x04,0x04,0x00,0x44,0x04,0x04,0x00,0x68,0x23,0x00,0x00,0xe0,0x0a,0x00,0x00,
+ 0x60,0x26,0x00,0x00,0x7f,0x01,0x00,0x00,0x64,0x01,0x00,0x00,0x00,0xff,0xff,0xff,
+ 0x3e,0x84,0x00,0x00,0x08,0x01,0x00,0x00,0xb4,0xff,0xff,0xff,0x87,0x00,0x00,0x00,
+ 0x72,0x01,0x22,0x03,0x73,0x01,0x33,0x03,0x23,0x1c,0xf5,0x32,0x02,0x60,0xf6,0x60,
+ 0x04,0x2d,0x17,0x60,0x37,0x0c,0x03,0xe8,0x32,0x12,0x62,0x01,0x0e,0xf0,0x27,0x12,
+ 0x27,0x1c,0x57,0x1c,0x67,0x1b,0x06,0x24,0x66,0x01,0x73,0x0c,0x03,0xe8,0x52,0x1c,
+ 0x62,0x01,0x73,0x05,0x15,0x3e,0x46,0x0f,0xf2,0xe7,0xcf,0x00,0x70,0x24,0x00,0x9f,
+ 0x28,0x77,0x07,0xa6,0x16,0x2e,0x06,0x2a,0x2f,0xe8,0x07,0xa6,0x26,0x75,0x56,0x16,
+ 0x07,0xb6,0x32,0x60,0x25,0x73,0x25,0x7f,0x32,0x60,0x25,0x73,0x24,0x7f,0x25,0x7f,
+ 0x87,0x2c,0x27,0x16,0x25,0x76,0x06,0xa5,0x06,0xb7,0x12,0x01,0x24,0x77,0x07,0xa5,
+ 0x07,0xb1,0x22,0x01,0x23,0x73,0x03,0xa5,0x03,0xb1,0x82,0x3f,0x22,0x74,0x04,0xa5,
+ 0x04,0xb2,0x06,0xa6,0x07,0xa5,0x85,0x3c,0x65,0x1e,0x03,0xa7,0x07,0x3d,0x57,0x1e,
+ 0x04,0xa3,0x83,0x3d,0x17,0x72,0x73,0x1e,0x1c,0x74,0x05,0x60,0x1c,0x7f,0x12,0x72,
+ 0x1c,0x73,0x44,0x60,0x05,0x60,0x19,0x7f,0x0e,0x77,0x07,0xa6,0x16,0x36,0x14,0xe8,
+ 0x07,0xa6,0x18,0x75,0x56,0x16,0x07,0xb6,0x32,0x60,0x17,0x73,0x0c,0x7f,0x32,0x60,
+ 0x17,0x73,0x0a,0x7f,0x16,0x72,0x16,0x73,0x17,0x74,0x05,0x60,0x10,0x7f,0x12,0x72,
+ 0x16,0x73,0x44,0x60,0x05,0x60,0x0d,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,0x00,0x00,
+ 0x3c,0xaa,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0xb0,0x00,0x00,0x3c,0x1f,0x00,0x00,
+ 0x00,0xa0,0x00,0x00,0x96,0x3b,0x00,0x00,0x33,0xaa,0x00,0x00,0x34,0xaa,0x00,0x00,
+ 0x35,0xaa,0x00,0x00,0x36,0xaa,0x00,0x00,0xc0,0x06,0x00,0x00,0x20,0x20,0x00,0x00,
+ 0xe4,0x01,0x00,0x00,0xfd,0x00,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0xc0,0x00,0x00,
+ 0x00,0x9c,0x00,0x00,0x23,0x01,0x00,0x00,0xe8,0x01,0x00,0x00,0x70,0x24,0x7e,0x00,
+ 0x0b,0x7e,0xe2,0x12,0x03,0x60,0x64,0x60,0x0a,0x7f,0x16,0x60,0x0e,0xb6,0xa7,0x60,
+ 0x2e,0xb7,0x07,0x60,0x1e,0xb7,0x3e,0xb6,0x5e,0xb7,0x06,0x72,0x03,0x60,0x04,0x64,
+ 0x04,0x7f,0x05,0x7f,0x06,0x7f,0x6e,0x00,0x70,0x20,0xcf,0x00,0xb0,0xa8,0x00,0x00,
+ 0xc4,0x09,0x00,0x00,0x70,0xa8,0x00,0x00,0xf8,0x16,0x00,0x00,0xf8,0x1a,0x00,0x00,
+ 0xf0,0x25,0x78,0x00,0x6d,0x75,0x55,0xa7,0x27,0x2a,0xd2,0xe8,0x85,0xa7,0x07,0x2a,
+ 0x0c,0xe8,0x6a,0x77,0x16,0x60,0x17,0xb6,0x06,0x60,0x07,0xb6,0x67,0xb6,0x77,0xb6,
+ 0x47,0xb6,0x57,0xb6,0x44,0x60,0x97,0xb4,0x85,0xb6,0x64,0x77,0x07,0xa6,0x06,0x2a,
+ 0x13,0xe0,0x17,0xa6,0x06,0x2a,0x10,0xe0,0x27,0xa6,0x06,0x2a,0x0d,0xe0,0x37,0xa2,
+ 0x02,0x2a,0x0a,0xe0,0x5f,0x7e,0x1e,0xb2,0x5f,0x7f,0x3e,0xa7,0x07,0x2a,0xfd,0xef,
+ 0x06,0x60,0x5b,0x77,0x37,0xb6,0xac,0xf0,0x5a,0x77,0x27,0xa6,0x07,0xa7,0xa6,0x2a,
+ 0x06,0xe0,0x17,0x2a,0x02,0xe0,0x58,0x7f,0x07,0xf0,0x58,0x7f,0x05,0xf0,0x17,0x2a,
+ 0x02,0xe0,0x57,0x7f,0x01,0xf0,0x57,0x7f,0x2b,0x12,0x51,0x77,0x07,0xa6,0x16,0x2a,
+ 0x02,0xe8,0x16,0x60,0x07,0xb6,0x07,0xa7,0x0a,0x60,0x17,0x2a,0x51,0xe0,0x4b,0x78,
+ 0x28,0xa7,0x0b,0xb7,0xbd,0x12,0x1d,0x20,0x50,0x7c,0xae,0x12,0xa9,0x12,0x0e,0x01,
+ 0x0c,0xa7,0x07,0x2a,0x38,0xe8,0x09,0x20,0x49,0x01,0xe6,0x12,0xe6,0x1c,0xe6,0x1c,
+ 0x66,0x1c,0x86,0x1c,0x65,0x12,0x0d,0xb1,0x46,0xa6,0x1d,0xb6,0x55,0xa6,0x2d,0xb6,
+ 0x65,0xa6,0x3d,0xb6,0x75,0xa6,0x4d,0xb6,0xc6,0x12,0x06,0x24,0x06,0xa6,0x5d,0xb6,
+ 0x6d,0xb7,0x6d,0x20,0x17,0x2a,0x03,0xe8,0x47,0x2a,0x04,0xe0,0x02,0xf0,0x7a,0x12,
+ 0x01,0xf0,0x1a,0x60,0x3e,0x75,0x55,0xa6,0x05,0x64,0x56,0x16,0x06,0x2a,0x13,0xe8,
+ 0xe6,0x12,0xe6,0x1c,0xe6,0x1c,0x66,0x1c,0x86,0x1c,0x46,0xaf,0x56,0xa4,0x84,0x3c,
+ 0x66,0xa1,0x76,0xa5,0x85,0x3c,0xc6,0x12,0x06,0x24,0x35,0x72,0xe3,0x12,0xf4,0x1e,
+ 0x15,0x1e,0x06,0xa6,0x34,0x7f,0x0e,0x20,0x5c,0x20,0x5e,0x2a,0xc0,0xe7,0x2f,0x77,
+ 0x57,0xa7,0x06,0x64,0x67,0x16,0x07,0x2a,0x02,0xe8,0x2f,0x72,0x2e,0x7f,0x1b,0xb9,
+ 0x23,0x77,0x27,0xa6,0x37,0xb6,0x22,0x77,0x22,0x7d,0x37,0xa6,0x06,0x2a,0xfc,0xef,
+ 0x07,0x60,0x3d,0xb7,0x4e,0x66,0x29,0x72,0x2a,0x7f,0x5d,0xa7,0x07,0x2a,0x04,0xe8,
+ 0x0e,0x24,0x4e,0x01,0x0e,0x2a,0xf7,0xe7,0x1a,0x77,0x27,0xa6,0xa6,0x2a,0x02,0xe0,
+ 0xb6,0x60,0x01,0xf0,0xa6,0x60,0x27,0xb6,0x16,0x7d,0x17,0x60,0x1d,0xb7,0x1a,0x2a,
+ 0x1c,0xe0,0x0d,0xa7,0x3e,0x60,0x0c,0x60,0x4d,0xbc,0x12,0x60,0x12,0x7f,0x1d,0x7f,
+ 0x03,0xf0,0x1d,0xa7,0x07,0x2a,0x0b,0xe8,0x4d,0xa7,0x07,0x2a,0xfa,0xef,0x02,0x60,
+ 0x0d,0x7f,0x16,0x72,0x17,0x7f,0x0e,0x24,0x4e,0x01,0x0e,0x2a,0xed,0xe7,0x02,0x60,
+ 0x09,0x7f,0x06,0x60,0x15,0x77,0x07,0x96,0x03,0xf0,0x12,0x60,0x06,0x7f,0x11,0x7f,
+ 0x68,0x00,0xf0,0x21,0xcf,0x00,0x00,0x00,0x20,0xaa,0x00,0x00,0x70,0xa8,0x00,0x00,
+ 0xb0,0xa8,0x00,0x00,0xb8,0x08,0x00,0x00,0xc2,0x17,0x00,0x00,0x06,0x1b,0x00,0x00,
+ 0xbe,0x17,0x00,0x00,0xfa,0x1a,0x00,0x00,0x79,0xa8,0x00,0x00,0x44,0xaa,0x00,0x00,
+ 0xc8,0x06,0x00,0x00,0x7a,0x25,0x00,0x00,0xe0,0x06,0x00,0x00,0xc8,0x00,0x00,0x00,
+ 0xe0,0x0a,0x00,0x00,0xea,0x08,0x00,0x00,0x18,0x02,0x04,0x00,0x89,0x76,0x06,0x87,
+ 0x57,0x30,0x07,0x31,0x06,0x97,0x36,0x60,0x87,0x77,0x07,0xb6,0x87,0x76,0x87,0x77,
+ 0x07,0xd6,0x87,0x76,0x88,0x77,0x07,0xd6,0x17,0x60,0x87,0x76,0x06,0xb7,0x87,0x76,
+ 0x06,0xb7,0x87,0x77,0x07,0xa6,0x35,0x60,0x56,0x1e,0x07,0xb6,0x54,0x60,0x85,0x76,
+ 0x06,0xb4,0x85,0x76,0x06,0xb5,0x07,0xa6,0x85,0x75,0x56,0x16,0x07,0xb6,0x84,0x76,
+ 0x85,0x77,0x07,0xd6,0x85,0x76,0x06,0x87,0x07,0x34,0x06,0x97,0x84,0x77,0x05,0x60,
+ 0x07,0xb5,0x17,0xb5,0x24,0x60,0x37,0xb4,0x27,0xb5,0x47,0xb5,0x06,0x87,0x80,0x75,
+ 0x57,0x1e,0x06,0x97,0xcf,0x00,0xf0,0x24,0x7c,0x00,0x7e,0x77,0x13,0x60,0x57,0xb3,
+ 0x7e,0x75,0x05,0xa5,0x05,0x2a,0x64,0xe8,0x7d,0x74,0x04,0xa6,0x16,0x2e,0x06,0x2a,
+ 0x03,0xe8,0x07,0xb3,0x26,0x60,0x05,0xf0,0x04,0xa4,0x14,0x36,0x04,0xe8,0x26,0x60,
+ 0x07,0xb6,0x72,0x77,0x37,0xb6,0x71,0x76,0x07,0x60,0x26,0xb7,0x36,0xa7,0x57,0x0f,
+ 0x4f,0xe0,0x04,0x60,0x73,0x7d,0x0d,0xa2,0x26,0xa3,0x53,0x0c,0x0f,0xe0,0x03,0x2a,
+ 0x03,0xe0,0x06,0xb2,0x07,0x60,0x09,0xf0,0x06,0xae,0x16,0xa7,0x87,0x3c,0xe7,0x1e,
+ 0x87,0x3c,0x27,0x1c,0x67,0x01,0x06,0xb7,0x87,0x3e,0x16,0xb7,0x64,0x7e,0x37,0x12,
+ 0x07,0x20,0x26,0xb7,0x04,0x20,0x44,0x01,0x54,0x0f,0xe5,0xe7,0x07,0x60,0x4e,0xb7,
+ 0x16,0x60,0x56,0x77,0x07,0xb6,0x5f,0x77,0x07,0xa7,0x8d,0x60,0x62,0x7c,0x17,0x2a,
+ 0x14,0xe0,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,0x5f,0x7f,0x0c,0xb2,0x0e,0xa6,
+ 0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,0x87,0x3e,0x1e,0xb7,
+ 0x0d,0x24,0x4d,0x01,0x0d,0x2a,0xed,0xe7,0x13,0xf0,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,
+ 0x72,0x1e,0x56,0x7f,0x0c,0xb2,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,
+ 0x67,0x01,0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x0d,0x24,0x4d,0x01,0x0d,0x2a,0xed,0xe7,
+ 0x47,0x77,0x47,0xa6,0x06,0x2a,0x04,0xe8,0x06,0x60,0x3c,0x75,0x05,0xb6,0x47,0xb6,
+ 0x0e,0x60,0x27,0xbe,0x02,0x60,0x4a,0x7f,0x43,0x77,0x17,0xa6,0x06,0x2a,0x01,0xe8,
+ 0x17,0xbe,0x6c,0x00,0xf0,0x20,0xcf,0x00,0x70,0x25,0x7b,0x00,0x3e,0x76,0x07,0x60,
+ 0x56,0xb7,0x3a,0x7e,0x17,0x60,0x4e,0xb7,0x06,0xb7,0x26,0x60,0x3e,0xb6,0x3a,0x7c,
+ 0x0c,0xac,0x0c,0x2a,0x28,0xe8,0x0d,0x60,0x3a,0x7b,0x0b,0xa3,0x2e,0xa7,0x3e,0xa6,
+ 0x67,0x0c,0x0b,0xe0,0x07,0x2a,0x02,0xe0,0x0e,0xb3,0x14,0xf0,0x0e,0xa6,0x1e,0xa7,
+ 0x87,0x3c,0x67,0x1e,0x87,0x3c,0x37,0x1c,0x0a,0xf0,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,
+ 0x72,0x1e,0x34,0x7f,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,
+ 0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x2e,0xa7,0x07,0x20,0x2e,0xb7,0x0d,0x20,0x4d,0x01,
+ 0xcd,0x0f,0xdb,0xe7,0x02,0xf0,0x19,0x76,0x06,0xb7,0x20,0x7e,0x2e,0xa6,0x3e,0xa7,
+ 0x76,0x0f,0x1a,0xe0,0x07,0x60,0x4e,0xb7,0x16,0x60,0x14,0x77,0x07,0xb6,0x8d,0x60,
+ 0x21,0x7c,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,0x1f,0x7f,0x0c,0xb2,0x0e,0xa6,
+ 0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,0x87,0x3e,0x1e,0xb7,
+ 0x0d,0x24,0x4d,0x01,0x0d,0x2a,0xed,0xe7,0x6b,0x00,0x70,0x21,0xcf,0x00,0x00,0x00,
+ 0x08,0x00,0x04,0x00,0x14,0x00,0x04,0x00,0x78,0x01,0x00,0x00,0x20,0x0e,0x04,0x00,
+ 0x48,0x01,0x00,0x00,0x24,0x0e,0x04,0x00,0x19,0x0e,0x04,0x00,0x1d,0x0e,0x04,0x00,
+ 0x00,0x0e,0x04,0x00,0x2c,0x0e,0x04,0x00,0x30,0x0e,0x04,0x00,0xfb,0x00,0x00,0x00,
+ 0x88,0x03,0x00,0x00,0x34,0x0e,0x04,0x00,0x10,0x00,0x00,0xe0,0x12,0x9e,0x00,0x00,
+ 0xfe,0x00,0x00,0x00,0xb0,0xa8,0x00,0x00,0x18,0x0e,0x04,0x00,0x0c,0x0e,0x04,0x00,
+ 0x14,0x0e,0x04,0x00,0x10,0x0e,0x04,0x00,0xc6,0x17,0x00,0x00,0x12,0x1b,0x00,0x00,
+ 0xb8,0x08,0x00,0x00,0x9a,0x18,0x00,0x00,0x70,0x25,0x7b,0x00,0x75,0x75,0x07,0x60,
+ 0x55,0xb7,0x74,0x7e,0x17,0x60,0x4e,0xb7,0x26,0x60,0x05,0xb6,0x3e,0xb6,0x72,0x7c,
+ 0x0c,0xac,0x0c,0x2a,0x28,0xe8,0x0d,0x60,0x71,0x7b,0x0b,0xa3,0x2e,0xa7,0x3e,0xa6,
+ 0x67,0x0c,0x0b,0xe0,0x07,0x2a,0x02,0xe0,0x0e,0xb3,0x14,0xf0,0x0e,0xa6,0x1e,0xa7,
+ 0x87,0x3c,0x67,0x1e,0x87,0x3c,0x37,0x1c,0x0a,0xf0,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,
+ 0x72,0x1e,0x67,0x7f,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,
+ 0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x2e,0xa7,0x07,0x20,0x2e,0xb7,0x0d,0x20,0x4d,0x01,
+ 0xcd,0x0f,0xdb,0xe7,0x02,0xf0,0x5f,0x76,0x06,0xb7,0x5a,0x7e,0x2e,0xa6,0x3e,0xa7,
+ 0x76,0x0f,0x1a,0xe0,0x07,0x60,0x4e,0xb7,0x16,0x60,0x5a,0x77,0x07,0xb6,0x8d,0x60,
+ 0x5a,0x7c,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,0x58,0x7f,0x0c,0xb2,0x0e,0xa6,
+ 0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,0x87,0x3e,0x1e,0xb7,
+ 0x0d,0x24,0x4d,0x01,0x0d,0x2a,0xed,0xe7,0x6b,0x00,0x70,0x21,0xcf,0x00,0x70,0x25,
+ 0x7b,0x00,0x4f,0x77,0x07,0xac,0x8c,0x28,0x4c,0x01,0x45,0x77,0x07,0xa6,0x16,0x2a,
+ 0x19,0xe0,0x12,0xf0,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,0x4a,0x7f,0x0b,0xb2,
+ 0x0d,0x20,0x4d,0x01,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,
+ 0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x03,0xf0,0x0d,0x60,0x3a,0x7e,0x3f,0x7b,0xcd,0x0f,
+ 0xe9,0xe7,0x1b,0xf0,0x07,0xa7,0x27,0x2a,0x18,0xe0,0x12,0xf0,0x0e,0xa2,0x1e,0xa7,
+ 0x87,0x3c,0x72,0x1e,0x3a,0x7f,0x0b,0xb2,0x0d,0x20,0x4d,0x01,0x0e,0xa6,0x1e,0xa7,
+ 0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x03,0xf0,
+ 0x0d,0x60,0x2c,0x7e,0x31,0x7b,0xcd,0x0f,0xe9,0xe7,0x6b,0x00,0x70,0x21,0xcf,0x00,
+ 0x70,0x24,0x00,0x9f,0x31,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,0x70,0x25,0x7a,0x00,
+ 0x2f,0x76,0x06,0xa7,0x17,0x2e,0x07,0x2a,0x07,0xe8,0x16,0x60,0x21,0x77,0x07,0xb6,
+ 0x26,0x60,0x20,0x77,0x37,0xb6,0x08,0xf0,0x06,0xa6,0x16,0x36,0x05,0xe8,0x27,0x60,
+ 0x1c,0x76,0x06,0xb7,0x1c,0x76,0x36,0xb7,0x1c,0x7c,0x0c,0xac,0x0d,0x60,0x1b,0x7b,
+ 0x19,0x7e,0x17,0x7a,0x28,0xf0,0x0b,0xa3,0x2e,0xa7,0x3e,0xa6,0x67,0x0c,0x0b,0xe0,
+ 0x07,0x2a,0x02,0xe0,0x0e,0xb3,0x19,0xf0,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,
+ 0x87,0x3c,0x37,0x1c,0x0f,0xf0,0x0a,0xa7,0x17,0x2a,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,
+ 0x72,0x1e,0x02,0xe0,0x17,0x7f,0x01,0xf0,0x0e,0x7f,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x2e,0xa7,0x07,0x20,
+ 0x2e,0xb7,0x0d,0x20,0x4d,0x01,0xcd,0x0f,0xd6,0xe7,0x6a,0x00,0x70,0x21,0xcf,0x00,
+ 0xb0,0xa8,0x00,0x00,0x12,0x9e,0x00,0x00,0x18,0x0e,0x04,0x00,0x14,0x0e,0x04,0x00,
+ 0x20,0x1b,0x00,0x00,0x1d,0x0e,0x04,0x00,0x10,0x0e,0x04,0x00,0x12,0x1b,0x00,0x00,
+ 0x1c,0x0e,0x04,0x00,0xc6,0x17,0x00,0x00,0x6e,0x13,0x00,0x00,0x0c,0x0e,0x04,0x00,
+ 0x9a,0x18,0x00,0x00,0x70,0x24,0x00,0x9f,0x4f,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,
+ 0x70,0x24,0x7e,0x00,0x4d,0x7e,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,0x4b,0x7f,
+ 0x4c,0x77,0x07,0xb2,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,
+ 0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x6e,0x00,0x70,0x20,0xcf,0x00,0x70,0x24,0x7e,0x00,
+ 0x42,0x7e,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,0x42,0x7f,0x41,0x77,0x07,0xb2,
+ 0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,0x87,0x3e,
+ 0x1e,0xb7,0x6e,0x00,0x70,0x20,0xcf,0x00,0x70,0x24,0x7e,0x00,0x16,0x60,0x3a,0x77,
+ 0x07,0xb6,0x35,0x7e,0x27,0x60,0x3e,0xb7,0x39,0x73,0x03,0xa3,0x2e,0xa5,0x16,0x60,
+ 0x56,0x0c,0x0e,0xe8,0x05,0x2a,0x04,0xe0,0x2e,0xb6,0x0e,0xb3,0x07,0x60,0x15,0xf0,
+ 0x2e,0xb7,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x87,0x3c,0x37,0x1c,0x0a,0xf0,
+ 0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,0x2e,0x7f,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,0x87,0x3e,0x1e,0xb7,0x6e,0x00,0x70,0x20,
+ 0xcf,0x00,0x70,0x24,0x7e,0x00,0x27,0x60,0x24,0x76,0x06,0xb7,0x1f,0x7e,0x3e,0xb7,
+ 0x23,0x73,0x03,0xa3,0x2e,0xa5,0x16,0x60,0x56,0x0c,0x0e,0xe8,0x05,0x2a,0x04,0xe0,
+ 0x2e,0xb6,0x0e,0xb3,0x07,0x60,0x15,0xf0,0x2e,0xb7,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x87,0x3c,0x37,0x1c,0x0a,0xf0,0x0e,0xa2,0x1e,0xa7,0x87,0x3c,0x72,0x1e,
+ 0x19,0x7f,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x20,0x67,0x01,0x0e,0xb7,
+ 0x87,0x3e,0x1e,0xb7,0x6e,0x00,0x70,0x20,0xcf,0x00,0x70,0x24,0x00,0x9f,0x12,0x77,
+ 0x07,0xa6,0x12,0x75,0x56,0x16,0x07,0xb6,0x07,0xa6,0x16,0x34,0x07,0xb6,0x10,0x7f,
+ 0x00,0x8f,0x70,0x20,0xcf,0x00,0x62,0x01,0x04,0x77,0x07,0xb2,0x82,0x3e,0x17,0xb2,
+ 0xcf,0x00,0x00,0x00,0xfc,0x13,0x00,0x00,0x12,0x9e,0x00,0x00,0xc6,0x17,0x00,0x00,
+ 0x10,0x0e,0x04,0x00,0x12,0x1b,0x00,0x00,0xb0,0xa8,0x00,0x00,0x14,0x0e,0x04,0x00,
+ 0x9a,0x18,0x00,0x00,0x20,0x1b,0x00,0x00,0x04,0x00,0x04,0x00,0xfd,0x00,0x00,0x00,
+ 0x2c,0x10,0x00,0x00,0xcf,0x00,0xcf,0x00,0xcf,0x00,0xcf,0x00,0xcf,0x00,0xcf,0x00,
+ 0x70,0x24,0x00,0x9f,0x22,0x7f,0x02,0x60,0x22,0x7f,0x16,0x60,0x22,0x77,0x47,0xb6,
+ 0x00,0x8f,0x70,0x20,0xcf,0x00,0x16,0x60,0x1f,0x77,0x37,0xb6,0x1f,0x77,0x07,0xa6,
+ 0x06,0x20,0x46,0x01,0x07,0xb6,0x1d,0x75,0x05,0xa5,0x56,0x0c,0x1e,0xe8,0x06,0x60,
+ 0x07,0xb6,0x1b,0x72,0x02,0xa6,0x1b,0x74,0x04,0xa7,0x87,0x3c,0x67,0x1e,0x1a,0x75,
+ 0x05,0xa3,0x03,0x3d,0x73,0x1e,0x19,0x76,0x06,0xa7,0x87,0x3d,0x37,0x1e,0x07,0x20,
+ 0x83,0x2c,0x73,0x16,0x02,0xa1,0x02,0xb3,0x17,0x01,0x04,0xa3,0x04,0xb1,0x27,0x01,
+ 0x05,0xa4,0x05,0xb1,0x87,0x3f,0x06,0xa5,0x06,0xb7,0x11,0x75,0x05,0xa4,0x11,0x76,
+ 0x06,0xa7,0x87,0x3c,0x47,0x1e,0x07,0x20,0x67,0x01,0x84,0x2c,0x74,0x16,0x05,0xa3,
+ 0x05,0xb4,0x87,0x3e,0x06,0xa5,0x06,0xb7,0xcf,0x00,0x00,0x00,0xfe,0x08,0x00,0x00,
+ 0xb8,0x08,0x00,0x00,0xb0,0xa8,0x00,0x00,0x17,0x9e,0x00,0x00,0xc6,0x9c,0x00,0x00,
+ 0x37,0xaa,0x00,0x00,0x38,0xaa,0x00,0x00,0x39,0xaa,0x00,0x00,0x3a,0xaa,0x00,0x00,
+ 0x40,0xaa,0x00,0x00,0x41,0xaa,0x00,0x00,0xf0,0x24,0x7d,0x00,0x91,0x7e,0xe2,0x12,
+ 0x03,0x60,0x24,0x62,0x90,0x7f,0x90,0x72,0x03,0x60,0x90,0x74,0x8e,0x7f,0x90,0x72,
+ 0x03,0x60,0x64,0x60,0x8c,0x7f,0x76,0x62,0x9e,0xb6,0x8e,0x77,0xae,0xb7,0x47,0x60,
+ 0xbe,0xb7,0xce,0xb6,0xee,0xb7,0x02,0x60,0x8c,0x7f,0x2e,0x12,0x42,0x60,0x8a,0x7f,
+ 0x2d,0x12,0x27,0x12,0x47,0x3c,0xe7,0x1e,0x47,0x01,0x88,0x76,0x06,0xb7,0x02,0x2a,
+ 0x05,0xe0,0x42,0x60,0x87,0x7f,0x42,0x60,0xd3,0x12,0x86,0x7f,0x7d,0x77,0x0e,0x60,
+ 0x67,0xbe,0x77,0xbe,0x1d,0x60,0x87,0xbd,0x84,0x7f,0x86,0x2c,0x26,0x16,0x83,0x77,
+ 0x07,0xa5,0x07,0xb6,0x12,0x01,0x82,0x77,0x07,0xa6,0x07,0xb1,0x22,0x01,0x81,0x77,
+ 0x07,0xa6,0x07,0xb1,0x82,0x3f,0x80,0x77,0x07,0xa6,0x07,0xb2,0x80,0x77,0x07,0xa6,
+ 0x07,0xbe,0x7f,0x77,0x07,0xa6,0x07,0xbe,0x7f,0x77,0x07,0xa6,0x07,0xbe,0x7e,0x77,
+ 0x07,0xa6,0x07,0xbe,0x7e,0x77,0x07,0xbe,0x7e,0x77,0x07,0xbe,0x6d,0x77,0x57,0xbe,
+ 0x26,0x62,0x7c,0x77,0x07,0xb6,0x6d,0x76,0x06,0xa6,0x27,0x62,0x76,0x0f,0x7a,0x77,
+ 0x02,0xe0,0x07,0xbd,0x01,0xf0,0x07,0xbe,0x6d,0x00,0xf0,0x20,0xcf,0x00,0x62,0x72,
+ 0xcf,0x00,0x76,0x72,0xcf,0x00,0x27,0x12,0x67,0x01,0xc6,0x2c,0x76,0x0c,0x06,0xe8,
+ 0x82,0x2c,0x16,0x62,0x76,0x0c,0x60,0xe8,0x5a,0x76,0x0f,0xf0,0xd6,0x2c,0x76,0x0c,
+ 0x14,0xe8,0x6f,0x75,0x57,0x1c,0x67,0x01,0x82,0x2c,0x6e,0x76,0x76,0x0c,0x54,0xe8,
+ 0x6e,0x76,0x26,0xa5,0xa5,0x2a,0x04,0xe0,0x54,0x76,0x76,0x1c,0x06,0xa2,0x4c,0xf0,
+ 0x26,0xa6,0xb6,0x2a,0x49,0xe0,0x65,0x76,0xf8,0xf7,0x68,0x76,0x76,0x0c,0x12,0xe8,
+ 0x57,0x75,0x05,0xa5,0x57,0x76,0x06,0xa6,0x86,0x3c,0x56,0x1e,0x56,0x75,0x05,0xa5,
+ 0x05,0x3d,0x65,0x1e,0x55,0x76,0x06,0xa6,0x86,0x3d,0x56,0x1e,0x61,0x75,0x57,0x1c,
+ 0x67,0x01,0xe3,0xf7,0xe6,0x2c,0x76,0x0c,0x06,0xe8,0x5e,0x76,0x67,0x1c,0x67,0x01,
+ 0x5e,0x75,0x57,0x1c,0x28,0xf0,0x76,0x12,0x76,0x01,0x82,0x2c,0xf6,0x37,0x24,0xe8,
+ 0x5b,0x76,0x76,0x0c,0x05,0xe8,0x5a,0x76,0x67,0x1c,0x67,0x01,0x5a,0x76,0x1a,0xf0,
+ 0x5a,0x76,0x76,0x0c,0x19,0xe0,0xc5,0x32,0x57,0x1c,0x67,0x01,0x47,0x2a,0x38,0x76,
+ 0x11,0xe0,0x06,0xa5,0x16,0xa7,0x87,0x3c,0x57,0x1e,0x26,0xa5,0x05,0x3d,0x75,0x1e,
+ 0x36,0xa7,0x87,0x3d,0x57,0x1e,0x51,0x76,0x67,0x0f,0x05,0xe0,0x15,0x60,0x50,0x76,
+ 0x06,0xb5,0x01,0xf0,0x67,0x1c,0x07,0xa2,0xcf,0x00,0x70,0x24,0x7e,0x00,0x62,0x01,
+ 0x3e,0x12,0x4e,0x01,0xc7,0x2c,0x27,0x0c,0xda,0xe8,0x17,0x62,0x27,0x0c,0xff,0xe8,
+ 0x22,0x2a,0x0b,0xe8,0x42,0x2a,0x0c,0xe8,0x02,0x2a,0xf9,0xe0,0x21,0x77,0x07,0xa6,
+ 0xe6,0x0f,0xf5,0xe8,0x16,0x60,0x17,0xb6,0xee,0xf0,0x1d,0x77,0x27,0xbe,0xef,0xf0,
+ 0x1e,0x2a,0x02,0xe0,0x40,0x7f,0xc1,0xf0,0x2e,0x2a,0x03,0xe0,0x19,0x77,0x57,0xbe,
+ 0xbc,0xf0,0x4e,0x2a,0x08,0xe0,0x3c,0x77,0x07,0xa7,0x37,0x2a,0xb6,0xe0,0x28,0x76,
+ 0x06,0xa7,0x17,0x34,0x86,0xf0,0xe6,0x12,0xf6,0x24,0x46,0x01,0x67,0x60,0x67,0x0c,
+ 0x03,0xe8,0xe2,0x12,0x36,0x7f,0xa9,0xf0,0x07,0x62,0x7e,0x0f,0x07,0xe0,0x16,0x60,
+ 0x34,0x77,0x07,0xb6,0x06,0x60,0x0a,0x77,0x27,0xb6,0x9f,0xf0,0x07,0x63,0x7e,0x0f,
+ 0x03,0xe0,0x16,0x60,0x2b,0x77,0x98,0xf0,0x57,0x65,0x16,0x60,0x7e,0x0f,0x04,0xe8,
+ 0x67,0x66,0x7e,0x0f,0x57,0xe0,0x06,0x60,0x02,0x77,0x77,0xb6,0x8e,0xf0,0x00,0x00,
+ 0x20,0xaa,0x00,0x00,0x22,0x83,0x00,0x00,0xc0,0xa8,0x00,0x00,0x60,0x01,0x00,0x00,
+ 0x44,0xaa,0x00,0x00,0x85,0xff,0xff,0xff,0xa4,0x21,0x00,0x00,0x30,0xaa,0x00,0x00,
+ 0xc2,0x20,0x00,0x00,0xec,0x20,0x00,0x00,0x96,0x3b,0x00,0x00,0x33,0xaa,0x00,0x00,
+ 0x34,0xaa,0x00,0x00,0x35,0xaa,0x00,0x00,0x36,0xaa,0x00,0x00,0x37,0xaa,0x00,0x00,
+ 0x38,0xaa,0x00,0x00,0x39,0xaa,0x00,0x00,0x3a,0xaa,0x00,0x00,0x3b,0xaa,0x00,0x00,
+ 0x3c,0xaa,0x00,0x00,0x3e,0xaa,0x00,0x00,0x3f,0xaa,0x00,0x00,0x30,0xa9,0x00,0x00,
+ 0x00,0xf0,0xff,0xff,0xb0,0x00,0x00,0x00,0xb0,0xa8,0x00,0x00,0xff,0x2f,0x00,0x00,
+ 0x00,0xe0,0xff,0xff,0x00,0xd0,0xff,0xff,0x00,0xe0,0x02,0x00,0xff,0x8f,0x00,0x00,
+ 0x00,0x80,0xff,0xff,0x00,0x9c,0x00,0x00,0xff,0xef,0x00,0x00,0xff,0xff,0x04,0x00,
+ 0x31,0xaa,0x00,0x00,0x60,0x26,0x00,0x00,0x18,0x9e,0x00,0x00,0x72,0x2d,0x00,0x00,
+ 0x32,0xaa,0x00,0x00,0x17,0x62,0x7e,0x0f,0x06,0xe0,0x07,0x60,0x32,0x76,0x06,0xb7,
+ 0x32,0x76,0x26,0xb7,0x32,0xf0,0x07,0x67,0x7e,0x0f,0x05,0xe0,0x30,0x76,0x06,0xa7,
+ 0x07,0x34,0x06,0xb7,0x2a,0xf0,0x2e,0x77,0x26,0x60,0x7e,0x0f,0x04,0xe8,0x2d,0x77,
+ 0x7e,0x0f,0x03,0xe0,0x06,0x60,0x2c,0x77,0x1f,0xf0,0x2c,0x77,0x16,0x60,0x7e,0x0f,
+ 0x04,0xe8,0x2b,0x77,0x7e,0x0f,0x03,0xe0,0x06,0x60,0x2a,0x77,0x15,0xf0,0x5e,0x2a,
+ 0x06,0xe0,0x16,0x60,0x29,0x77,0x07,0xb6,0x32,0x60,0x13,0x60,0x07,0xf0,0x6e,0x2a,
+ 0x07,0xe0,0x16,0x60,0x25,0x77,0x07,0xb6,0x32,0x60,0x03,0x60,0x24,0x7f,0x05,0xf0,
+ 0x7e,0x2a,0x03,0xe0,0x06,0x60,0x20,0x77,0x07,0xb6,0x21,0x77,0x24,0xf0,0xd7,0x2c,
+ 0x27,0x0c,0x25,0xe0,0xe7,0x2c,0x27,0x0c,0x22,0xe0,0x27,0x12,0xf7,0x36,0x1f,0xe8,
+ 0x1d,0x77,0x27,0x0c,0x05,0xe8,0x1c,0x77,0x72,0x1c,0x62,0x01,0x1c,0x77,0x15,0xf0,
+ 0x1c,0x77,0x27,0x0c,0x14,0xe0,0xc7,0x32,0x72,0x1c,0x62,0x01,0x42,0x2a,0x19,0x77,
+ 0x0c,0xe0,0x07,0xa6,0x17,0xa5,0x85,0x3c,0x65,0x1e,0x27,0xa6,0x06,0x3d,0x56,0x1e,
+ 0x37,0xa7,0x87,0x3d,0x67,0x1e,0x07,0xbe,0x02,0xf0,0x72,0x1c,0x02,0xbe,0x6e,0x00,
+ 0x70,0x20,0xcf,0x00,0x32,0xaa,0x00,0x00,0x20,0xaa,0x00,0x00,0x3c,0xaa,0x00,0x00,
+ 0x90,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x72,0x9c,0x00,0x00,0xa0,0x00,0x00,0x00,
+ 0xa1,0x00,0x00,0x00,0x11,0x9d,0x00,0x00,0x3b,0xaa,0x00,0x00,0x0c,0x21,0x00,0x00,
+ 0x18,0x9e,0x00,0x00,0xff,0x8f,0x00,0x00,0x00,0x80,0xff,0xff,0x00,0x9c,0x00,0x00,
+ 0xff,0xef,0x00,0x00,0x44,0xaa,0x00,0x00,0xcf,0x00,0x70,0x24,0x00,0x9f,0x0c,0x7f,
+ 0x00,0x8f,0x70,0x20,0xcf,0x00,0x70,0x24,0x00,0x9f,0x0a,0x7f,0x00,0x8f,0x70,0x20,
+ 0xcf,0x00,0x70,0x24,0x00,0x9f,0x62,0x01,0x08,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,
+ 0x70,0x24,0x00,0x9f,0x62,0x01,0x43,0x01,0x05,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,
+ 0xbe,0x17,0x00,0x00,0xc2,0x17,0x00,0x00,0xc6,0x17,0x00,0x00,0x9a,0x18,0x00,0x00,
+ 0x70,0x24,0x00,0x9f,0x09,0x77,0x07,0x86,0x09,0x77,0x09,0x72,0x76,0x0f,0x05,0xe0,
+ 0x09,0x73,0x09,0x74,0x15,0x60,0x09,0x7f,0x03,0xf0,0x09,0x73,0x07,0x74,0x09,0x7f,
+ 0x00,0x8f,0x70,0x20,0xcf,0x00,0x00,0x00,0x00,0xd0,0x10,0x00,0x5a,0xa5,0x66,0x6a,
+ 0x00,0x9c,0x00,0x00,0x00,0xc0,0x00,0x00,0x23,0x01,0x00,0x00,0xf6,0x1e,0x00,0x00,
+ 0xec,0x01,0x00,0x00,0x8c,0x82,0x00,0x00,0xf0,0x25,0x79,0x00,0x24,0x76,0x07,0x60,
+ 0x16,0xb7,0x56,0xb7,0x23,0x75,0x05,0xb7,0x26,0xb7,0x22,0x7f,0x23,0x72,0x23,0x7f,
+ 0x24,0x7f,0x24,0x7f,0x25,0x7f,0x25,0x7f,0x02,0x2a,0xfb,0xe7,0x25,0x7f,0x1b,0x7e,
+ 0x1c,0x79,0x24,0x7d,0x25,0x7c,0x25,0x7b,0x26,0x7a,0x26,0xf0,0x26,0x7f,0x26,0x7f,
+ 0x1d,0x7f,0x1d,0x7f,0x1e,0x7f,0x02,0x2a,0xfb,0xe7,0x24,0x7f,0x25,0x7f,0x09,0xa7,
+ 0x07,0x2a,0x13,0xe8,0x24,0x7f,0x87,0x2c,0x27,0x16,0x0d,0xa6,0x0d,0xb7,0x12,0x01,
+ 0x0c,0xa7,0x0c,0xb1,0x22,0x01,0x0b,0xa7,0x0b,0xb1,0x82,0x3f,0x0a,0xa7,0x0a,0xb2,
+ 0x17,0x60,0x2e,0xb7,0x2e,0xa7,0x07,0x2a,0xfd,0xe7,0x1b,0x7f,0x1c,0x7f,0x1c,0x7f,
+ 0x1d,0x72,0x1d,0x7f,0x1e,0x7f,0x1e,0x7f,0x0e,0xa7,0x07,0x2a,0xd7,0xef,0x1d,0x7f,
+ 0x16,0x60,0x02,0x77,0x17,0xb6,0x69,0x00,0xf0,0x21,0xcf,0x00,0x20,0xaa,0x00,0x00,
+ 0x32,0xaa,0x00,0x00,0x58,0x09,0x00,0x00,0x00,0x9c,0x00,0x00,0x8e,0x31,0x00,0x00,
+ 0x94,0x3b,0x00,0x00,0xd0,0x3c,0x00,0x00,0xea,0x3c,0x00,0x00,0x16,0x3c,0x00,0x00,
+ 0x7c,0x3c,0x00,0x00,0x33,0xaa,0x00,0x00,0x34,0xaa,0x00,0x00,0x35,0xaa,0x00,0x00,
+ 0x36,0xaa,0x00,0x00,0x8e,0x23,0x00,0x00,0x5e,0x31,0x00,0x00,0x70,0x3b,0x00,0x00,
+ 0xfc,0x0c,0x00,0x00,0x96,0x3b,0x00,0x00,0xf4,0x3c,0x00,0x00,0x5c,0x4f,0x00,0x00,
+ 0xf6,0x63,0x00,0x00,0x70,0xa8,0x00,0x00,0x3a,0x48,0x00,0x00,0x30,0x0e,0x00,0x00,
+ 0xee,0x0a,0x00,0x00,0x74,0x09,0x00,0x00,0xf0,0x25,0x79,0x00,0x02,0x60,0x34,0x7f,
+ 0x35,0x7e,0x0d,0x60,0x2e,0xbd,0x5e,0xbd,0x34,0x72,0x34,0x7f,0x1e,0xbd,0x34,0x7d,
+ 0x35,0x7c,0x35,0x7b,0x36,0x7a,0x19,0x60,0x20,0xf0,0x35,0x7f,0x36,0x7f,0x36,0x7f,
+ 0x37,0x7f,0x37,0x7f,0x02,0x2a,0xfb,0xe7,0x37,0x7f,0x37,0x7f,0x38,0x7f,0x87,0x2c,
+ 0x27,0x16,0x0d,0xa6,0x0d,0xb7,0x12,0x01,0x0c,0xa7,0x0c,0xb1,0x22,0x01,0x0b,0xa7,
+ 0x0b,0xb1,0x82,0x3f,0x0a,0xa7,0x0a,0xb2,0x2e,0xb9,0x04,0xf0,0x29,0x7f,0x0e,0xa7,
+ 0x17,0x2a,0x03,0xe0,0x2e,0xa7,0x07,0x2a,0xf9,0xe7,0x0e,0xa7,0x17,0x2a,0xdd,0xef,
+ 0x06,0x60,0x1c,0x77,0x27,0xb6,0x69,0x00,0xf0,0x21,0xcf,0x00,0xf0,0x25,0x78,0x00,
+ 0x02,0x60,0x17,0x7f,0x18,0x7e,0x0d,0x60,0x2e,0xbd,0x5e,0xbd,0x17,0x72,0x17,0x7f,
+ 0x1e,0xbd,0xed,0x12,0x23,0x7c,0x23,0x7b,0x24,0x7a,0x24,0x79,0x25,0x78,0x17,0xf0,
+ 0x18,0x7f,0x0c,0xbe,0x0b,0xa7,0x17,0x3e,0x47,0x34,0x0a,0xb7,0x09,0xbe,0x07,0x60,
+ 0x08,0xd7,0x20,0x76,0x21,0x77,0x07,0xd6,0x17,0x60,0x2d,0xb7,0x04,0xf0,0x10,0x7f,
+ 0x0d,0xa7,0x27,0x2a,0x03,0xe0,0x2d,0xa7,0x07,0x2a,0xf9,0xe7,0x13,0x7f,0x0d,0xae,
+ 0x2e,0x2a,0xe6,0xef,0x06,0x60,0x03,0x77,0x27,0xb6,0x68,0x00,0xf0,0x21,0xcf,0x00,
+ 0xa8,0x08,0x00,0x00,0x20,0xaa,0x00,0x00,0x00,0x9c,0x00,0x00,0x8e,0x31,0x00,0x00,
+ 0x33,0xaa,0x00,0x00,0x34,0xaa,0x00,0x00,0x35,0xaa,0x00,0x00,0x36,0xaa,0x00,0x00,
+ 0x8e,0x23,0x00,0x00,0x5e,0x31,0x00,0x00,0xd0,0x3c,0x00,0x00,0xea,0x3c,0x00,0x00,
+ 0x16,0x3c,0x00,0x00,0x70,0x3b,0x00,0x00,0xfc,0x0c,0x00,0x00,0x96,0x3b,0x00,0x00,
+ 0x5c,0x08,0x04,0x00,0x2a,0x9c,0x00,0x00,0x5d,0x08,0x04,0x00,0x74,0x08,0x04,0x00,
+ 0xa8,0x08,0x04,0x00,0xfc,0x7f,0x00,0x00,0xaa,0x08,0x04,0x00,0x02,0x01,0x78,0x76,
+ 0x06,0x87,0x78,0x74,0x47,0x1e,0x06,0x97,0x11,0x2a,0x77,0x77,0x07,0xa6,0x1a,0xe0,
+ 0x77,0x75,0x56,0x16,0x07,0xb6,0x76,0x76,0x06,0xa7,0x57,0x16,0x06,0xb7,0x75,0x76,
+ 0x06,0xa7,0x57,0x16,0x06,0xb7,0x74,0x77,0x07,0xb1,0x36,0x60,0x74,0x77,0x07,0xb6,
+ 0x74,0x75,0x05,0xa7,0x6e,0x74,0x47,0x16,0x05,0xb7,0x72,0x75,0x05,0xa7,0x47,0x16,
+ 0x05,0xb7,0x32,0xf0,0x21,0x2a,0x1a,0xe0,0x69,0x75,0x56,0x16,0x07,0xb6,0x68,0x76,
+ 0x06,0xa7,0x07,0x34,0x06,0xb7,0x67,0x76,0x06,0xa7,0x57,0x16,0x06,0xb7,0x16,0x60,
+ 0x66,0x77,0x07,0xb6,0x35,0x60,0x65,0x77,0x07,0xb5,0x65,0x75,0x05,0xa7,0x5f,0x74,
+ 0x47,0x16,0x05,0xb7,0x64,0x77,0x07,0xb6,0xb6,0x63,0x16,0xf0,0x41,0x2a,0x0d,0xe0,
+ 0x5b,0x75,0x56,0x16,0x07,0xb6,0x5a,0x77,0x07,0xa7,0x07,0x34,0x5a,0x76,0x06,0xb7,
+ 0x16,0x60,0x5c,0x77,0x07,0xb6,0xb6,0x66,0x07,0xf0,0x54,0x74,0x46,0x16,0x07,0xb6,
+ 0x16,0x60,0x58,0x77,0x07,0xb6,0xb6,0x60,0x58,0x77,0x07,0xb6,0x16,0x60,0x57,0x77,
+ 0x07,0x96,0xcf,0x00,0xc6,0x32,0x56,0x77,0x07,0x96,0x66,0x60,0x56,0x77,0x07,0xb6,
+ 0x06,0x60,0x55,0x77,0x07,0x96,0x55,0x77,0x16,0x60,0x07,0xb6,0x07,0xa6,0x06,0x2a,
+ 0xfd,0xe7,0xcf,0x00,0x02,0x01,0x52,0x76,0x4e,0x77,0x07,0x96,0x57,0x63,0x01,0x2a,
+ 0x01,0xe0,0x57,0x60,0x4c,0x76,0x06,0xb7,0x16,0x60,0x4b,0x77,0x07,0x96,0x4b,0x77,
+ 0x07,0xb6,0x07,0xa6,0x06,0x2a,0xfd,0xe7,0x4b,0x72,0x02,0xa2,0xcf,0x00,0x70,0x24,
+ 0x00,0x9f,0x49,0x7f,0x4a,0x76,0x42,0x77,0x07,0x96,0x16,0x60,0x42,0x77,0x07,0xb6,
+ 0x05,0x60,0x41,0x77,0x07,0x95,0x46,0x75,0x47,0x77,0x07,0xd5,0x40,0x77,0x07,0xb6,
+ 0x07,0xa6,0x06,0x2a,0xfd,0xe7,0x02,0x60,0x44,0x7f,0x12,0x2e,0x02,0x2a,0xfb,0xe7,
+ 0x00,0x8f,0x70,0x20,0xcf,0x00,0x05,0x01,0x11,0x2a,0x07,0xe8,0x21,0x2a,0x08,0xe8,
+ 0x41,0x2a,0x09,0xe0,0x3e,0x76,0xb7,0x66,0x08,0xf0,0x3d,0x76,0x37,0x60,0x05,0xf0,
+ 0x3d,0x76,0xb7,0x63,0x02,0xf0,0x3c,0x76,0xb7,0x60,0x2d,0x75,0x05,0x96,0x2d,0x76,
+ 0x06,0xb7,0x3a,0x77,0x07,0x93,0x3a,0x77,0x07,0x92,0x2b,0x77,0x07,0x94,0x2b,0x77,
+ 0x16,0x60,0x07,0xb6,0x07,0xa6,0x06,0x2a,0xfd,0xe7,0xcf,0x00,0xf0,0x24,0x7d,0x00,
+ 0x3d,0x12,0x2e,0x12,0x4e,0x01,0x28,0x7f,0x0e,0x2a,0x22,0x77,0x06,0xe0,0x31,0x76,
+ 0x07,0xb6,0xc6,0x32,0x1f,0x77,0x07,0x96,0x12,0xf0,0x1e,0x2a,0x06,0xe0,0x26,0x65,
+ 0x07,0xb6,0x2d,0x76,0x1b,0x77,0x07,0x96,0x08,0xf0,0x04,0x62,0x2e,0x2a,0x18,0x76,
+ 0x2a,0x75,0x01,0xe0,0x2a,0x74,0x07,0xb4,0x06,0x95,0x24,0x77,0x07,0x9d,0x06,0x60,
+ 0x16,0x77,0x07,0x96,0x16,0x77,0x16,0x60,0x07,0xb6,0x07,0xa6,0x06,0x2a,0xfd,0xe7,
+ 0x02,0x60,0x19,0x7f,0x12,0x2e,0x02,0x2a,0xfb,0xe7,0x6d,0x00,0xf0,0x20,0xcf,0x00,
+ 0x00,0x06,0x04,0x00,0x00,0x00,0x00,0x15,0x04,0x06,0x04,0x00,0xfe,0x00,0x00,0x00,
+ 0x08,0x06,0x04,0x00,0x0c,0x06,0x04,0x00,0x10,0x06,0x04,0x00,0x14,0x06,0x04,0x00,
+ 0x18,0x06,0x04,0x00,0x1c,0x06,0x04,0x00,0x20,0x06,0x04,0x00,0x28,0x06,0x04,0x00,
+ 0x2c,0x06,0x04,0x00,0x30,0x06,0x04,0x00,0x40,0x06,0x04,0x00,0x44,0x06,0x04,0x00,
+ 0x00,0x10,0x08,0x00,0x48,0x06,0x04,0x00,0x74,0x1e,0x00,0x00,0x20,0x10,0x00,0x00,
+ 0x02,0x02,0x00,0x00,0x38,0x06,0x04,0x00,0x94,0x1e,0x00,0x00,0x01,0x1d,0x18,0x00,
+ 0x00,0x13,0x18,0x00,0x01,0x1d,0x1a,0x00,0x01,0x13,0x18,0x00,0x34,0x06,0x04,0x00,
+ 0x3c,0x06,0x04,0x00,0xc7,0xff,0xff,0xff,0x00,0x13,0x00,0x00,0xd8,0xff,0xff,0xff,
+ 0xf0,0x25,0x78,0x00,0x70,0x24,0x38,0x12,0x4c,0x12,0x05,0x01,0x2a,0x60,0x01,0x2a,
+ 0x03,0xe8,0x11,0x2a,0x01,0xe0,0x74,0x7a,0x2b,0x12,0x0e,0x60,0x74,0x79,0x24,0xf0,
+ 0x02,0x60,0x73,0x7f,0x12,0x2e,0x02,0x2a,0xfb,0xe7,0x72,0x7f,0x73,0x77,0x73,0x76,
+ 0x06,0x97,0x73,0x76,0x06,0x9b,0x00,0x86,0x73,0x77,0x07,0x96,0x87,0x32,0xd7,0x1c,
+ 0x7c,0x0c,0x03,0xe8,0x87,0x32,0x09,0xd7,0x03,0xf0,0xcd,0x14,0x6d,0x01,0x09,0xdd,
+ 0x6e,0x77,0x07,0xba,0x6e,0x77,0x16,0x60,0x07,0xb6,0x86,0x32,0x6b,0x1c,0x07,0xa6,
+ 0x06,0x2a,0xfd,0xe7,0x87,0x32,0x7e,0x1c,0xe6,0x12,0x86,0x1c,0x00,0x96,0xed,0x12,
+ 0x6d,0x01,0xcd,0x0c,0xd5,0xef,0x02,0x60,0x5e,0x7f,0x12,0x2e,0x02,0x2a,0xfb,0xe7,
+ 0x70,0x20,0x68,0x00,0xf0,0x21,0xcf,0x00,0x02,0x01,0x01,0x2a,0x61,0x76,0x06,0xa7,
+ 0x04,0xe0,0x07,0x30,0x17,0x30,0x07,0x34,0x02,0xf0,0x5e,0x75,0x57,0x16,0x06,0xb7,
+ 0xcf,0x00,0x02,0x01,0x87,0x60,0x17,0x0c,0x06,0xe8,0x5b,0x77,0x07,0xc6,0x11,0x13,
+ 0x61,0x1e,0x61,0x01,0x07,0xd1,0xcf,0x00,0x02,0x01,0x87,0x60,0x17,0x0c,0x05,0xe8,
+ 0x56,0x76,0x06,0xc7,0x11,0x13,0x17,0x1f,0x06,0xd7,0xcf,0x00,0x02,0x01,0x43,0x01,
+ 0x87,0x60,0x17,0x0c,0x0a,0xe8,0x03,0x2a,0x51,0x77,0x11,0x13,0x07,0xc6,0x03,0xe8,
+ 0x16,0x1e,0x66,0x01,0x01,0xf0,0x16,0x1f,0x07,0xd6,0xcf,0x00,0x02,0x01,0x43,0x01,
+ 0x87,0x60,0x17,0x0c,0x0a,0xe8,0x03,0x2a,0x4a,0x77,0x11,0x13,0x07,0xc6,0x03,0xe8,
+ 0x16,0x1e,0x66,0x01,0x01,0xf0,0x16,0x1f,0x07,0xd6,0xcf,0x00,0x02,0x01,0x43,0x01,
+ 0x87,0x60,0x17,0x0c,0x0a,0xe8,0x03,0x2a,0x43,0x77,0x11,0x13,0x07,0xc6,0x03,0xe8,
+ 0x16,0x1e,0x66,0x01,0x01,0xf0,0x16,0x1f,0x07,0xd6,0xcf,0x00,0x02,0x01,0x82,0x2c,
+ 0x87,0x60,0x17,0x0c,0x08,0xe8,0x3c,0x76,0x06,0xc6,0x17,0x13,0x67,0x16,0x47,0x01,
+ 0x17,0x1a,0x72,0x12,0x42,0x01,0xcf,0x00,0x02,0x01,0x77,0x60,0x17,0x0c,0x0a,0xe8,
+ 0x37,0x76,0x06,0xc7,0x11,0x13,0x17,0x1f,0x06,0xd7,0x35,0x76,0x06,0xc7,0x17,0x1e,
+ 0x67,0x01,0x06,0xd7,0xcf,0x00,0x02,0x01,0x77,0x60,0x17,0x0c,0x0a,0xe8,0x30,0x76,
+ 0x06,0xc7,0x11,0x13,0x17,0x1f,0x06,0xd7,0x2d,0x76,0x06,0xc7,0x17,0x1e,0x67,0x01,
+ 0x06,0xd7,0xcf,0x00,0x70,0x25,0x7a,0x00,0x2e,0x12,0x4e,0x01,0x82,0x2c,0x77,0x60,
+ 0xe7,0x0c,0x26,0xe8,0x21,0x7d,0x0d,0xcd,0x21,0x7c,0x0c,0xcc,0x24,0x7b,0x0b,0xcb,
+ 0x24,0x7a,0x0a,0xca,0xe2,0x12,0x23,0x7f,0xe2,0x12,0x03,0x60,0x23,0x7f,0xe2,0x12,
+ 0x23,0x7f,0x42,0x66,0x23,0x7f,0xe2,0x12,0x23,0x7f,0x02,0x2a,0x09,0xe8,0xe2,0x12,
+ 0x22,0x7f,0x42,0x66,0x1f,0x7f,0xe2,0x12,0x1f,0x7f,0x02,0x2a,0x32,0x00,0x02,0x20,
+ 0x12,0x77,0x07,0xdd,0x12,0x77,0x07,0xdc,0x15,0x77,0x07,0xdb,0x15,0x77,0x07,0xda,
+ 0x6a,0x00,0x70,0x21,0xcf,0x00,0x00,0x00,0xf2,0x00,0x00,0x00,0x40,0x06,0x04,0x00,
+ 0x94,0x1e,0x00,0x00,0x74,0x1e,0x00,0x00,0x00,0x13,0x00,0x00,0x2c,0x06,0x04,0x00,
+ 0x34,0x06,0x04,0x00,0x3c,0x06,0x04,0x00,0x30,0x06,0x04,0x00,0x44,0x06,0x04,0x00,
+ 0x58,0x01,0x04,0x00,0xfc,0x00,0x00,0x00,0x1c,0x04,0x04,0x00,0x20,0x04,0x04,0x00,
+ 0x24,0x04,0x04,0x00,0x08,0x04,0x04,0x00,0x28,0x04,0x04,0x00,0x38,0x04,0x04,0x00,
+ 0x3c,0x04,0x04,0x00,0xc2,0x20,0x00,0x00,0xec,0x20,0x00,0x00,0x68,0x21,0x00,0x00,
+ 0xe0,0x0a,0x00,0x00,0x4c,0x21,0x00,0x00,0x86,0x21,0x00,0x00,0xf0,0x24,0x7d,0x00,
+ 0x2e,0x12,0x4e,0x01,0x3d,0x12,0x4d,0x01,0x87,0x60,0xe7,0x0c,0x12,0xe8,0xe2,0x12,
+ 0x03,0x60,0x5f,0x7f,0x60,0x76,0x06,0xc7,0xee,0x13,0xe5,0x12,0x65,0x01,0x57,0x1e,
+ 0x06,0xd7,0x0d,0x2a,0x5d,0x77,0x07,0xc6,0x02,0xe8,0x56,0x1e,0x01,0xf0,0xe6,0x1f,
+ 0x07,0xd6,0x6d,0x00,0xf0,0x20,0xcf,0x00,0x02,0x01,0x21,0x2a,0x08,0xe0,0x57,0x76,
+ 0x06,0xa7,0x17,0x34,0x06,0xb7,0x56,0x76,0x06,0xa7,0x17,0x34,0x06,0xb7,0x55,0x76,
+ 0x06,0xa7,0x55,0x75,0x57,0x1e,0x06,0xb7,0x55,0x76,0x06,0xa7,0x57,0x1e,0x06,0xb7,
+ 0x54,0x76,0x06,0xa7,0x57,0x1e,0x06,0xb7,0x66,0x60,0x52,0x77,0x07,0xb6,0x52,0x76,
+ 0x06,0x87,0xa7,0x34,0x06,0x97,0x16,0x60,0x51,0x77,0x07,0xb6,0x51,0x76,0x06,0xa7,
+ 0x07,0x34,0x06,0xb7,0x50,0x76,0x06,0x87,0x50,0x75,0x57,0x1e,0x06,0x97,0x4f,0x76,
+ 0x06,0x87,0x4f,0x75,0x57,0x1e,0x06,0x97,0x4f,0x77,0x07,0xa6,0x16,0x34,0x07,0xb6,
+ 0x07,0xa6,0x06,0x34,0x07,0xb6,0xcf,0x00,0x02,0x01,0x4a,0x77,0x07,0xa6,0x4a,0x75,
+ 0x56,0x16,0x07,0xb6,0x07,0xa6,0x49,0x75,0x56,0x16,0x07,0xb6,0x06,0x60,0x3f,0x77,
+ 0x07,0xb6,0x40,0x76,0x06,0x87,0x46,0x75,0x57,0x16,0x06,0x97,0x40,0x76,0x06,0x87,
+ 0x45,0x75,0x57,0x16,0x06,0x97,0x3a,0x76,0x06,0xa7,0x3f,0x75,0x57,0x16,0x06,0xb7,
+ 0x21,0x2a,0x09,0xe0,0x2e,0x76,0x06,0xa7,0x3d,0x75,0x57,0x16,0x06,0xb7,0x2c,0x76,
+ 0x06,0xa7,0x57,0x16,0x06,0xb7,0xcf,0x00,0x70,0x24,0x7e,0x00,0x2e,0x12,0x4e,0x01,
+ 0xe2,0x12,0x39,0x7f,0xe2,0x12,0x39,0x7f,0x6e,0x00,0x70,0x20,0xcf,0x00,0x38,0x76,
+ 0x39,0x77,0x07,0xd6,0xcf,0x00,0x06,0x60,0x37,0x77,0x07,0xd6,0xcf,0x00,0x36,0x77,
+ 0x16,0x60,0x07,0xb6,0x36,0x76,0x07,0xb6,0xcf,0x00,0xf0,0x24,0x7d,0x00,0x2d,0x12,
+ 0x3e,0x12,0x4e,0x01,0x32,0x60,0x0e,0x2a,0x0b,0xe8,0x52,0x60,0x4e,0x2a,0x08,0xe8,
+ 0x5e,0x2a,0x03,0xe0,0x12,0x60,0x23,0x12,0x04,0xf0,0x7e,0x2a,0x14,0xe0,0x22,0x60,
+ 0x13,0x60,0x2b,0x7f,0xe2,0x12,0x2b,0x7f,0xd2,0x12,0x2b,0x73,0x2c,0x7f,0x62,0x01,
+ 0x2c,0x77,0x07,0xd2,0x2c,0x77,0x07,0xd2,0x16,0x61,0x2b,0x77,0x07,0xb6,0x2b,0x76,
+ 0x06,0xa7,0x07,0x34,0x06,0xb7,0x6d,0x00,0xf0,0x20,0xcf,0x00,0x02,0x01,0x28,0x77,
+ 0x07,0xb1,0x28,0x76,0x06,0xa7,0x17,0x2e,0x07,0x2a,0xfc,0xe7,0xcf,0x00,0x00,0x00,
+ 0xec,0x20,0x00,0x00,0x40,0x04,0x04,0x00,0x44,0x04,0x04,0x00,0x21,0x0e,0x04,0x00,
+ 0x25,0x0e,0x04,0x00,0x1c,0x04,0x04,0x00,0xa7,0x00,0x00,0x00,0x20,0x04,0x04,0x00,
+ 0x38,0x04,0x04,0x00,0x61,0x01,0x04,0x00,0x04,0x00,0x04,0x00,0x40,0x01,0x04,0x00,
+ 0x0b,0x00,0x04,0x00,0x0c,0x01,0x04,0x00,0x00,0x00,0x0f,0x00,0x04,0x01,0x04,0x00,
+ 0x03,0x20,0x00,0x00,0x44,0x01,0x04,0x00,0xfe,0x00,0x00,0x00,0xfd,0x00,0x00,0x00,
+ 0xff,0xff,0xf0,0xff,0xfc,0xdf,0xff,0xff,0xa8,0x22,0x00,0x00,0x18,0x23,0x00,0x00,
+ 0x10,0x02,0x00,0x00,0x24,0x00,0x04,0x00,0x26,0x00,0x04,0x00,0xfe,0xff,0xff,0xff,
+ 0x2c,0x21,0x00,0x00,0xd8,0x20,0x00,0x00,0x00,0xc2,0x01,0x00,0xd0,0x83,0x00,0x00,
+ 0x22,0x02,0x04,0x00,0x32,0x02,0x04,0x00,0x30,0x02,0x04,0x00,0x20,0x02,0x04,0x00,
+ 0x24,0x02,0x04,0x00,0x28,0x02,0x04,0x00,0xf0,0x25,0x78,0x00,0x01,0x63,0x10,0x05,
+ 0x3c,0x12,0x4a,0x12,0x4a,0x01,0x59,0x12,0x49,0x01,0x09,0x2a,0x27,0x00,0xa0,0x97,
+ 0xa6,0x12,0x06,0x24,0x46,0x01,0xf7,0x60,0x67,0x0c,0x07,0xe0,0xa7,0x62,0x03,0xb7,
+ 0x07,0x60,0x13,0xb7,0x32,0x12,0x12,0x20,0x4d,0xf0,0x0b,0x60,0xf2,0x37,0x03,0xe8,
+ 0xaa,0x2a,0x4d,0xe8,0x2b,0x60,0x2d,0x12,0x2b,0x2a,0x4c,0xe0,0x5a,0x77,0x26,0x12,
+ 0xf6,0x2e,0x67,0x1c,0x07,0xa7,0x00,0xb7,0x4d,0x3e,0x1e,0x60,0x08,0x12,0xe8,0x1c,
+ 0x13,0xf0,0xd2,0x12,0xa3,0x12,0x54,0x7f,0x53,0x77,0x72,0x1c,0x02,0xa7,0x08,0xb7,
+ 0x08,0x20,0x0e,0x20,0x4e,0x01,0xd2,0x12,0xa3,0x12,0x50,0x7f,0x2d,0x12,0xa0,0x87,
+ 0x07,0x2a,0x02,0xe8,0x9e,0x0c,0x04,0xe0,0x0d,0x2a,0xeb,0xe7,0x08,0x0f,0xe9,0xef,
+ 0x1b,0x2a,0x05,0xe0,0xd7,0x62,0x08,0xb7,0x08,0x20,0x0e,0x20,0x4e,0x01,0xa0,0x87,
+ 0x07,0x2a,0x06,0xe8,0x08,0xf0,0x08,0xb7,0x08,0x20,0x0e,0x20,0x4e,0x01,0x04,0xf0,
+ 0x86,0x12,0xc7,0x12,0x08,0xf0,0x07,0x62,0x9e,0x0c,0xf5,0xef,0xf9,0xf7,0x06,0x24,
+ 0x06,0xa5,0x07,0xb5,0x07,0x20,0x06,0x0f,0xfa,0xe7,0x82,0x12,0x02,0x05,0xc2,0x1c,
+ 0x07,0x60,0x02,0xb7,0x01,0x63,0x10,0x1c,0x68,0x00,0xf0,0x21,0xcf,0x00,0x2d,0x12,
+ 0x0d,0x28,0x1b,0x60,0x0e,0x60,0x08,0x12,0xcf,0xf7,0x81,0x63,0x10,0x05,0xd0,0x97,
+ 0xc0,0x96,0xb0,0x95,0xa0,0x94,0x90,0x93,0x80,0x92,0x78,0x00,0xf0,0x25,0x07,0x64,
+ 0x07,0x1c,0x07,0x8e,0x4d,0x64,0x0d,0x1c,0x0c,0x60,0x59,0x62,0x4a,0x64,0x4a,0xf0,
+ 0x97,0x0f,0x0a,0xe8,0x0c,0x2a,0x08,0xe0,0xa7,0x2a,0x02,0xe0,0xd2,0x60,0x28,0x7f,
+ 0x0e,0xa2,0x0e,0x20,0x27,0x7f,0x3e,0xf0,0x1e,0xa2,0xa2,0x0f,0x18,0xe8,0x2a,0x0c,
+ 0x06,0xe8,0x24,0x76,0x26,0x1c,0x87,0x60,0x67,0x0c,0x2f,0xe8,0x0a,0xf0,0x47,0x66,
+ 0x72,0x0f,0x0d,0xe8,0x87,0x67,0x72,0x0f,0x19,0xe8,0x87,0x65,0x72,0x0f,0x25,0xe0,
+ 0x15,0xf0,0x0e,0x20,0x1d,0x77,0x72,0x1c,0x2c,0x12,0x4c,0x01,0x23,0xf0,0xdb,0x12,
+ 0x3b,0x20,0x0d,0x82,0x03,0x12,0xa4,0x60,0xc5,0x12,0x18,0x7f,0x08,0x12,0x02,0xf0,
+ 0x14,0x7f,0x08,0x20,0x08,0xa2,0x02,0x2a,0xfb,0xe7,0x11,0xf0,0xdb,0x12,0x3b,0x20,
+ 0x0d,0x82,0x03,0x12,0x04,0x61,0xc5,0x12,0x11,0x7f,0x08,0x12,0x02,0xf0,0x0c,0x7f,
+ 0x08,0x20,0x08,0xa2,0x02,0x2a,0xfb,0xe7,0x02,0xf0,0x09,0x7f,0xdb,0x12,0x1e,0x20,
+ 0xbd,0x12,0x0c,0x60,0x0e,0xa7,0x07,0x2a,0xb3,0xe7,0xf0,0x21,0x68,0x00,0x81,0x63,
+ 0x10,0x1c,0xcf,0x00,0xe4,0x06,0x00,0x00,0x08,0x84,0x00,0x00,0xd0,0x83,0x00,0x00,
+ 0xec,0x23,0x00,0x00,0xcf,0xff,0xff,0xff,0xd0,0xff,0xff,0xff,0x98,0x24,0x00,0x00,
+ 0x10,0x76,0x06,0xa7,0x10,0x75,0x57,0x16,0x06,0xb7,0xcf,0x00,0x0f,0x75,0x05,0xc5,
+ 0x45,0x01,0xf7,0x61,0x57,0x0c,0x12,0xe8,0x0d,0x77,0x07,0xc7,0xf7,0x01,0x47,0x01,
+ 0x75,0x0f,0x0c,0xe0,0x07,0xf0,0x07,0x84,0x47,0xa3,0x04,0xb3,0x06,0x20,0x46,0x01,
+ 0x77,0x20,0x02,0xf0,0x07,0x77,0x06,0x60,0x56,0x0c,0xf5,0xef,0xcf,0x00,0x00,0x00,
+ 0x04,0x00,0x04,0x00,0xfe,0x00,0x00,0x00,0x00,0xf0,0x10,0x00,0x02,0xf0,0x10,0x00,
+ 0x04,0xf0,0x10,0x00,0x70,0x24,0x7e,0x00,0xb2,0x71,0x01,0x8f,0xff,0x34,0x01,0x9f,
+ 0xb1,0x71,0x01,0x8f,0x1f,0x34,0x01,0x9f,0xb0,0x7e,0x0e,0xb2,0xb0,0x72,0x02,0xb3,
+ 0xb0,0x73,0x03,0x95,0xb0,0x75,0x05,0x96,0xb0,0x76,0x06,0x97,0xb0,0x77,0x20,0x86,
+ 0x07,0x96,0xaf,0x77,0x07,0xb4,0x01,0x87,0x07,0x34,0x01,0x97,0xae,0x76,0x06,0x87,
+ 0x17,0x2e,0x07,0x2a,0xfc,0xe7,0xa2,0x76,0x06,0x87,0xf7,0x34,0x06,0x97,0xa1,0x76,
+ 0x06,0x87,0x17,0x30,0x06,0x97,0x6e,0x00,0x70,0x20,0xcf,0x00,0x70,0x24,0x7e,0x00,
+ 0x2e,0x12,0x32,0x12,0xf2,0x3c,0x03,0x60,0x35,0x12,0xa3,0x7f,0xa4,0x74,0x05,0x60,
+ 0xa4,0x7f,0x62,0x01,0x0e,0x2a,0x02,0xe0,0xa3,0x77,0x13,0xf0,0x1e,0x2a,0x04,0xe8,
+ 0x2e,0x2a,0x04,0xe0,0xa0,0x77,0x07,0xd2,0xa0,0x77,0x0b,0xf0,0x3e,0x2a,0x02,0xe0,
+ 0x9f,0x77,0x07,0xf0,0x4e,0x2a,0x04,0xe8,0x5e,0x2a,0x04,0xe0,0x9c,0x77,0x07,0xd2,
+ 0x9c,0x77,0x07,0xd2,0x6e,0x00,0x70,0x20,0xcf,0x00,0x70,0x24,0x7e,0x00,0x2e,0x12,
+ 0x99,0x72,0x99,0x7f,0x0e,0x2a,0x03,0xe0,0x62,0x01,0x98,0x77,0x09,0xf0,0x1e,0x2a,
+ 0x03,0xe0,0x62,0x01,0x97,0x77,0x04,0xf0,0x2e,0x2a,0x03,0xe0,0x62,0x01,0x95,0x77,
+ 0x07,0xd2,0x6e,0x00,0x70,0x20,0xcf,0x00,0x94,0x75,0x95,0xa6,0x06,0x2a,0x93,0x77,
+ 0x01,0xe8,0x16,0x60,0x07,0xb6,0x05,0xa6,0x15,0xa7,0x87,0x3c,0x67,0x1e,0x25,0xa6,
+ 0x06,0x3d,0x76,0x1e,0x35,0xa7,0x87,0x3d,0x67,0x1e,0x8d,0x76,0x67,0x0f,0x02,0xe0,
+ 0x8d,0x76,0x04,0xf0,0x8d,0x76,0x67,0x0f,0x03,0xe0,0x8c,0x76,0x8d,0x77,0x07,0xd6,
+ 0x86,0x77,0xa7,0xa6,0xb7,0xa7,0x87,0x3c,0x67,0x1e,0x8a,0x76,0x06,0xd7,0x6c,0x76,
+ 0x06,0x87,0xf7,0x34,0x06,0x97,0x88,0x77,0x07,0x86,0x16,0x34,0x07,0x96,0x07,0x86,
+ 0x06,0x34,0x07,0x96,0x70,0x76,0x06,0x87,0x87,0x2e,0x07,0x2a,0xfc,0xe7,0x64,0x76,
+ 0x06,0x87,0xf7,0x34,0x06,0x97,0x80,0x76,0x06,0x87,0x17,0x30,0x06,0x97,0xcf,0x00,
+ 0x70,0x25,0x7a,0x00,0x75,0x77,0x0d,0x60,0x97,0xbd,0x7c,0x7e,0x07,0xbe,0x1e,0x01,
+ 0x17,0xb1,0x2e,0x01,0x27,0xb1,0xe6,0x12,0x86,0x3f,0x37,0xb6,0x79,0x76,0x26,0xac,
+ 0x8b,0x2c,0xcb,0x16,0xa7,0xbb,0x06,0x60,0xb7,0xb6,0x76,0x7f,0x77,0x74,0x04,0xa6,
+ 0x14,0xa7,0x87,0x3c,0x67,0x1e,0x75,0x76,0x06,0xb7,0x87,0x3e,0x16,0xb7,0x74,0x77,
+ 0x07,0xbd,0x74,0x77,0x07,0xbd,0x74,0x77,0x07,0xad,0x74,0x77,0x07,0xa3,0x02,0x60,
+ 0x25,0x12,0x6f,0x12,0xa1,0x61,0x19,0xf0,0x57,0x12,0x57,0x1c,0x46,0x12,0x76,0x1c,
+ 0x26,0xaa,0xf7,0x1c,0x27,0xba,0x36,0xaa,0x37,0xba,0x26,0xa7,0x36,0xaa,0xf1,0x11,
+ 0x17,0x03,0xf9,0x11,0xa7,0x1c,0x77,0x1c,0xe7,0x1c,0x07,0xc7,0x72,0x0c,0x03,0xe0,
+ 0x26,0xad,0x36,0xa3,0x72,0x12,0x05,0x20,0x65,0x01,0x04,0xa6,0x14,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x75,0x0c,0xe1,0xef,0x87,0x2c,0x27,0x16,0x5d,0x76,0x06,0xb7,0x82,0x3e,
+ 0x5d,0x76,0x06,0xb2,0x5d,0x76,0x06,0xbd,0x5d,0x76,0x06,0xb3,0x42,0x01,0x82,0x3c,
+ 0x72,0x1e,0x5b,0x77,0x07,0x8e,0x5b,0x77,0xe7,0x1c,0x07,0xa7,0xf1,0x11,0x72,0x03,
+ 0xf9,0x11,0x43,0x66,0x41,0x7f,0x2c,0x0c,0x58,0x77,0x06,0xe0,0x62,0x01,0x07,0xb2,
+ 0x82,0x3e,0x56,0x77,0x07,0xb2,0x04,0xf0,0x07,0xbb,0x54,0x77,0x06,0x60,0x07,0xb6,
+ 0x3e,0x76,0x17,0x60,0x96,0xb7,0x52,0x77,0xe7,0x1c,0x07,0xa5,0x52,0x77,0x7e,0x1c,
+ 0x0e,0xa7,0x87,0x3c,0x57,0x1e,0x07,0x3d,0x07,0x3b,0xe7,0x01,0x67,0x01,0x0e,0x60,
+ 0xa6,0xb7,0x87,0x3e,0xb6,0xb7,0x3f,0x7f,0x40,0x74,0x04,0xa6,0x14,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x49,0x76,0x06,0xb7,0x87,0x3e,0x49,0x76,0x06,0xb7,0x49,0x77,0x07,0xbe,
+ 0x49,0x77,0x07,0xbe,0xe6,0x12,0x39,0x7d,0x35,0x72,0xa3,0x61,0x1b,0xf0,0x65,0x12,
+ 0x65,0x1c,0x45,0x1c,0x25,0xaf,0x07,0x67,0x67,0x1c,0x77,0x1c,0xd7,0x1c,0x27,0xbf,
+ 0x35,0xaf,0x37,0xbf,0x25,0xa7,0x35,0xa5,0xf1,0x11,0x37,0x03,0xf9,0x11,0x57,0x1c,
+ 0x77,0x1c,0x27,0x1c,0x07,0xc7,0x77,0x01,0xe7,0x01,0x67,0x01,0x7e,0x0c,0x7e,0x0a,
+ 0x06,0x20,0x66,0x01,0x04,0xa5,0x14,0xa7,0x87,0x3c,0x57,0x1e,0x76,0x0c,0xdf,0xef,
+ 0x34,0x77,0x07,0xbe,0x8e,0x3e,0x33,0x77,0x07,0xbe,0x6a,0x00,0x70,0x21,0xcf,0x00,
+ 0x08,0x00,0x04,0x00,0x30,0x0a,0x04,0x00,0x00,0x0a,0x04,0x00,0x01,0x0a,0x04,0x00,
+ 0x24,0x0a,0x04,0x00,0x28,0x0a,0x04,0x00,0x20,0x0a,0x04,0x00,0x2c,0x0a,0x04,0x00,
+ 0x02,0x0a,0x04,0x00,0x3c,0x0a,0x04,0x00,0xd2,0x84,0x00,0x00,0x80,0xc3,0xc9,0x01,
+ 0x34,0x85,0x00,0x00,0x50,0x08,0x04,0x00,0x52,0x08,0x04,0x00,0x6c,0x08,0x04,0x00,
+ 0x6e,0x08,0x04,0x00,0xa0,0x25,0x26,0x00,0xd0,0x83,0x00,0x00,0x9a,0x08,0x04,0x00,
+ 0x9e,0x08,0x04,0x00,0x96,0x08,0x04,0x00,0x7a,0x9e,0x00,0x00,0x47,0x0a,0x04,0x00,
+ 0x00,0xe0,0x02,0x00,0x36,0x50,0x00,0x00,0x00,0x00,0x03,0x00,0x36,0x60,0x00,0x00,
+ 0x40,0x0a,0x04,0x00,0x48,0x0a,0x04,0x00,0x50,0x0a,0x04,0x00,0x00,0xe0,0x02,0x00,
+ 0x86,0x9e,0x00,0x00,0x88,0x27,0x00,0x00,0x00,0x08,0x03,0x00,0x8a,0x9e,0x00,0x00,
+ 0x3e,0xa4,0x00,0x00,0x3f,0xa4,0x00,0x00,0x40,0xa4,0x00,0x00,0x41,0xa4,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0xc0,0x00,0x00,0x00,0x44,0xa4,0x00,0x00,0x45,0xa4,0x00,0x00,
+ 0xb5,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x6a,0x9f,0x00,0x00,0x6b,0x9f,0x00,0x00,
+ 0x42,0xa4,0x00,0x00,0x43,0xa4,0x00,0x00,0xf0,0x25,0x78,0x00,0x81,0x62,0x10,0x05,
+ 0x29,0x12,0x3c,0x12,0x58,0x12,0x58,0x1c,0x4d,0x12,0x0b,0x60,0xa0,0xf0,0x02,0x12,
+ 0x03,0x60,0x04,0x61,0x52,0x7f,0x06,0x12,0xf6,0x20,0x07,0x60,0x73,0x12,0x04,0x61,
+ 0x1e,0xf0,0x75,0x12,0x75,0x1c,0xd5,0x1c,0x05,0xc5,0x75,0x01,0x4d,0x72,0x52,0x0d,
+ 0x02,0xe8,0x06,0xb4,0x0c,0xf0,0x4b,0x72,0x25,0x0d,0x02,0xe8,0x06,0xb3,0x07,0xf0,
+ 0xf5,0x37,0x02,0xe8,0xf2,0x63,0x25,0x1c,0x65,0x3a,0x75,0x20,0x06,0xb5,0x06,0xa5,
+ 0x55,0x01,0x05,0x1c,0x05,0xa2,0x02,0x20,0x05,0xb2,0x07,0x20,0x06,0x20,0x07,0x01,
+ 0xc1,0x0c,0xdf,0xef,0x07,0x60,0x76,0x12,0x75,0x12,0x07,0x01,0x04,0x12,0x74,0x1c,
+ 0x04,0xa4,0x45,0x0c,0x16,0x0a,0x45,0x0a,0x07,0x20,0x07,0x2b,0xf6,0xe7,0x07,0x60,
+ 0x7a,0x12,0x75,0x12,0x07,0x01,0x04,0x12,0x74,0x1c,0x04,0xa4,0x45,0x0c,0x03,0xe0,
+ 0x61,0x0f,0x1a,0x02,0x45,0x02,0x07,0x20,0x07,0x2b,0xf4,0xe7,0x07,0x60,0x72,0x12,
+ 0x73,0x12,0x10,0xf0,0x05,0x61,0x05,0x1c,0x75,0x1c,0x05,0xa5,0x55,0x01,0x65,0x0f,
+ 0x08,0xe0,0x75,0x12,0x75,0x1c,0xd5,0x1c,0x05,0xc5,0x75,0x01,0x52,0x1c,0x03,0x20,
+ 0x43,0x01,0x07,0x20,0x07,0x01,0xc1,0x0c,0xed,0xef,0x03,0x2a,0x03,0xe8,0x26,0x7f,
+ 0x2e,0x12,0x01,0xf0,0x3e,0x12,0x25,0x76,0x06,0x87,0x62,0x67,0x27,0x1c,0x07,0xa6,
+ 0x07,0x60,0x72,0x12,0x73,0x12,0xe6,0x0d,0x11,0xe0,0x18,0xf0,0x06,0x61,0x06,0x1c,
+ 0x76,0x1c,0x06,0xa6,0x56,0x01,0xa6,0x0f,0x08,0xe0,0x76,0x12,0x76,0x1c,0xd6,0x1c,
+ 0x06,0xc6,0x76,0x01,0x62,0x1c,0x03,0x20,0x43,0x01,0x07,0x20,0x07,0x01,0xc1,0x0c,
+ 0xed,0xef,0x03,0x2a,0x03,0xe8,0x14,0x7f,0x2e,0x0d,0x2e,0x0a,0xd7,0x12,0x06,0x60,
+ 0x63,0x12,0x10,0xf0,0x07,0xc5,0x54,0x12,0x74,0x01,0xf4,0x37,0x02,0xe0,0x4e,0x0d,
+ 0x01,0xf0,0xe4,0x0d,0x03,0xe8,0xe5,0x05,0x07,0xd5,0x01,0xf0,0x07,0xd3,0x06,0x20,
+ 0x46,0x01,0x17,0x20,0xc6,0x0f,0xee,0xe7,0x0b,0x20,0x4b,0x01,0x8d,0x1c,0x9b,0x0f,
+ 0x5e,0xe7,0x81,0x62,0x10,0x1c,0x68,0x00,0xf0,0x21,0xcf,0x00,0x22,0x83,0x00,0x00,
+ 0xc0,0x01,0x00,0x00,0x40,0xfe,0xff,0xff,0x3e,0x84,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0xf0,0x25,0x79,0x00,0x2a,0x12,0x39,0x12,0x83,0x7f,0x83,0x72,0xa3,0x12,0x83,0x7f,
+ 0x0d,0x60,0x83,0x7c,0x17,0xf0,0x9e,0x12,0xf1,0x11,0xde,0x03,0xf9,0x11,0xee,0x1c,
+ 0xae,0x1c,0x0b,0x60,0x07,0xf0,0x0e,0xc3,0x1e,0x20,0x7e,0x72,0x73,0x01,0x7b,0x7f,
+ 0x0b,0x20,0x6b,0x01,0x0c,0x87,0x57,0xa7,0x7b,0x0c,0xf5,0xef,0x7b,0x72,0x77,0x7f,
+ 0x0d,0x20,0x6d,0x01,0x0c,0x87,0x47,0xa7,0x7d,0x0c,0xe5,0xef,0x69,0x00,0xf0,0x21,
+ 0xcf,0x00,0xf0,0x25,0x79,0x00,0x70,0x24,0xc6,0x60,0x74,0x77,0x07,0xb6,0x0e,0x60,
+ 0xed,0x12,0x73,0x7b,0x74,0x7a,0x74,0x79,0x6e,0x7c,0x19,0xf0,0xe6,0x12,0xe6,0x1c,
+ 0xe6,0x1c,0x46,0x3c,0x09,0xa2,0x0c,0x87,0x57,0xa3,0x70,0x77,0x67,0x1e,0x00,0x97,
+ 0xf4,0x60,0x6f,0x75,0x70,0x77,0x76,0x1e,0x07,0x60,0x6f,0x7f,0xb7,0x12,0xd7,0x1c,
+ 0x26,0x62,0x67,0x1c,0x07,0xa7,0x7e,0x1c,0x4e,0x01,0x0d,0x20,0x4d,0x01,0x0a,0xa7,
+ 0x7d,0x0c,0xe4,0xef,0x5f,0x7e,0x0e,0x87,0x47,0xa2,0x57,0xa3,0x64,0x7c,0x65,0x7d,
+ 0x00,0x9d,0x24,0x60,0xc5,0x12,0x06,0x60,0x67,0x12,0x63,0x7f,0xc6,0x60,0x63,0x77,
+ 0x07,0xb6,0x0e,0x8e,0x4e,0xa2,0x5e,0xa3,0x00,0x9c,0xe4,0x60,0xd5,0x12,0x60,0x76,
+ 0x07,0x60,0x5d,0x7f,0x4e,0xa2,0x5e,0xa3,0x00,0x9d,0x24,0x60,0xc5,0x12,0x06,0x60,
+ 0x67,0x12,0x59,0x7f,0x70,0x20,0x69,0x00,0xf0,0x21,0xcf,0x00,0xf0,0x24,0x20,0x9f,
+ 0x30,0x9e,0x4b,0x77,0x07,0x8e,0x4e,0xa2,0x5e,0xa3,0x56,0x77,0x00,0x97,0x24,0x60,
+ 0x51,0x75,0x06,0x60,0x67,0x12,0x50,0x7f,0x54,0x77,0x7e,0x1c,0x0e,0xa7,0x07,0x2a,
+ 0x13,0xe8,0x47,0x77,0x17,0xa6,0x07,0xa7,0x76,0x1c,0x07,0x60,0x50,0x73,0x50,0x74,
+ 0x09,0xf0,0x75,0x12,0x75,0x1c,0x32,0x12,0x52,0x1c,0x45,0x1c,0x05,0xc5,0x02,0xd5,
+ 0x07,0x20,0x47,0x01,0x67,0x0d,0xf5,0xe7,0x20,0x8f,0x30,0x8e,0xf0,0x20,0xcf,0x00,
+ 0x70,0x25,0x7a,0x00,0x2a,0x12,0x3c,0x12,0x0d,0x60,0xde,0x12,0x46,0x7b,0x23,0xf0,
+ 0xa7,0x12,0xe7,0x1c,0x07,0xa7,0x07,0x2a,0x1c,0xe0,0xe6,0x12,0xe6,0x1c,0xc6,0x1c,
+ 0x26,0xa5,0x87,0x65,0xd7,0x1c,0x77,0x1c,0xb7,0x1c,0x87,0xb5,0x36,0xa6,0x97,0xb6,
+ 0xed,0x0f,0x0d,0xe8,0xd2,0x12,0x32,0x3c,0xd2,0x05,0xb2,0x1c,0xe3,0x12,0x33,0x3c,
+ 0xe3,0x05,0xb3,0x1c,0xa7,0x62,0x72,0x1c,0x73,0x1c,0x74,0x60,0x37,0x7f,0x0d,0x20,
+ 0x4d,0x01,0x0e,0x20,0x4e,0x01,0x0c,0xa6,0x1c,0xa7,0x87,0x3c,0x67,0x1e,0x7e,0x0c,
+ 0xd7,0xef,0x32,0x77,0x07,0xbd,0x32,0x77,0x06,0x60,0x07,0xb6,0x6a,0x00,0x70,0x21,
+ 0xcf,0x00,0x02,0x01,0x01,0x2b,0x0e,0xe0,0x1a,0x77,0x07,0x84,0x2e,0x77,0x2e,0x75,
+ 0x46,0x12,0x76,0x1c,0x2e,0x73,0x36,0x1c,0x06,0xa6,0x07,0xb6,0x07,0x20,0x57,0x0f,
+ 0xf7,0xe7,0x13,0xf0,0xf1,0x2e,0x27,0x77,0x14,0x60,0x27,0x75,0x07,0xa6,0x06,0x2a,
+ 0x03,0xe8,0x61,0x0c,0x06,0xe0,0x01,0xf0,0x06,0x62,0x16,0x05,0x46,0x01,0x07,0xb6,
+ 0x01,0xf0,0x07,0xb4,0x07,0x20,0x57,0x0f,0xf1,0xe7,0x07,0x60,0x1f,0x76,0x06,0xb7,
+ 0x20,0x76,0x06,0xb7,0x07,0x77,0x07,0x87,0x1f,0x76,0x67,0x1c,0x07,0xa6,0x1e,0x77,
+ 0x07,0xb6,0xcf,0x00,0x8e,0x23,0x00,0x00,0xf8,0x06,0x00,0x00,0x7a,0x25,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0x04,0x07,0x00,0x00,0x0c,0x07,0x00,0x00,0x07,0x0a,0x04,0x00,
+ 0x20,0x9e,0x00,0x00,0x41,0x9e,0x00,0x00,0x42,0x9e,0x00,0x00,0x00,0x00,0x03,0x18,
+ 0x00,0x00,0x01,0x24,0x00,0x00,0x02,0x18,0xb4,0x26,0x00,0x00,0x06,0x0a,0x04,0x00,
+ 0x00,0x00,0x07,0x18,0x36,0x00,0x04,0x1a,0xc8,0x00,0x00,0x00,0x5e,0xa5,0x00,0x00,
+ 0xec,0xa4,0x00,0x00,0x8a,0x9e,0x00,0x00,0x8c,0x82,0x00,0x00,0x40,0x9f,0x00,0x00,
+ 0x41,0x9f,0x00,0x00,0x40,0x09,0x04,0x00,0x64,0x09,0x04,0x00,0xa8,0xf7,0xfb,0xff,
+ 0x65,0x09,0x04,0x00,0xcc,0x00,0x00,0x00,0x66,0x09,0x04,0x00,0x42,0x01,0x03,0x01,
+ 0x44,0x01,0x15,0x12,0x35,0x2e,0x55,0x1c,0x27,0x12,0x37,0x3c,0x27,0x1c,0x16,0x33,
+ 0x67,0x1c,0x21,0x3e,0x17,0x1c,0x07,0xa6,0x33,0x60,0x53,0x1b,0x36,0x1f,0x54,0x1b,
+ 0x46,0x1e,0x46,0x01,0x07,0xb6,0xcf,0x00,0xf0,0x25,0x78,0x00,0xf0,0x24,0x2d,0x12,
+ 0x4d,0x01,0x12,0x33,0x83,0x2c,0x80,0x74,0x81,0x7f,0x81,0x76,0x06,0x8e,0x4e,0xa1,
+ 0x00,0x91,0x0d,0x2a,0x14,0xe0,0x7f,0x75,0x05,0xa7,0x7f,0x74,0x47,0x16,0x05,0xb7,
+ 0xde,0x12,0x6d,0x12,0x07,0xf0,0xe7,0x1c,0xe2,0x12,0x67,0xa3,0x14,0x60,0x7b,0x7f,
+ 0x0e,0x20,0x4e,0x01,0x0d,0x87,0x47,0xa6,0x6e,0x0c,0xf5,0xef,0xc8,0xf0,0x2d,0x2a,
+ 0x3f,0xe0,0x74,0x76,0x06,0xa7,0x07,0x30,0x17,0x30,0x17,0x34,0x06,0xb7,0x6e,0xa5,
+ 0x16,0x61,0x07,0x60,0x56,0x0c,0x4e,0xa5,0x07,0xe8,0x11,0xf0,0x07,0x20,0xe4,0x12,
+ 0x74,0x1c,0x54,0xa4,0x46,0x0c,0x10,0xe0,0x7c,0x12,0x4c,0x01,0x5c,0x0c,0xf6,0xef,
+ 0x0a,0xf0,0x07,0x20,0xe4,0x12,0x74,0x1c,0x54,0xa4,0x46,0x0c,0x05,0xe8,0x7c,0x12,
+ 0x4c,0x01,0x5c,0x0c,0xf6,0xef,0x0c,0x60,0x0d,0x60,0x07,0xf0,0xe7,0x12,0xd7,0x1c,
+ 0xd2,0x12,0x67,0xa3,0x14,0x60,0x61,0x7f,0x0d,0x20,0x0d,0x01,0xc1,0x0c,0xf6,0xef,
+ 0x0d,0x60,0x07,0xf0,0xe7,0x1c,0xd2,0x12,0x67,0xa3,0x14,0x60,0x5c,0x7f,0x0d,0x20,
+ 0x4d,0x01,0xd7,0x12,0xc7,0x1c,0x47,0x01,0x4e,0xa6,0x67,0x0c,0xf3,0xef,0x87,0xf0,
+ 0x1d,0x2a,0x85,0xe0,0x54,0x76,0x06,0xa7,0x07,0x30,0x17,0x30,0x07,0x34,0x06,0xb7,
+ 0x4e,0xad,0x53,0x7b,0x07,0x60,0x0b,0xb7,0x05,0x60,0x00,0x95,0x26,0x65,0xe6,0x1c,
+ 0x10,0x96,0xe9,0x12,0x44,0xf0,0x78,0x1c,0x07,0x20,0x07,0x01,0x61,0x0c,0xfb,0xef,
+ 0x6d,0x0c,0xd6,0x0a,0x30,0x96,0x00,0x8c,0x0a,0x60,0xc7,0x12,0x97,0x1c,0x20,0x97,
+ 0x1a,0xf0,0xe2,0x12,0xa2,0x1c,0x10,0x81,0x01,0xa3,0x46,0x7f,0x82,0x1c,0x02,0xa4,
+ 0x20,0x87,0xe7,0x1c,0xc2,0x12,0x67,0xa3,0x14,0x2a,0x01,0xe8,0x24,0x60,0x3f,0x7f,
+ 0x0e,0x20,0x01,0xf0,0x0e,0x60,0x0e,0x01,0x30,0x84,0x41,0x0c,0xea,0xef,0x0a,0x20,
+ 0x4a,0x01,0x0c,0x20,0x4c,0x01,0x10,0x85,0x05,0xa7,0x7a,0x0c,0xf3,0xef,0x0b,0xa6,
+ 0xd7,0x0c,0x08,0xe0,0x39,0x71,0x16,0x1c,0x24,0x62,0x46,0x1c,0x06,0xb7,0x7d,0x05,
+ 0x4d,0x01,0x06,0xf0,0x35,0x75,0x56,0x1c,0x21,0x62,0x16,0x1c,0x06,0xbd,0x0d,0x60,
+ 0x00,0x84,0x47,0x1c,0x07,0x01,0x00,0x91,0x0b,0xa7,0x07,0x20,0x0b,0xb7,0x0d,0x2a,
+ 0x04,0xe0,0x2e,0x77,0x07,0xab,0x2e,0x79,0x09,0xf0,0x10,0x84,0x04,0xa6,0x07,0x60,
+ 0x2d,0x78,0xb3,0xf7,0xd7,0x12,0xd7,0x1c,0x79,0x1c,0x0d,0x20,0x0d,0x01,0xb1,0x0c,
+ 0xf9,0xef,0x0d,0x60,0x29,0x78,0x19,0xf0,0xea,0x12,0xea,0x1c,0xca,0x1c,0xe2,0x12,
+ 0xd2,0x1c,0xb3,0x12,0x20,0x7f,0x22,0x1c,0x92,0x1c,0x02,0xc7,0x0a,0xd7,0x0e,0x20,
+ 0x07,0xf0,0x0e,0x60,0xdc,0x12,0x3c,0x3c,0xd7,0x12,0x67,0x3c,0x7c,0x1c,0x8c,0x1c,
+ 0x0e,0x01,0xb1,0x0c,0xe9,0xef,0x0d,0x20,0x4d,0x01,0xbd,0x0f,0xf2,0xe7,0x10,0x77,
+ 0x07,0x85,0x45,0xa3,0x07,0x60,0x76,0x12,0x14,0x61,0x08,0xf0,0x52,0x12,0x72,0x1c,
+ 0x62,0xa2,0x24,0x0c,0x02,0xe8,0x06,0x20,0x46,0x01,0x07,0x20,0x07,0x01,0x31,0x0c,
+ 0xf5,0xef,0x12,0x77,0x07,0xb6,0x00,0x85,0x56,0x14,0x46,0x01,0x11,0x77,0x07,0xb6,
+ 0xf0,0x20,0x68,0x00,0xf0,0x21,0xcf,0x00,0x44,0x01,0x00,0x00,0xc4,0x09,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0x54,0x08,0x04,0x00,0xfc,0x00,0x00,0x00,0x4c,0x2e,0x00,0x00,
+ 0x41,0x9e,0x00,0x00,0x74,0x84,0x00,0x00,0x20,0x9e,0x00,0x00,0x42,0x9e,0x00,0x00,
+ 0x10,0x03,0x00,0x00,0xb4,0x04,0x00,0x00,0x00,0x60,0x02,0x00,0x5a,0x08,0x04,0x00,
+ 0x5b,0x08,0x04,0x00,0x70,0x25,0x7b,0x00,0x23,0x12,0x63,0x01,0xb7,0x72,0xb7,0x7f,
+ 0x2d,0x12,0x6d,0x01,0xdc,0x12,0x0c,0x20,0x1c,0x3e,0x0e,0x60,0xb5,0x7b,0x0b,0xf0,
+ 0xe2,0x12,0x92,0x3c,0xd3,0x12,0xb1,0x7f,0xb3,0x7f,0xb2,0x14,0x42,0x01,0xb2,0x77,
+ 0xe7,0x1c,0x07,0xb2,0x0e,0x20,0xe7,0x12,0x67,0x01,0xc7,0x0c,0xf1,0xef,0x6b,0x00,
+ 0x70,0x21,0xcf,0x00,0x70,0x24,0x7e,0x00,0xad,0x7e,0xe2,0x12,0x03,0x60,0x44,0x61,
+ 0xac,0x7f,0x07,0x60,0x0e,0xb7,0x2e,0xb7,0x3e,0xb7,0xfe,0xb7,0xae,0xb7,0x07,0x2c,
+ 0xbe,0xb7,0xf7,0x67,0xce,0xb7,0x6e,0x00,0x70,0x20,0xcf,0x00,0x70,0x24,0x00,0x9f,
+ 0xa5,0x77,0x07,0xa7,0xa5,0x76,0x06,0x85,0xa5,0x76,0x65,0x0f,0x0a,0xe0,0x74,0x12,
+ 0x74,0x1c,0x74,0x1c,0xa3,0x72,0xa3,0x73,0x44,0x3c,0x15,0x60,0xa3,0x7f,0x16,0x60,
+ 0x01,0xf0,0x06,0x60,0x9a,0x77,0x27,0xb6,0x00,0x8f,0x70,0x20,0xcf,0x00,0x70,0x24,
+ 0x00,0x9f,0x06,0x60,0x9e,0x77,0x07,0xb6,0x9e,0x76,0x06,0x87,0x9e,0x75,0x57,0x16,
+ 0x06,0x97,0x9d,0x76,0x06,0x87,0xd7,0x30,0x06,0x97,0x9c,0x76,0x06,0x87,0xe7,0x31,
+ 0xf7,0x31,0x06,0x97,0xa2,0x60,0x9a,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,0xf0,0x25,
+ 0x78,0x00,0xf0,0x24,0x98,0x7e,0x0e,0x92,0x98,0x77,0x00,0x97,0x42,0x62,0x83,0x61,
+ 0x94,0x60,0x05,0x60,0x56,0x12,0x57,0x12,0x95,0x7f,0x95,0x77,0x00,0x97,0x42,0x62,
+ 0x83,0x61,0x94,0x60,0x05,0x60,0x56,0x12,0x57,0x12,0x90,0x7f,0x92,0x77,0x00,0x97,
+ 0x42,0x62,0xa3,0x61,0x94,0x60,0x05,0x60,0x56,0x12,0x57,0x12,0x8c,0x7f,0x8e,0x77,
+ 0x00,0x97,0x42,0x62,0xa3,0x61,0x94,0x60,0x05,0x60,0x56,0x12,0x57,0x12,0x87,0x7f,
+ 0x8b,0x77,0x00,0x97,0x42,0x62,0xa3,0x61,0x94,0x60,0x05,0x60,0x56,0x12,0x57,0x12,
+ 0x83,0x7f,0x87,0x77,0x00,0x97,0x42,0x62,0x23,0x12,0x94,0x60,0x05,0x60,0x56,0x12,
+ 0x57,0x12,0x7e,0x7f,0x70,0x7d,0xd2,0x12,0x03,0x60,0x64,0x64,0x6d,0x7f,0x0e,0x8e,
+ 0x27,0x64,0xe7,0x1c,0x07,0xa7,0x07,0x2a,0x05,0xe0,0x4e,0xa7,0x0d,0xb7,0x5e,0xa7,
+ 0x1d,0xb7,0x99,0xf0,0x36,0x64,0xe6,0x1c,0x06,0xa4,0x14,0x2a,0x4e,0xa5,0x5e,0xa6,
+ 0x19,0xe0,0x54,0x12,0x04,0x24,0x0d,0xb4,0x64,0x12,0x74,0x05,0x1d,0xb4,0x03,0x60,
+ 0x34,0x12,0x72,0x12,0xd2,0x1c,0x09,0xf0,0x4c,0x12,0xdc,0x1c,0x2c,0xb5,0x2c,0x12,
+ 0x3c,0x1c,0x6c,0xb6,0x04,0x20,0x06,0x24,0x46,0x01,0x03,0x24,0x04,0x01,0x71,0x0c,
+ 0xf3,0xef,0x79,0xf0,0x24,0x2a,0x19,0xe0,0x54,0x12,0x74,0x05,0x0d,0xb4,0x64,0x12,
+ 0x04,0x24,0x1d,0xb4,0x03,0x60,0x34,0x12,0x72,0x12,0xd2,0x1c,0x09,0xf0,0x4c,0x12,
+ 0xdc,0x1c,0x6c,0xb6,0x2c,0x12,0x3c,0x1c,0x2c,0xb5,0x04,0x20,0x05,0x24,0x45,0x01,
+ 0x03,0x24,0x04,0x01,0x71,0x0c,0xf3,0xef,0x5e,0xf0,0x34,0x2a,0x25,0xe0,0x54,0x12,
+ 0x04,0x24,0x0d,0xb4,0x1d,0xb6,0x5b,0x73,0x04,0x60,0x1a,0xf0,0x32,0x12,0x32,0x24,
+ 0x02,0xb5,0x02,0x60,0xed,0x12,0x4d,0x1c,0x48,0x64,0x8d,0x1c,0x0c,0xf0,0x02,0x20,
+ 0xec,0x12,0x2c,0x1c,0x9b,0x62,0xbc,0x1c,0x0d,0xab,0x0c,0xac,0xcb,0x0f,0x03,0xe0,
+ 0x01,0x20,0x03,0xb1,0x03,0xf0,0x02,0x01,0x61,0x0c,0xf1,0xef,0x04,0x20,0x03,0x20,
+ 0x04,0x01,0x71,0x0c,0xe3,0xef,0x37,0xf0,0x44,0x2a,0x21,0xe0,0x0d,0xb5,0x64,0x12,
+ 0x04,0x24,0x1d,0xb4,0x49,0x73,0x04,0x60,0x16,0xf0,0x43,0xb6,0x02,0x60,0xed,0x12,
+ 0x4d,0x1c,0x4f,0x64,0xfd,0x1c,0x0a,0xf0,0x02,0x20,0xec,0x12,0x2c,0x1c,0x0d,0xab,
+ 0x5c,0xac,0xcb,0x0f,0x03,0xe0,0x01,0x20,0x03,0xb1,0x03,0xf0,0x02,0x01,0x51,0x0c,
+ 0xf3,0xef,0x04,0x20,0x03,0x20,0x04,0x01,0x71,0x0c,0xe7,0xef,0x14,0xf0,0x54,0x12,
+ 0x74,0x05,0x0d,0xb4,0x64,0x12,0x74,0x05,0x1d,0xb4,0x7d,0x1c,0x0d,0x20,0x04,0x60,
+ 0x53,0x12,0x43,0x05,0x0d,0xb3,0x63,0x12,0x43,0x05,0x4d,0xb3,0x04,0x20,0x44,0x01,
+ 0x0d,0x24,0x74,0x0f,0xf5,0xe7,0x1b,0x77,0x17,0xad,0x8d,0x3c,0x86,0x2c,0xd6,0x16,
+ 0xa7,0xb6,0xd6,0x12,0x86,0x3e,0xb7,0xb6,0x2d,0x76,0xe6,0x1c,0x06,0xa6,0x26,0x2a,
+ 0x07,0xa6,0x96,0x00,0x86,0x3c,0x66,0x01,0x85,0x2c,0x65,0x16,0xc7,0xb5,0x86,0x3e,
+ 0xd7,0xb6,0x27,0x77,0xe7,0x1c,0x07,0xa9,0x27,0x77,0xe7,0x1c,0x07,0xa8,0x98,0x1c,
+ 0x26,0x72,0x28,0x1c,0x26,0x77,0xe7,0x1c,0x07,0xa7,0x20,0x97,0x0a,0x7a,0xca,0xa7,
+ 0xda,0xac,0x8c,0x3c,0x7c,0x1e,0x44,0xf0,0xa0,0x25,0x26,0x00,0xd0,0x83,0x00,0x00,
+ 0xfe,0xff,0xff,0xff,0xa0,0x0b,0x00,0x00,0x00,0x80,0x01,0x00,0x66,0x9e,0x00,0x00,
+ 0xc4,0x09,0x00,0x00,0x20,0x9e,0x00,0x00,0x00,0xb0,0x10,0x00,0xaa,0xa5,0x55,0x5a,
+ 0x00,0x00,0x03,0x00,0x00,0xa0,0x00,0x00,0xf6,0x1e,0x00,0x00,0x40,0x01,0x04,0x00,
+ 0x0c,0x01,0x04,0x00,0xff,0xff,0xf0,0xff,0x04,0x01,0x04,0x00,0x08,0x01,0x04,0x00,
+ 0xe0,0x0a,0x00,0x00,0x1c,0x9e,0x00,0x00,0x00,0x00,0x02,0x18,0xb4,0x26,0x00,0x00,
+ 0x00,0x00,0x03,0x18,0x00,0x00,0x04,0x1a,0x00,0x00,0x05,0x1a,0x00,0x00,0x06,0x1a,
+ 0x00,0x00,0x01,0x24,0x26,0x9e,0x00,0x00,0x22,0x9e,0x00,0x00,0x20,0x01,0x00,0x00,
+ 0x95,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x97,0x00,0x00,0x00,
+ 0x27,0x12,0xc7,0x1c,0x20,0x8b,0xb7,0x1c,0xca,0x76,0xe6,0x1c,0x06,0xa6,0x67,0x1c,
+ 0x67,0x01,0x30,0x97,0x7f,0x32,0xf9,0x1c,0x0e,0xa7,0x1e,0xab,0x8b,0x3c,0x7b,0x1e,
+ 0xd8,0x1c,0x68,0x01,0x92,0x12,0xf1,0x11,0xb2,0x03,0xf9,0x11,0x83,0x12,0xc1,0x7f,
+ 0x62,0x01,0xea,0xb2,0x82,0x3e,0xfa,0xb2,0x92,0x12,0xd2,0x1c,0xbf,0x73,0x32,0x1c,
+ 0xf1,0x11,0xb2,0x03,0xf9,0x11,0x83,0x12,0xbb,0x7f,0x62,0x01,0xbc,0x77,0x07,0xb2,
+ 0x82,0x3e,0xbb,0x77,0x07,0xb2,0x20,0x8a,0x74,0x32,0x4a,0x1c,0x2e,0xa7,0x3e,0xab,
+ 0x8b,0x3c,0x7b,0x1e,0xa2,0x12,0xf1,0x11,0xb2,0x03,0xf9,0x11,0x30,0x83,0xb1,0x7f,
+ 0x62,0x01,0xb4,0x77,0x07,0xb2,0x82,0x3e,0xb4,0x77,0x07,0xb2,0xa2,0x12,0xc2,0x1c,
+ 0xae,0x75,0x52,0x1c,0xf1,0x11,0xb2,0x03,0xf9,0x11,0x30,0x83,0xaa,0x7f,0x62,0x01,
+ 0xaf,0x77,0x07,0xb2,0x82,0x3e,0xae,0x77,0x07,0xb2,0xae,0x77,0xe7,0x1c,0x07,0xa6,
+ 0xae,0x77,0xe7,0x1c,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x85,0x2c,0x75,0x16,0xab,0x76,
+ 0x06,0xb5,0x76,0x12,0x86,0x3e,0xaa,0x74,0x04,0xb6,0xaa,0x74,0x04,0xb5,0xaa,0x75,
+ 0x05,0xb6,0x7d,0x05,0x6d,0x01,0xa9,0x76,0x06,0xbd,0x8d,0x3e,0xa9,0x76,0x06,0xbd,
+ 0xc7,0x14,0x67,0x01,0xa8,0x76,0x06,0xb7,0x87,0x3e,0xa7,0x76,0x06,0xb7,0xa7,0x77,
+ 0x66,0x67,0xe6,0x1c,0x06,0xa6,0x07,0xb6,0x56,0x67,0xe6,0x1c,0x06,0xa6,0x17,0xb6,
+ 0x46,0x67,0xe6,0x1c,0x06,0xa6,0x27,0xb6,0x06,0x60,0x37,0xb6,0xb7,0x64,0xe7,0x1c,
+ 0x07,0xa7,0x27,0x2a,0xcd,0x64,0xed,0x1c,0xdc,0x64,0xec,0x1c,0x0d,0xa3,0x0c,0xa6,
+ 0x86,0x3c,0xe7,0x64,0xe7,0x1c,0x07,0xa4,0xf7,0x64,0xe7,0x1c,0x07,0xa7,0x87,0x3c,
+ 0x10,0xe0,0x02,0x60,0x63,0x1e,0x74,0x1e,0x96,0x7f,0x0d,0xa3,0x0c,0xa6,0x86,0x3c,
+ 0x07,0x65,0xe7,0x1c,0x07,0xa4,0x17,0x65,0xe7,0x1c,0x07,0xa7,0x87,0x3c,0x12,0x60,
+ 0x01,0xf0,0x22,0x60,0x63,0x1e,0x74,0x1e,0x8e,0x7f,0xc7,0x64,0xe7,0x1c,0x07,0xa3,
+ 0xd7,0x64,0xe7,0x1c,0x07,0xa7,0x87,0x3c,0x02,0x60,0x73,0x1e,0x8a,0x7f,0xb7,0x64,
+ 0x7e,0x1c,0x0e,0xa2,0x89,0x7f,0x89,0x77,0xc6,0x60,0x07,0xb6,0x07,0xb6,0x88,0x72,
+ 0xf3,0x63,0x84,0x61,0x88,0x7f,0x88,0x77,0x07,0x83,0x07,0x60,0x75,0x12,0x76,0x12,
+ 0x18,0xf0,0x34,0x12,0x64,0x1c,0xa8,0x62,0x84,0x1c,0x04,0xa2,0x14,0x60,0x42,0x0e,
+ 0x12,0x3e,0x64,0x12,0x54,0x34,0x05,0xe8,0x05,0x20,0x45,0x01,0x80,0x7b,0xb2,0x1c,
+ 0x04,0xf0,0x07,0x20,0x47,0x01,0x7a,0x7d,0xd2,0x1c,0x44,0x01,0x02,0xb4,0x06,0x20,
+ 0x66,0x01,0x53,0xa4,0x46,0x0c,0xe5,0xef,0x7a,0x76,0x06,0xb7,0x7a,0x77,0x07,0xb5,
+ 0x86,0x60,0x79,0x77,0x07,0x96,0x74,0x77,0x07,0x87,0x36,0x65,0x76,0x1c,0x06,0xa6,
+ 0x06,0x2a,0x09,0xe0,0x76,0x77,0x07,0xa6,0x76,0x7e,0xe6,0x16,0x07,0xb6,0x07,0xa6,
+ 0x75,0x72,0x26,0x16,0x0f,0xf0,0xc6,0x64,0x76,0x1c,0x06,0xa2,0xd3,0x64,0x37,0x1c,
+ 0x07,0xa7,0x87,0x3c,0x72,0x1e,0x70,0x7f,0x6d,0x77,0x07,0xa6,0x06,0x34,0x07,0xb6,
+ 0x07,0xa6,0x46,0x34,0x07,0xb6,0x69,0x76,0x06,0xa7,0x17,0x34,0x06,0xb7,0x6b,0x75,
+ 0x05,0xa7,0x6b,0x74,0x47,0x16,0x60,0x76,0x06,0x86,0xb4,0x66,0x64,0x1c,0x04,0xa4,
+ 0x47,0x1e,0x05,0xb7,0x68,0x75,0x05,0xa7,0x07,0x34,0x05,0xb7,0x15,0x60,0x66,0x77,
+ 0x07,0xb5,0x05,0x60,0x66,0x77,0x07,0xb5,0x05,0x60,0x65,0x77,0x07,0xd5,0x85,0x32,
+ 0x65,0x77,0x07,0xd5,0x65,0x75,0x65,0x77,0x07,0xd5,0x05,0x62,0x65,0x77,0x07,0xb5,
+ 0x07,0x60,0x12,0xf0,0x64,0x12,0x74,0x1c,0xa3,0x62,0x43,0x1c,0x03,0xa5,0x61,0x78,
+ 0x85,0x1c,0x61,0x7b,0xb4,0x1c,0x04,0xa2,0x05,0xb2,0x03,0xa5,0x60,0x7d,0xd5,0x1c,
+ 0x04,0xa4,0x05,0xb4,0x07,0x20,0x67,0x01,0x56,0xa5,0x57,0x0c,0xeb,0xef,0x5c,0x72,
+ 0x03,0x60,0x44,0x62,0x44,0x7f,0x44,0x77,0x07,0x87,0xbe,0x64,0xe7,0x1c,0x07,0xa7,
+ 0x17,0x2a,0x46,0x77,0x07,0xa6,0x02,0xe8,0x26,0x34,0x02,0xf0,0x56,0x72,0x26,0x16,
+ 0x07,0xb6,0x3d,0x7e,0x0e,0x87,0xf3,0x66,0x37,0x1c,0x07,0xa3,0x53,0x72,0x83,0x3c,
+ 0x53,0x74,0x53,0x7f,0x0e,0x87,0x04,0x67,0x47,0x1c,0x07,0xa7,0x73,0x12,0x43,0x3c,
+ 0x73,0x1e,0x50,0x72,0x43,0x01,0x50,0x74,0x33,0x7f,0x0e,0x87,0x50,0x75,0x57,0x1c,
+ 0x07,0xa7,0x73,0x12,0x43,0x3c,0x73,0x1e,0x4e,0x72,0x43,0x01,0x54,0x62,0x2d,0x7f,
+ 0x0e,0x87,0x4c,0x76,0x76,0x1c,0x06,0xa6,0x06,0x2a,0xd2,0xe8,0xe6,0x64,0x76,0x1c,
+ 0x06,0xa6,0xf5,0x64,0x75,0x1c,0x05,0xa2,0x82,0x3c,0x62,0x1e,0xc6,0x64,0x76,0x1c,
+ 0x06,0xa5,0xd6,0x64,0x76,0x1c,0x06,0xa6,0x86,0x3c,0x56,0x1e,0x43,0x78,0x87,0x1c,
+ 0xf1,0x11,0x62,0x03,0xf9,0x11,0x07,0xa3,0x07,0x7f,0x23,0x12,0x63,0x01,0x12,0x60,
+ 0x19,0x7f,0x15,0x60,0x3e,0x77,0x07,0xb5,0x0e,0x87,0x3d,0x7b,0x7b,0xf0,0x00,0x00,
+ 0x98,0x00,0x00,0x00,0xd0,0x83,0x00,0x00,0x00,0xfe,0xff,0xff,0x30,0x9e,0x00,0x00,
+ 0x31,0x9e,0x00,0x00,0x32,0x9e,0x00,0x00,0x33,0x9e,0x00,0x00,0x34,0x9e,0x00,0x00,
+ 0x35,0x9e,0x00,0x00,0xa7,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x36,0x9e,0x00,0x00,
+ 0x37,0x9e,0x00,0x00,0x38,0x9e,0x00,0x00,0x39,0x9e,0x00,0x00,0x3a,0x9e,0x00,0x00,
+ 0x3b,0x9e,0x00,0x00,0x3c,0x9e,0x00,0x00,0x3d,0x9e,0x00,0x00,0x86,0x9e,0x00,0x00,
+ 0x0c,0x27,0x00,0x00,0x5a,0x27,0x00,0x00,0x78,0x2e,0x00,0x00,0x62,0x08,0x04,0x00,
+ 0x00,0x08,0x04,0x00,0xc4,0x09,0x00,0x00,0x1c,0x9e,0x00,0x00,0x0c,0x08,0x04,0x00,
+ 0x58,0x08,0x04,0x00,0x59,0x08,0x04,0x00,0x2c,0x08,0x04,0x00,0x40,0x08,0x04,0x00,
+ 0xfe,0x00,0x00,0x00,0xef,0x00,0x00,0x00,0xc4,0x30,0x00,0x00,0x41,0x08,0x04,0x00,
+ 0xf8,0xff,0xff,0xff,0x68,0x08,0x04,0x00,0x69,0x08,0x04,0x00,0x84,0x08,0x04,0x00,
+ 0x8c,0x08,0x04,0x00,0x8e,0x08,0x04,0x00,0x96,0x00,0x00,0x00,0x90,0x08,0x04,0x00,
+ 0x98,0x08,0x04,0x00,0xd0,0x08,0x04,0x00,0xd0,0x00,0x00,0x00,0xe8,0x08,0x04,0x00,
+ 0x14,0x09,0x04,0x00,0xfb,0x00,0x00,0x00,0x00,0x40,0x02,0x00,0x60,0x03,0x00,0x00,
+ 0xd8,0x09,0x00,0x00,0x00,0x20,0x02,0x00,0xbc,0x01,0x00,0x00,0xcb,0x00,0x00,0x00,
+ 0xbc,0x21,0x02,0x00,0xc8,0x00,0x00,0x00,0xca,0x00,0x00,0x00,0x9c,0x08,0x04,0x00,
+ 0xc9,0x00,0x00,0x00,0xb7,0x1c,0x07,0xa6,0x06,0x2a,0x96,0x77,0x06,0xe0,0x07,0x85,
+ 0x35,0x31,0x07,0x95,0x95,0x77,0x07,0xb6,0x0a,0xf0,0x16,0x2a,0x07,0x86,0x03,0xe0,
+ 0x36,0x35,0x07,0x96,0x04,0xf0,0x36,0x31,0x07,0x96,0x8f,0x77,0x07,0xb5,0x8f,0x76,
+ 0x06,0xa7,0x57,0x34,0x06,0xb7,0x8e,0x75,0x05,0xa6,0x8e,0x77,0x07,0x87,0xbd,0x66,
+ 0xd7,0x1c,0x07,0xa7,0x47,0x3c,0x8c,0x7e,0xe6,0x16,0x67,0x1e,0x47,0x01,0x05,0xb7,
+ 0x86,0x2d,0x8a,0x77,0x07,0x96,0x06,0x2c,0x8a,0x77,0x07,0x96,0x06,0x60,0x89,0x77,
+ 0x07,0xb6,0xf6,0x61,0x89,0x77,0x07,0xb6,0x89,0x76,0x06,0xa7,0x17,0x34,0x04,0xf0,
+ 0x87,0x76,0x06,0xa7,0x87,0x72,0x27,0x16,0x06,0xb7,0x86,0x7f,0x02,0x61,0x86,0x7f,
+ 0x02,0x60,0x86,0x77,0x07,0xb2,0x86,0x77,0x07,0x84,0x7a,0x73,0x03,0x85,0x16,0x67,
+ 0x56,0x1c,0x06,0xa6,0x66,0x3d,0x64,0x31,0x74,0x31,0x46,0x1e,0x07,0x96,0x07,0x84,
+ 0xe6,0x66,0x56,0x1c,0x06,0xa6,0x16,0x3d,0x14,0x31,0x24,0x31,0x46,0x1e,0x07,0x96,
+ 0x07,0x84,0xd6,0x66,0x56,0x1c,0x06,0xa6,0x06,0x3d,0x04,0x31,0x46,0x1e,0x07,0x96,
+ 0x79,0x74,0x04,0x86,0xc7,0x66,0x75,0x1c,0x05,0xa7,0xd7,0x3c,0xd6,0x30,0xe6,0x30,
+ 0x67,0x1e,0x04,0x97,0x75,0x75,0x05,0xb2,0x07,0x60,0xf2,0x61,0x34,0x60,0x0f,0xf0,
+ 0x76,0x1c,0x66,0xa6,0x62,0x0c,0x09,0xe0,0x05,0xae,0xf6,0x25,0x06,0x30,0x48,0x12,
+ 0x68,0x1b,0x86,0x12,0xe6,0x1e,0x46,0x01,0x05,0xb6,0x07,0x20,0x47,0x01,0x03,0x86,
+ 0x46,0xae,0xe7,0x0c,0xed,0xef,0x69,0x72,0x03,0x60,0x69,0x74,0x6a,0x7f,0x07,0x60,
+ 0x6a,0x76,0x06,0xb7,0x15,0x60,0x69,0x76,0x06,0xb5,0x69,0x76,0x06,0xb7,0x63,0x73,
+ 0x3e,0x12,0x06,0x60,0x62,0x12,0x3a,0x12,0x6b,0x64,0x0c,0x2c,0xfd,0x67,0x1b,0xf0,
+ 0xe7,0x12,0x47,0x1c,0x64,0x78,0x87,0x1c,0x07,0xb2,0x57,0x12,0x37,0x3c,0x57,0x05,
+ 0x97,0x1c,0xa7,0x1c,0x61,0x78,0x87,0x1c,0x37,0xbc,0x47,0xbd,0x57,0xbc,0x67,0xbd,
+ 0x77,0xb2,0x87,0xb2,0x05,0x20,0x64,0x20,0xa5,0x2a,0xea,0xe7,0x06,0x20,0x6f,0x64,
+ 0xfe,0x1c,0x46,0x2a,0x24,0xe8,0x04,0x60,0x45,0x12,0x69,0x12,0xf1,0x11,0xb9,0x03,
+ 0xf9,0x11,0xde,0xf7,0x47,0x12,0x37,0x3c,0x37,0x1c,0x54,0x78,0x87,0x1c,0x0d,0x60,
+ 0x07,0xb5,0x27,0x12,0x47,0x1c,0x52,0x7b,0xb7,0x1c,0x37,0x3c,0xe7,0x1c,0x0b,0x60,
+ 0x37,0xbc,0x47,0xb5,0x57,0xb5,0x67,0xb5,0x77,0xb5,0x87,0xb5,0x04,0x20,0xa4,0x2a,
+ 0xe9,0xe7,0x06,0x20,0x0f,0x65,0xf3,0x1c,0x46,0x2a,0x05,0xe0,0x0b,0xf0,0x06,0x60,
+ 0x6c,0x12,0x65,0x12,0x3e,0x7e,0x04,0x60,0x62,0x12,0x62,0x1c,0x67,0x12,0x37,0x3c,
+ 0x72,0x1c,0xd8,0xf7,0x44,0x72,0xb3,0x12,0x3b,0x7f,0x43,0x7e,0x44,0x77,0x0e,0xb7,
+ 0x17,0x01,0x1e,0xb1,0x27,0x01,0x2e,0xb1,0x87,0x3f,0x3e,0xb7,0x47,0x61,0x4e,0xb7,
+ 0x40,0x7c,0x0c,0xa7,0x5e,0xb7,0x1c,0xa7,0x6e,0xb7,0x9e,0xbb,0x22,0x7a,0x0a,0x87,
+ 0x42,0x67,0x27,0x1c,0x07,0xa7,0xae,0xb7,0x07,0x60,0xbe,0xb7,0x3a,0x77,0x07,0xa6,
+ 0x07,0xbd,0x17,0xa6,0x17,0xbd,0x38,0x77,0x27,0xbd,0x37,0xbd,0x07,0xbd,0x17,0xbd,
+ 0x47,0xbd,0x57,0xbd,0x36,0x7f,0x36,0x76,0x06,0x87,0x47,0x30,0x57,0x30,0x06,0x97,
+ 0x35,0x76,0x35,0x77,0x07,0xd6,0x35,0x76,0x36,0x77,0x07,0xd6,0x1c,0xa7,0x87,0x3c,
+ 0x0c,0xa6,0x67,0x1e,0x34,0x76,0x06,0xd7,0xa6,0x61,0x33,0x77,0x07,0xb6,0x33,0x77,
+ 0x07,0xbd,0x0a,0x87,0x43,0x67,0x37,0x1c,0x07,0xa6,0x31,0x77,0x07,0xd6,0x4e,0xa6,
+ 0x31,0x77,0x07,0xb6,0x31,0x7f,0x31,0x77,0x07,0x9b,0xf0,0x20,0x68,0x00,0xf0,0x21,
+ 0xcf,0x00,0x00,0x00,0x08,0x01,0x04,0x00,0x9d,0x08,0x04,0x00,0x40,0x08,0x04,0x00,
+ 0x41,0x08,0x04,0x00,0x1c,0x9e,0x00,0x00,0x8f,0xff,0xff,0xff,0xa0,0x08,0x04,0x00,
+ 0xa4,0x08,0x04,0x00,0xcc,0x08,0x04,0x00,0x66,0x09,0x04,0x00,0x68,0x08,0x04,0x00,
+ 0xfd,0x00,0x00,0x00,0x5e,0x31,0x00,0x00,0x72,0x2d,0x00,0x00,0x3c,0x01,0x04,0x00,
+ 0x04,0x01,0x04,0x00,0x0c,0x01,0x04,0x00,0x48,0x01,0x04,0x00,0x8a,0x9e,0x00,0x00,
+ 0xe9,0x05,0x00,0x00,0xc4,0x09,0x00,0x00,0x63,0xa4,0x00,0x00,0x64,0xa4,0x00,0x00,
+ 0xe5,0xa3,0x00,0x00,0x02,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x1a,0x04,0x00,0x00,
+ 0x83,0x00,0x00,0x00,0xfa,0xa3,0x00,0x00,0x7a,0x9e,0x00,0x00,0x00,0xe0,0x02,0x00,
+ 0x20,0x9e,0x00,0x00,0x00,0x08,0x03,0x00,0x73,0xa4,0x00,0x00,0x04,0x31,0x00,0x00,
+ 0x04,0x0a,0x04,0x00,0x36,0x50,0x00,0x00,0x40,0x0a,0x04,0x00,0x00,0x68,0x00,0x00,
+ 0x42,0x0a,0x04,0x00,0x44,0x0a,0x04,0x00,0x46,0x0a,0x04,0x00,0x47,0x0a,0x04,0x00,
+ 0x48,0x0a,0x04,0x00,0x4a,0x0a,0x04,0x00,0x2c,0x31,0x00,0x00,0x4c,0x06,0x04,0x00,
+ 0x16,0x60,0x82,0x77,0x07,0xb6,0x82,0x76,0x06,0x87,0x82,0x75,0x57,0x1e,0x06,0x97,
+ 0x82,0x76,0x06,0x87,0xd7,0x34,0x06,0x97,0x81,0x76,0x06,0x87,0xe7,0x35,0xf7,0x35,
+ 0x06,0x97,0xcf,0x00,0xcf,0x00,0x7e,0x72,0xcf,0x00,0xf0,0x24,0x7d,0x00,0x7d,0x77,
+ 0x07,0x83,0x7d,0x77,0x37,0x1c,0x07,0xa7,0x07,0x2a,0x32,0xe8,0x7c,0x77,0x17,0xa6,
+ 0x7c,0x74,0x05,0x60,0x7c,0x72,0x0b,0xf0,0x37,0x12,0x57,0x1c,0xaf,0x62,0xf7,0x1c,
+ 0x07,0xa7,0x77,0x1c,0x27,0x1c,0x07,0xc7,0x04,0xd7,0x05,0x20,0x14,0x20,0x05,0x01,
+ 0x61,0x0c,0xf2,0xef,0x72,0x77,0x07,0xad,0x05,0x60,0x54,0x12,0xff,0x61,0x70,0x7e,
+ 0x71,0x71,0x66,0x1c,0x11,0xf0,0x37,0x12,0x47,0x1c,0x67,0xa7,0x52,0x12,0x62,0x1c,
+ 0xe2,0x1c,0x7f,0x0c,0x02,0xe8,0x77,0x21,0x01,0xf0,0xb7,0x24,0x77,0x1c,0x17,0x1c,
+ 0x07,0xc7,0x02,0xd7,0x04,0x20,0x15,0x20,0x47,0x12,0x47,0x01,0xd7,0x0c,0xeb,0xef,
+ 0x6d,0x00,0xf0,0x20,0xcf,0x00,0x70,0x24,0x7e,0x00,0x63,0x77,0x07,0xa6,0x06,0x20,
+ 0x46,0x01,0x07,0xb6,0x62,0x77,0x07,0xa7,0x07,0x2a,0x07,0xe0,0x5a,0x77,0x07,0x87,
+ 0x85,0x67,0x75,0x1c,0x05,0xa5,0x05,0x2a,0x0d,0xe0,0x06,0x60,0x5b,0x77,0x07,0xb6,
+ 0x55,0x77,0x07,0x87,0xb6,0x64,0x67,0x1c,0x07,0xa7,0x0e,0x60,0x17,0x2a,0x12,0xe0,
+ 0x58,0x7f,0x10,0xf0,0x95,0x67,0x75,0x1c,0x05,0xa5,0x1e,0x60,0x56,0x0c,0x0a,0xe8,
+ 0xb6,0x64,0x67,0x1c,0x07,0xa7,0x17,0x2a,0x01,0xe0,0x51,0x7f,0x06,0x60,0x4e,0x77,
+ 0x07,0xb6,0x0e,0x60,0xe2,0x12,0x6e,0x00,0x70,0x20,0xcf,0x00,0xf0,0x24,0x20,0x9f,
+ 0x30,0x9e,0x44,0x77,0x07,0x8e,0x4e,0xa2,0x5e,0xa3,0x4a,0x77,0x00,0x97,0x24,0x60,
+ 0x4a,0x75,0x06,0x60,0x67,0x12,0x49,0x7f,0x40,0x77,0x7e,0x1c,0x0e,0xa7,0x07,0x2a,
+ 0x13,0xe8,0x3e,0x77,0x17,0xa6,0x07,0xa7,0x76,0x1c,0x07,0x60,0x45,0x73,0x3c,0x74,
+ 0x09,0xf0,0x75,0x12,0x75,0x1c,0x32,0x12,0x52,0x1c,0x45,0x1c,0x05,0xc5,0x02,0xd5,
+ 0x07,0x20,0x47,0x01,0x67,0x0d,0xf5,0xe7,0x20,0x8f,0x30,0x8e,0xf0,0x20,0xcf,0x00,
+ 0x70,0x24,0x00,0x9f,0x06,0x60,0x3b,0x77,0x07,0xb6,0x16,0x60,0x3b,0x77,0x07,0xb6,
+ 0xa2,0x60,0x3a,0x7f,0x00,0x8f,0x70,0x20,0xcf,0x00,0x37,0x77,0x07,0xa6,0x16,0x36,
+ 0xfd,0xef,0xcf,0x00,0xcf,0x00,0xcf,0x00,0x42,0x01,0x03,0x01,0x35,0x76,0xa7,0x61,
+ 0xf1,0x11,0x27,0x03,0xf9,0x11,0x17,0x1c,0x77,0x1c,0x76,0x1c,0x06,0xc5,0x54,0x12,
+ 0x74,0x01,0x30,0x77,0x07,0xa6,0x30,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x74,0x0d,
+ 0x17,0xe0,0x2e,0x77,0x27,0xa3,0x37,0xa6,0x86,0x3c,0x36,0x1e,0x06,0x20,0x66,0x01,
+ 0x27,0xb6,0x86,0x3e,0x37,0xb6,0x47,0xa3,0x57,0xa6,0x86,0x3c,0x36,0x1e,0x46,0x0d,
+ 0x07,0xe8,0x56,0x12,0x66,0x01,0x47,0xb6,0x86,0x3e,0x57,0xb6,0x67,0xb2,0x77,0xb1,
+ 0x23,0x77,0x87,0xa6,0x62,0x0c,0x01,0xe0,0x87,0xb2,0x97,0xa7,0x71,0x0c,0x02,0xe0,
+ 0x1f,0x77,0x97,0xb1,0x1e,0x77,0xa7,0xa6,0x26,0x0c,0x01,0xe0,0xa7,0xb2,0xb7,0xa7,
+ 0x17,0x0c,0x02,0xe0,0x1a,0x77,0xb7,0xb1,0xcf,0x00,0x00,0x00,0x40,0x01,0x04,0x00,
+ 0x0c,0x01,0x04,0x00,0x00,0x00,0x0f,0x00,0x04,0x01,0x04,0x00,0x08,0x01,0x04,0x00,
+ 0x00,0x80,0x02,0x00,0x1c,0x9e,0x00,0x00,0xc8,0x00,0x00,0x00,0x20,0x9e,0x00,0x00,
+ 0xec,0xa4,0x00,0x00,0xc0,0x86,0x02,0x00,0x5c,0xa5,0x00,0x00,0x20,0xaa,0x00,0x00,
+ 0x12,0x2c,0x00,0x00,0x36,0x00,0x04,0x1a,0x00,0x00,0x02,0x18,0xb4,0x26,0x00,0x00,
+ 0x5e,0xa5,0x00,0x00,0x74,0x08,0x04,0x00,0x70,0x08,0x04,0x00,0xe0,0x0a,0x00,0x00,
+ 0x00,0xe0,0x02,0x00,0x44,0xa4,0x00,0x00,0x45,0xa4,0x00,0x00,0x73,0xa4,0x00,0x00,
+ 0x70,0x25,0x7a,0x00,0x42,0x01,0x43,0x01,0x04,0x01,0xad,0x77,0x07,0xa7,0xad,0x76,
+ 0x06,0xaf,0x8f,0x3c,0x7f,0x1e,0x86,0x2c,0x5e,0x60,0xec,0x12,0x05,0x60,0xaa,0x7a,
+ 0x1a,0xf0,0x52,0x0f,0x16,0xe8,0x87,0x65,0x57,0x1c,0x77,0x1c,0xa7,0x1c,0x87,0xad,
+ 0x3d,0x14,0x5d,0x01,0xed,0x01,0x4d,0x01,0x97,0xa7,0x17,0x14,0x57,0x01,0xe7,0x01,
+ 0x47,0x01,0x7b,0x12,0xdb,0x1c,0x4b,0x01,0x6b,0x0c,0x03,0xe0,0xb6,0x12,0x7e,0x12,
+ 0xdc,0x12,0x05,0x20,0x45,0x01,0xf5,0x0c,0xe4,0xef,0x22,0x60,0x62,0x0c,0x09,0xe8,
+ 0x02,0x60,0x26,0x2a,0x06,0xe0,0x62,0x12,0x0c,0x2a,0x03,0xe8,0x0e,0x2a,0x22,0x00,
+ 0x22,0x28,0x6a,0x00,0x70,0x21,0xcf,0x00,0xf0,0x25,0x79,0x00,0x27,0x12,0x47,0x01,
+ 0x03,0x01,0x45,0x01,0x5b,0x12,0x5b,0x1c,0x02,0x60,0x2f,0x12,0x07,0x24,0x01,0x24,
+ 0x4c,0x61,0x1b,0xf0,0xe3,0x12,0xa3,0x1c,0x33,0x1c,0x43,0x1c,0x3d,0x12,0xbd,0x1c,
+ 0x03,0xc6,0x76,0x01,0x1d,0xc9,0x79,0x01,0x96,0x1c,0x13,0xc3,0x73,0x01,0x36,0x05,
+ 0x0d,0xc3,0x73,0x01,0x36,0x05,0xe6,0x01,0x6c,0x0c,0x01,0xe0,0x62,0x1c,0x0e,0x20,
+ 0x4e,0x01,0x1e,0x0d,0xe7,0xe7,0x0f,0x20,0x4f,0x01,0x7f,0x0d,0x06,0xe8,0xfa,0x12,
+ 0xf1,0x11,0x5a,0x03,0xf9,0x11,0x0e,0x60,0xf4,0xf7,0x69,0x00,0xf0,0x21,0xcf,0x00,
+ 0xf0,0x24,0x7d,0x00,0x7a,0x77,0x07,0xa2,0x17,0xa3,0x79,0x74,0x85,0x61,0x79,0x7f,
+ 0x7a,0x7d,0x0d,0xa5,0x05,0x2a,0x79,0x77,0x7a,0x7e,0x03,0xe0,0x07,0x92,0x0e,0x92,
+ 0x2d,0xf0,0x0e,0x86,0x26,0x1c,0x0e,0x96,0x07,0x87,0x72,0x05,0xe2,0x01,0x75,0x77,
+ 0x07,0x87,0x75,0x74,0x74,0x1c,0x04,0xa4,0x75,0x73,0x37,0x1c,0x07,0xa7,0x87,0x3c,
+ 0x47,0x1e,0x27,0x0d,0x04,0xe8,0x06,0x60,0x72,0x77,0x07,0xb6,0x15,0xf0,0x87,0x60,
+ 0x57,0x0c,0x14,0xe0,0x15,0x60,0x6e,0x77,0x07,0xb5,0x62,0x12,0xa3,0x60,0x6d,0x7f,
+ 0x0e,0x92,0x6d,0x77,0x07,0xb2,0x12,0x01,0x6d,0x77,0x07,0xb1,0x22,0x01,0x6c,0x77,
+ 0x07,0xb1,0x82,0x3f,0x6c,0x77,0x07,0xb2,0x07,0x2c,0x0d,0xb7,0x5f,0x77,0x07,0xa6,
+ 0x06,0x20,0x07,0xb6,0x65,0x77,0x07,0xa6,0x65,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,
+ 0x64,0x76,0x06,0xa6,0x06,0x3d,0x76,0x1e,0x63,0x77,0x07,0xa7,0x87,0x3d,0x67,0x1e,
+ 0x62,0x7e,0x0e,0xa5,0x62,0x74,0x04,0xa6,0x86,0x3c,0x56,0x1e,0x61,0x75,0x05,0xa2,
+ 0x02,0x3d,0x62,0x1e,0x60,0x76,0x06,0xa3,0x83,0x3d,0x23,0x1e,0x37,0x0c,0x07,0xe0,
+ 0x0e,0xb7,0x17,0x01,0x04,0xb1,0x27,0x01,0x05,0xb1,0x87,0x3f,0x06,0xb7,0x6d,0x00,
+ 0xf0,0x20,0xcf,0x00,0x70,0x24,0x7e,0x00,0x58,0x7e,0x0e,0xa7,0x17,0x2a,0x03,0xe0,
+ 0x27,0x60,0x0e,0xb7,0xff,0xf0,0x27,0x2a,0xfd,0xe0,0x54,0x7f,0x55,0x77,0x07,0xa7,
+ 0x07,0x2a,0xd0,0xe8,0xfe,0xa7,0x07,0x2a,0x20,0xe8,0x52,0x77,0x07,0xa7,0x52,0x76,
+ 0x06,0xa6,0x86,0x3c,0x76,0x1e,0x51,0x77,0x07,0xa7,0x07,0x3d,0x67,0x1e,0x50,0x76,
+ 0x06,0xa6,0x86,0x3d,0x76,0x1e,0x4f,0x77,0x07,0xa5,0x4f,0x77,0x07,0xa7,0x87,0x3c,
+ 0x57,0x1e,0x4e,0x75,0x05,0xa5,0x05,0x3d,0x75,0x1e,0x4d,0x77,0x07,0xa7,0x87,0x3d,
+ 0x57,0x1e,0x67,0x0c,0x02,0xe0,0x07,0x60,0xfe,0xb7,0x4a,0x77,0x07,0xa7,0x07,0x2a,
+ 0x3e,0x77,0x26,0xe8,0x06,0x60,0xa7,0xb6,0x34,0x76,0x06,0xa6,0x34,0x75,0x05,0xa5,
+ 0x85,0x3c,0x65,0x1e,0x33,0x76,0x06,0xa6,0x06,0x3d,0x56,0x1e,0x32,0x75,0x05,0xa5,
+ 0x85,0x3d,0x65,0x1e,0x41,0x76,0x06,0xa4,0x41,0x76,0x06,0xa6,0x86,0x3c,0x46,0x1e,
+ 0x40,0x74,0x04,0xa4,0x04,0x3d,0x64,0x1e,0x3f,0x76,0x06,0xa6,0x86,0x3d,0x46,0x1e,
+ 0x16,0x3e,0x65,0x0c,0xaf,0xe0,0x16,0x60,0x3c,0x75,0x05,0xb6,0xf7,0xb6,0xaa,0xf0,
+ 0x16,0x60,0xa7,0xb6,0xf7,0xa6,0x06,0x2a,0x1b,0x76,0x06,0x85,0x0e,0xe8,0x37,0x76,
+ 0x56,0x1c,0x06,0xa4,0x37,0x76,0x56,0x1c,0x06,0xa6,0x86,0x3c,0x46,0x1e,0xb7,0xb6,
+ 0x86,0x3e,0xc7,0xb6,0x34,0x74,0x45,0x1c,0x0d,0xf0,0x33,0x76,0x56,0x1c,0x06,0xa4,
+ 0x33,0x76,0x56,0x1c,0x06,0xa6,0x86,0x3c,0x46,0x1e,0xb7,0xb6,0x86,0x3e,0xc7,0xb6,
+ 0x30,0x76,0x65,0x1c,0x05,0xa6,0xd7,0xb6,0x06,0x60,0xe7,0xb6,0x83,0xf0,0x00,0x00,
+ 0x40,0x9f,0x00,0x00,0x41,0x9f,0x00,0x00,0x8a,0x9e,0x00,0x00,0x20,0x9e,0x00,0x00,
+ 0x00,0x80,0x02,0x00,0x58,0x3e,0x00,0x00,0xce,0xa5,0x00,0x00,0xd0,0xa5,0x00,0x00,
+ 0xd4,0xa5,0x00,0x00,0x1c,0x9e,0x00,0x00,0x1e,0x01,0x00,0x00,0x1f,0x01,0x00,0x00,
+ 0x71,0xa4,0x00,0x00,0xd0,0x83,0x00,0x00,0x6d,0xa4,0x00,0x00,0x6e,0xa4,0x00,0x00,
+ 0x6f,0xa4,0x00,0x00,0x70,0xa4,0x00,0x00,0x69,0xa4,0x00,0x00,0x6a,0xa4,0x00,0x00,
+ 0x6b,0xa4,0x00,0x00,0x6c,0xa4,0x00,0x00,0x66,0x9e,0x00,0x00,0xc0,0x3e,0x00,0x00,
+ 0x64,0xa4,0x00,0x00,0x37,0xaa,0x00,0x00,0x38,0xaa,0x00,0x00,0x39,0xaa,0x00,0x00,
+ 0x3a,0xaa,0x00,0x00,0x76,0x9e,0x00,0x00,0x77,0x9e,0x00,0x00,0x78,0x9e,0x00,0x00,
+ 0x79,0x9e,0x00,0x00,0xa5,0xa0,0x00,0x00,0x65,0xa4,0x00,0x00,0x66,0xa4,0x00,0x00,
+ 0x67,0xa4,0x00,0x00,0x68,0xa4,0x00,0x00,0x31,0xaa,0x00,0x00,0x0f,0x01,0x00,0x00,
+ 0x10,0x01,0x00,0x00,0x0e,0x01,0x00,0x00,0x0c,0x01,0x00,0x00,0x0d,0x01,0x00,0x00,
+ 0xba,0x00,0x00,0x00,0x17,0x60,0xad,0x76,0x06,0xb7,0xfe,0xb7,0xad,0x77,0x07,0xa7,
+ 0xad,0x76,0x06,0xa6,0x86,0x3c,0x76,0x1e,0xac,0x77,0x07,0xa7,0x07,0x3d,0x67,0x1e,
+ 0xab,0x76,0x06,0xa6,0x86,0x3d,0x76,0x1e,0xaa,0x77,0x07,0x87,0xaa,0x75,0x75,0x1c,
+ 0x05,0xa5,0xa9,0x74,0x47,0x1c,0x07,0xa7,0x87,0x3c,0x57,0x1e,0x67,0x1c,0xa7,0x76,
+ 0x06,0xb7,0x17,0x01,0xa7,0x76,0x06,0xb1,0x27,0x01,0xa6,0x76,0x06,0xb1,0x87,0x3f,
+ 0xa6,0x76,0x06,0xb7,0x6e,0x00,0x70,0x20,0xcf,0x00,0xa4,0x77,0x37,0xa6,0x06,0x2a,
+ 0xa4,0xe0,0xa3,0x75,0x05,0xa6,0x06,0x2a,0x40,0xe0,0xa2,0x76,0x06,0xa4,0xa2,0x76,
+ 0x06,0xa6,0x86,0x3c,0x46,0x1e,0xa1,0x74,0x04,0xa4,0x04,0x3d,0x64,0x1e,0xa0,0x76,
+ 0x06,0xa6,0x86,0x3d,0x46,0x1e,0x06,0x2a,0xbd,0xe1,0x9e,0x76,0x06,0xa4,0x9e,0x76,
+ 0x06,0xa6,0x86,0x3c,0x46,0x1e,0x9d,0x74,0x04,0xa4,0x04,0x3d,0x64,0x1e,0x9c,0x76,
+ 0x06,0xa6,0x86,0x3d,0x46,0x1e,0x06,0x2a,0x02,0xe8,0x16,0x60,0xa6,0xf0,0x17,0xa6,
+ 0x06,0x20,0x46,0x01,0x17,0xb6,0x55,0x60,0x65,0x0c,0xba,0xe1,0x16,0x60,0x17,0xb6,
+ 0xa7,0xb6,0x83,0x76,0x06,0x85,0x93,0x76,0x56,0x1c,0x06,0xa4,0x93,0x76,0x56,0x1c,
+ 0x06,0xa6,0x86,0x3c,0x46,0x1e,0xb7,0xb6,0x86,0x3e,0xc7,0xb6,0x90,0x72,0x25,0x1c,
+ 0x05,0xa6,0xd7,0xb6,0x06,0x60,0xe7,0xb6,0xa3,0xf1,0x16,0x2a,0x2b,0xe0,0x81,0x74,
+ 0x04,0xa3,0x81,0x74,0x04,0xa4,0x84,0x3c,0x34,0x1e,0x80,0x73,0x03,0xa3,0x03,0x3d,
+ 0x43,0x1e,0x7f,0x74,0x04,0xa4,0x84,0x3d,0x34,0x1e,0x04,0x2a,0x09,0xe8,0x17,0xa5,
+ 0x05,0x20,0x45,0x01,0x17,0xb5,0x34,0x60,0x54,0x0c,0x8a,0xe1,0x17,0xb6,0x41,0xf0,
+ 0x79,0x74,0x04,0xa3,0x79,0x74,0x04,0xa4,0x84,0x3c,0x34,0x1e,0x78,0x73,0x03,0xa3,
+ 0x03,0x3d,0x43,0x1e,0x77,0x74,0x04,0xa4,0x84,0x3d,0x34,0x1e,0x17,0xb6,0x04,0x2a,
+ 0x4d,0xe9,0x5c,0xf0,0x26,0x2a,0x74,0xe1,0x6b,0x74,0x04,0xa3,0x6b,0x74,0x04,0xa4,
+ 0x84,0x3c,0x34,0x1e,0x6a,0x73,0x03,0xa3,0x03,0x3d,0x43,0x1e,0x69,0x74,0x04,0xa4,
+ 0x84,0x3d,0x34,0x1e,0x04,0x2a,0x4e,0xe1,0x67,0x74,0x04,0xa3,0x67,0x74,0x04,0xa4,
+ 0x84,0x3c,0x34,0x1e,0x66,0x73,0x03,0xa3,0x03,0x3d,0x43,0x1e,0x65,0x74,0x04,0xa4,
+ 0x84,0x3d,0x34,0x1e,0x04,0x2a,0x0f,0xe8,0x17,0xa4,0x04,0x20,0x44,0x01,0x17,0xb4,
+ 0x50,0x75,0x05,0x85,0x63,0x73,0x35,0x1c,0x05,0xa5,0x45,0x0c,0x49,0xe1,0x15,0x60,
+ 0x17,0xb5,0x37,0xb6,0x45,0xf1,0x16,0x60,0xbd,0xf0,0x16,0x2a,0xbd,0xe0,0x05,0x60,
+ 0xa7,0xb5,0x4f,0x75,0x05,0xa3,0x03,0x2a,0x30,0xe0,0x4e,0x74,0x04,0xa3,0x4e,0x74,
+ 0x04,0xa4,0x84,0x3c,0x34,0x1e,0x4d,0x73,0x03,0xa3,0x03,0x3d,0x43,0x1e,0x4c,0x74,
+ 0x04,0xa4,0x84,0x3d,0x34,0x1e,0x04,0x2a,0x16,0xe1,0x4a,0x74,0x04,0xa3,0x4a,0x74,
+ 0x04,0xa4,0x84,0x3c,0x34,0x1e,0x49,0x73,0x03,0xa3,0x03,0x3d,0x43,0x1e,0x48,0x74,
+ 0x04,0xa4,0x84,0x3d,0x34,0x1e,0x04,0x2a,0x04,0xe8,0x17,0xb6,0x27,0x60,0x05,0xb7,
+ 0x17,0xf1,0x17,0xa3,0x03,0x20,0x43,0x01,0x17,0xb3,0x31,0x75,0x05,0x85,0x44,0x72,
+ 0x25,0x1c,0x05,0xa5,0x35,0x0c,0x0c,0xe1,0xbb,0xf0,0x13,0x2a,0x22,0xe0,0x35,0x76,
+ 0x06,0xa4,0x35,0x76,0x06,0xa6,0x86,0x3c,0x46,0x1e,0x34,0x74,0x04,0xa4,0x04,0x3d,
+ 0x64,0x1e,0x33,0x76,0x06,0xa6,0x86,0x3d,0x46,0x1e,0x06,0x2a,0xf9,0xe0,0x31,0x76,
+ 0x06,0xa4,0x31,0x76,0x06,0xa6,0x86,0x3c,0x46,0x1e,0x30,0x74,0x04,0xa4,0x04,0x3d,
+ 0x64,0x1e,0x2f,0x76,0x06,0xa6,0x86,0x3d,0x46,0x1e,0x17,0xb3,0x06,0x2a,0xe7,0xe8,
+ 0xcd,0xf7,0x23,0x2a,0xe5,0xe0,0x23,0x74,0x04,0xa2,0x23,0x74,0x04,0xa4,0x84,0x3c,
+ 0x24,0x1e,0x22,0x72,0x02,0xa2,0x02,0x3d,0x42,0x1e,0x21,0x74,0x04,0xa4,0x84,0x3d,
+ 0x24,0x1e,0x04,0x2a,0xc0,0xe0,0x1f,0x74,0x04,0xa2,0x1f,0x74,0x04,0xa4,0x84,0x3c,
+ 0x24,0x1e,0x1e,0x72,0x02,0xa2,0x02,0x3d,0x42,0x1e,0x1d,0x74,0x04,0xa4,0x84,0x3d,
+ 0x24,0x1e,0x04,0x2a,0x3f,0xe8,0x0a,0x75,0x05,0x85,0x1d,0x74,0x45,0x1c,0x17,0xa4,
+ 0x05,0xa5,0x45,0x0c,0xbd,0xe0,0x17,0xb6,0x37,0xb3,0xba,0xf0,0x31,0xaa,0x00,0x00,
+ 0x37,0xaa,0x00,0x00,0x38,0xaa,0x00,0x00,0x39,0xaa,0x00,0x00,0x3a,0xaa,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0xb7,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x76,0x9e,0x00,0x00,
+ 0x77,0x9e,0x00,0x00,0x78,0x9e,0x00,0x00,0x79,0x9e,0x00,0x00,0x66,0x9e,0x00,0x00,
+ 0xd8,0xa5,0x00,0x00,0x46,0xa4,0x00,0x00,0x47,0xa4,0x00,0x00,0x48,0xa4,0x00,0x00,
+ 0x49,0xa4,0x00,0x00,0x4a,0xa4,0x00,0x00,0x4b,0xa4,0x00,0x00,0x4c,0xa4,0x00,0x00,
+ 0x4d,0xa4,0x00,0x00,0x0c,0x01,0x00,0x00,0x0d,0x01,0x00,0x00,0xba,0x00,0x00,0x00,
+ 0xb9,0x00,0x00,0x00,0x17,0xb6,0x5a,0xf0,0x26,0x2a,0x82,0xe0,0x05,0x60,0xa7,0xb5,
+ 0x92,0x75,0x05,0xa3,0x03,0x2a,0x2f,0xe0,0x91,0x74,0x04,0xa3,0x91,0x74,0x04,0xa4,
+ 0x84,0x3c,0x34,0x1e,0x90,0x73,0x03,0xa3,0x03,0x3d,0x43,0x1e,0x8f,0x74,0x04,0xa4,
+ 0x84,0x3d,0x34,0x1e,0x04,0x2a,0x56,0xe0,0x8d,0x74,0x04,0xa3,0x8d,0x74,0x04,0xa4,
+ 0x84,0x3c,0x34,0x1e,0x8c,0x73,0x03,0xa3,0x03,0x3d,0x43,0x1e,0x8b,0x74,0x04,0xa4,
+ 0x84,0x3d,0x34,0x1e,0x04,0x2a,0x59,0xe0,0x17,0xa5,0x05,0x20,0x45,0x01,0x17,0xb5,
+ 0x87,0x76,0x06,0x86,0x87,0x72,0x26,0x1c,0x06,0xa6,0x56,0x0c,0x51,0xe0,0x16,0x60,
+ 0x17,0xb6,0x37,0xb4,0x4d,0xf0,0x13,0x2a,0x23,0xe0,0x78,0x74,0x04,0xa2,0x78,0x74,
+ 0x04,0xa4,0x84,0x3c,0x24,0x1e,0x77,0x72,0x02,0xa2,0x02,0x3d,0x42,0x1e,0x76,0x74,
+ 0x04,0xa4,0x84,0x3d,0x24,0x1e,0x04,0x2a,0x3b,0xe0,0x74,0x74,0x04,0xa2,0x74,0x74,
+ 0x04,0xa4,0x84,0x3c,0x24,0x1e,0x73,0x72,0x02,0xa2,0x02,0x3d,0x42,0x1e,0x72,0x74,
+ 0x04,0xa4,0x84,0x3d,0x24,0x1e,0x17,0xb3,0x04,0x2a,0x29,0xe0,0x05,0xb4,0x28,0xf0,
+ 0x23,0x2a,0x26,0xe0,0x66,0x76,0x06,0xa4,0x66,0x76,0x06,0xa6,0x86,0x3c,0x46,0x1e,
+ 0x65,0x74,0x04,0xa4,0x04,0x3d,0x64,0x1e,0x64,0x76,0x06,0xa6,0x86,0x3d,0x46,0x1e,
+ 0x06,0x2a,0x03,0xe8,0x16,0x60,0x17,0xb6,0x12,0xf0,0x60,0x76,0x06,0xa4,0x60,0x76,
+ 0x06,0xa6,0x86,0x3c,0x46,0x1e,0x5f,0x74,0x04,0xa4,0x04,0x3d,0x64,0x1e,0x5e,0x76,
+ 0x06,0xa6,0x86,0x3d,0x46,0x1e,0x06,0x2a,0x03,0xe0,0x14,0x60,0x17,0xb4,0x05,0xb6,
+ 0xcf,0x00,0xf0,0x24,0x7c,0x00,0x5b,0x77,0x77,0xa7,0x07,0x2a,0x05,0xe8,0x57,0x77,
+ 0x07,0x87,0x76,0x67,0x67,0x1c,0x07,0xf0,0x58,0x77,0x07,0xa7,0x07,0x2a,0x57,0x77,
+ 0x02,0xe8,0x17,0xa7,0x01,0xf0,0x07,0xa7,0x56,0x73,0x03,0xa6,0x13,0xa4,0x84,0x3c,
+ 0x64,0x1e,0x06,0x60,0x65,0x12,0x53,0x7d,0xae,0x61,0x7f,0x12,0x7f,0x01,0x1d,0xf0,
+ 0x57,0x12,0x57,0x1c,0x37,0x1c,0x27,0xa1,0x37,0xa2,0x2c,0x12,0x5c,0x01,0x17,0x12,
+ 0x57,0x01,0xf1,0x11,0xe7,0x03,0xf9,0x11,0xc7,0x1c,0x77,0x1c,0xd7,0x1c,0x07,0xc7,
+ 0x77,0x01,0x7f,0x0d,0x08,0xe8,0x87,0x65,0x67,0x1c,0x77,0x1c,0x37,0x1c,0x87,0xb1,
+ 0x97,0xb2,0x06,0x20,0x46,0x01,0x05,0x20,0x45,0x01,0x45,0x0c,0xe1,0xef,0x42,0x77,
+ 0x07,0xb6,0x42,0x77,0x06,0x60,0x07,0xb6,0x6c,0x00,0xf0,0x20,0xcf,0x00,0xf0,0x25,
+ 0x60,0x9f,0x70,0x9e,0x36,0x77,0x07,0x87,0x3e,0x76,0x76,0x1c,0x06,0xa6,0x06,0x2a,
+ 0x4f,0xe8,0x3c,0x76,0x06,0xa4,0x3c,0x76,0x06,0xa6,0x86,0x3c,0x3c,0x75,0x75,0x1c,
+ 0x05,0xa5,0x3b,0x72,0x27,0x1c,0x07,0xa7,0x87,0x3c,0x46,0x1e,0x57,0x1e,0x67,0x0c,
+ 0x39,0x75,0x39,0x77,0x04,0xe0,0x16,0x60,0x05,0xb6,0x56,0x60,0x05,0xf0,0x07,0xa6,
+ 0x06,0x2a,0x86,0x00,0x01,0xe0,0x05,0xb6,0x07,0xb6,0x32,0x77,0x07,0xa7,0x07,0x2a,
+ 0x2f,0xe8,0x2c,0x77,0x07,0xa6,0x2c,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x7e,0x12,
+ 0x7e,0x1c,0x7e,0x1c,0x2e,0x3e,0x02,0x12,0x03,0x60,0x44,0x61,0x2c,0x7f,0x22,0x77,
+ 0x07,0xa7,0x22,0x76,0x06,0xa5,0x85,0x3c,0x75,0x1e,0x06,0x60,0x1d,0x73,0x7e,0x01,
+ 0x14,0x60,0x11,0xf0,0x67,0x12,0x37,0x3c,0x67,0x05,0x37,0x1c,0x82,0x62,0x27,0x1c,
+ 0x77,0xa2,0x87,0xa7,0x87,0x3c,0x27,0x1e,0xe7,0x0d,0x03,0xe8,0x07,0x12,0x67,0x1c,
+ 0x07,0xb4,0x06,0x20,0x46,0x01,0x56,0x0c,0xed,0xef,0x02,0x12,0x13,0x73,0x1c,0x7f,
+ 0x60,0x8f,0x70,0x8e,0xf0,0x21,0xcf,0x00,0xd8,0xa5,0x00,0x00,0x46,0xa4,0x00,0x00,
+ 0x47,0xa4,0x00,0x00,0x48,0xa4,0x00,0x00,0x49,0xa4,0x00,0x00,0x4a,0xa4,0x00,0x00,
+ 0x4b,0xa4,0x00,0x00,0x4c,0xa4,0x00,0x00,0x4d,0xa4,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0xb9,0x00,0x00,0x00,0x20,0xaa,0x00,0x00,0x4a,0xa0,0x00,0x00,0x86,0x9e,0x00,0x00,
+ 0x8a,0x9e,0x00,0x00,0x00,0xe0,0x02,0x00,0x40,0x9f,0x00,0x00,0x41,0x9f,0x00,0x00,
+ 0x83,0x00,0x00,0x00,0x3e,0xa4,0x00,0x00,0x3f,0xa4,0x00,0x00,0x84,0x00,0x00,0x00,
+ 0x85,0x00,0x00,0x00,0xd9,0xa5,0x00,0x00,0x00,0x9e,0x00,0x00,0x22,0x83,0x00,0x00,
+ 0x00,0x2d,0x00,0x00,0x70,0x25,0x7a,0x00,0xbd,0x77,0x07,0x8f,0xbd,0x77,0xf7,0x1c,
+ 0x07,0xa7,0x07,0x2a,0x77,0xe8,0xbb,0x77,0x07,0xa6,0xbb,0x77,0x07,0xa7,0xbb,0x71,
+ 0x7c,0x12,0x4c,0x3c,0x75,0x12,0x65,0x3c,0x5c,0x1c,0xb9,0x74,0xc4,0x1c,0x03,0x60,
+ 0x7d,0x12,0x7d,0x1c,0x37,0x3c,0x7d,0x1c,0x0c,0x28,0x6e,0x12,0x4e,0x3c,0x67,0x12,
+ 0x67,0x3c,0x7e,0x1c,0x01,0xa7,0x27,0x2a,0x58,0xe0,0x04,0xa7,0x07,0x2a,0x55,0xe8,
+ 0xb1,0x7b,0xd7,0x12,0x37,0x1c,0xb0,0x75,0x57,0x1c,0x37,0x3c,0xb7,0x1c,0x77,0xa5,
+ 0x87,0xa2,0x82,0x3c,0x52,0x1e,0xad,0x77,0xf7,0x1c,0x07,0xa7,0xad,0x75,0xf5,0x1c,
+ 0x05,0xa5,0x85,0x3c,0x75,0x1e,0x25,0x0c,0x12,0xe0,0x67,0x12,0x67,0x1c,0x6a,0x12,
+ 0x3a,0x3c,0xa7,0x1c,0x37,0x1c,0xa4,0x7a,0xa7,0x1c,0x37,0x3c,0xb7,0x1c,0x77,0xab,
+ 0x87,0xa7,0x87,0x3c,0xb7,0x1e,0x5b,0x12,0x2b,0x3e,0x7b,0x0c,0x14,0xe8,0x9d,0x7b,
+ 0x67,0x12,0x67,0x1c,0x6a,0x12,0x3a,0x3c,0xa7,0x1c,0x37,0x1c,0x9b,0x7a,0xa7,0x1c,
+ 0x37,0x3c,0x7b,0x1c,0x7b,0xaa,0x8b,0xa7,0x87,0x3c,0xa7,0x1e,0x75,0x0c,0x1d,0xe0,
+ 0x25,0x3e,0x25,0x0c,0x1a,0xe0,0x74,0xa7,0xc5,0x12,0x45,0x1c,0xe5,0x1c,0x75,0xa5,
+ 0x57,0x05,0xe7,0x01,0x67,0x01,0x72,0x12,0x72,0x01,0xa5,0x65,0x25,0x0d,0x04,0xe8,
+ 0x91,0x75,0x75,0x05,0x57,0x12,0x67,0x01,0x77,0x01,0x8f,0x75,0xf5,0x1c,0x05,0xa5,
+ 0x15,0x3e,0x75,0x0d,0x02,0xe8,0x47,0x60,0x01,0xb7,0x03,0x20,0x01,0x20,0x74,0x20,
+ 0xa3,0x2a,0xa0,0xe7,0x6a,0x00,0x70,0x21,0xcf,0x00,0xf0,0x25,0x78,0x00,0x01,0x65,
+ 0x10,0x05,0x2a,0x12,0x86,0x77,0x07,0xa7,0x07,0x2a,0x17,0xe8,0x06,0x60,0x84,0x77,
+ 0x07,0xb6,0x7a,0x77,0x84,0x72,0x13,0x60,0x44,0x60,0x07,0xa5,0x15,0x2a,0x02,0xe0,
+ 0x07,0xb6,0x05,0xf0,0x15,0x24,0x45,0x01,0x53,0x0c,0x01,0xe8,0x07,0xb4,0x07,0x20,
+ 0x27,0x0f,0xf3,0xe7,0x06,0x60,0x79,0x77,0x07,0xb6,0x79,0x77,0x07,0xa7,0x0a,0xb7,
+ 0x7a,0x77,0x07,0xa7,0x1a,0xb7,0x79,0x77,0x07,0xa7,0x2a,0xb7,0xa6,0x12,0x07,0x60,
+ 0x6d,0x74,0x75,0x12,0x45,0x1c,0x76,0x73,0x35,0x1c,0x05,0xa5,0x96,0xb5,0x07,0x20,
+ 0x56,0x20,0xa7,0x2a,0xf6,0xe7,0x61,0x77,0x07,0x8e,0x72,0x77,0xe7,0x1c,0x07,0xa7,
+ 0x74,0x32,0x47,0x1c,0x00,0x97,0x70,0x77,0xe7,0x1c,0x07,0xa7,0x47,0x1c,0x10,0x97,
+ 0x6f,0x77,0xa7,0xa6,0xb7,0xa5,0x85,0x3c,0x65,0x1e,0x40,0x95,0x56,0x12,0x6c,0x73,
+ 0x36,0x1c,0x66,0x01,0x60,0x96,0xc7,0xa6,0xd7,0xa4,0x84,0x3c,0x64,0x1e,0x50,0x94,
+ 0x46,0x12,0x36,0x1c,0x66,0x01,0x70,0x96,0xe7,0xa6,0xf7,0xa8,0x88,0x3c,0x68,0x1e,
+ 0x65,0x77,0x07,0xa7,0x65,0x76,0x06,0xa9,0x89,0x3c,0x79,0x1e,0x95,0x12,0x85,0x05,
+ 0xc0,0x95,0x62,0x77,0x07,0xa7,0x62,0x76,0x06,0xa6,0x86,0x3c,0x76,0x1e,0x20,0x96,
+ 0x61,0x77,0x07,0xa7,0x61,0x76,0x06,0xa6,0x86,0x3c,0x76,0x1e,0x30,0x96,0x20,0x87,
+ 0x76,0x05,0xd0,0x96,0x0d,0x60,0x5d,0x73,0xe3,0x1c,0x90,0x93,0x5d,0x74,0xe4,0x1c,
+ 0xa0,0x94,0x5c,0x75,0xe5,0x1c,0xe0,0x95,0x5c,0x76,0xe6,0x1c,0xb0,0x96,0x10,0x87,
+ 0x83,0x2c,0x37,0x16,0xf0,0x97,0x10,0x85,0x85,0x3e,0x04,0x64,0x04,0x1c,0x04,0x95,
+ 0x00,0x87,0x37,0x16,0x46,0x64,0x06,0x1c,0x06,0x97,0x00,0x84,0x84,0x3e,0x83,0x64,
+ 0x03,0x1c,0x03,0x94,0x87,0xf1,0xd6,0x12,0x36,0x3c,0xd6,0x1c,0x76,0x1c,0x4f,0x75,
+ 0x65,0x1c,0x05,0xab,0xb7,0x1c,0x3e,0x75,0x57,0x1c,0x07,0xa7,0x47,0x2a,0x78,0xe9,
+ 0x07,0x2a,0x76,0xe9,0x4b,0x77,0x76,0x1c,0x56,0xa7,0x66,0xa2,0x82,0x3c,0x72,0x1e,
+ 0x87,0x32,0x27,0x0c,0x22,0xe8,0x00,0x83,0x32,0x0c,0x08,0xe0,0x44,0x64,0x04,0x1c,
+ 0x04,0xa4,0x56,0xb4,0x85,0x64,0x05,0x1c,0x05,0xa5,0x66,0xb5,0xd7,0x12,0x37,0x3c,
+ 0xd7,0x1c,0x40,0x76,0x67,0x1c,0x57,0xa6,0x67,0xa7,0x87,0x3c,0x67,0x1e,0x00,0x86,
+ 0x67,0x05,0x90,0x83,0x03,0xac,0x82,0x12,0xc2,0x05,0xf1,0x11,0x72,0x03,0xf9,0x11,
+ 0x83,0x32,0x63,0x05,0x39,0x7f,0x2c,0x1c,0x8b,0xf0,0x60,0x84,0x24,0x0c,0x7d,0xe0,
+ 0x37,0x77,0xe7,0x1c,0x07,0xa7,0x45,0x12,0x73,0x32,0x35,0x1c,0x75,0x05,0x25,0x0d,
+ 0x08,0xe8,0x40,0x85,0x33,0x74,0x45,0x1c,0x75,0x05,0x65,0x01,0x56,0xb5,0x85,0x3e,
+ 0x66,0xb5,0xd5,0x12,0x35,0x3c,0xd5,0x1c,0x2b,0x76,0x65,0x1c,0x55,0xa6,0x65,0xa2,
+ 0x82,0x3c,0x62,0x1e,0x60,0x85,0x52,0x05,0x0e,0xa5,0x1e,0xa6,0x86,0x3c,0x56,0x1e,
+ 0x29,0x75,0xe5,0x1c,0x05,0xa5,0x56,0x05,0x4f,0xf0,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0x1a,0x01,0x00,0x00,0xe4,0xa3,0x00,0x00,0xe5,0xa3,0x00,0x00,0xfa,0xa3,0x00,0x00,
+ 0xa4,0xa2,0x00,0x00,0x8a,0x9e,0x00,0x00,0x83,0x00,0x00,0x00,0x1b,0x01,0x00,0x00,
+ 0x1c,0x01,0x00,0x00,0xb4,0x00,0x00,0x00,0x1d,0x01,0x00,0x00,0x62,0xa4,0x00,0x00,
+ 0xa5,0xa0,0x00,0x00,0x04,0xa4,0x00,0x00,0x4a,0xa0,0x00,0x00,0x2c,0xa4,0x00,0x00,
+ 0x70,0x05,0x00,0x00,0x9d,0x00,0x00,0x00,0x9f,0x00,0x00,0x00,0x20,0x9e,0x00,0x00,
+ 0x00,0xff,0xff,0xff,0x30,0x9e,0x00,0x00,0x31,0x9e,0x00,0x00,0x32,0x9e,0x00,0x00,
+ 0x33,0x9e,0x00,0x00,0x34,0x9e,0x00,0x00,0x35,0x9e,0x00,0x00,0x99,0x00,0x00,0x00,
+ 0x9b,0x00,0x00,0x00,0xc1,0x00,0x00,0x00,0x9c,0x00,0x00,0x00,0x1c,0x02,0x00,0x00,
+ 0x18,0x02,0x00,0x00,0xa2,0xa0,0x00,0x00,0xd0,0x83,0x00,0x00,0x9e,0x00,0x00,0x00,
+ 0x80,0xff,0xff,0xff,0x9a,0x00,0x00,0x00,0x96,0x05,0xf1,0x11,0x62,0x03,0xf9,0x11,
+ 0x73,0x32,0x73,0x05,0x7e,0x7f,0x92,0x1c,0x0a,0xf0,0x7d,0x76,0x62,0x1c,0xc0,0x87,
+ 0xf1,0x11,0x72,0x03,0xf9,0x11,0x60,0x83,0x63,0x1c,0x78,0x7f,0x82,0x1c,0x2c,0x12,
+ 0x6c,0x01,0xd6,0x12,0x36,0x3c,0xd6,0x1c,0x77,0x77,0x76,0x1c,0x76,0xa7,0x86,0xa2,
+ 0x82,0x3c,0x72,0x1e,0x87,0x2c,0x27,0x0c,0x24,0xe8,0x10,0x83,0x32,0x0c,0x08,0xe0,
+ 0xc4,0x63,0x04,0x1c,0x04,0xa4,0x76,0xb4,0x05,0x64,0x05,0x1c,0x05,0xa5,0x86,0xb5,
+ 0xd7,0x12,0x37,0x3c,0xd7,0x1c,0x6b,0x76,0x67,0x1c,0x77,0xa6,0x87,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x10,0x86,0x67,0x05,0xa0,0x83,0x03,0xa3,0x80,0x93,0x20,0x82,0x32,0x05,
+ 0xf1,0x11,0x72,0x03,0xf9,0x11,0x83,0x32,0x63,0x05,0x60,0x7f,0x80,0x84,0x42,0x1c,
+ 0x3b,0xf0,0x70,0x85,0x25,0x0c,0x2d,0xe0,0x60,0x77,0xe7,0x1c,0x07,0xa7,0x73,0x32,
+ 0x35,0x1c,0x75,0x05,0x25,0x0d,0x08,0xe8,0x50,0x85,0x5c,0x74,0x45,0x1c,0x75,0x05,
+ 0x65,0x01,0x76,0xb5,0x85,0x3e,0x86,0xb5,0xd5,0x12,0x35,0x3c,0xd5,0x1c,0x55,0x76,
+ 0x65,0x1c,0x75,0xa6,0x85,0xa2,0x82,0x3c,0x62,0x1e,0x70,0x85,0x52,0x05,0x2e,0xa5,
+ 0x3e,0xa6,0x86,0x3c,0x56,0x1e,0xb0,0x83,0x03,0xa5,0x56,0x05,0x30,0x84,0x46,0x05,
+ 0xf1,0x11,0x62,0x03,0xf9,0x11,0x73,0x32,0x73,0x05,0x48,0x7f,0x30,0x85,0x52,0x1c,
+ 0x0b,0xf0,0x47,0x76,0x62,0x1c,0xd0,0x87,0xf1,0x11,0x72,0x03,0xf9,0x11,0x70,0x83,
+ 0x63,0x1c,0x42,0x7f,0x20,0x83,0x32,0x1c,0x62,0x01,0x90,0x84,0x04,0xa7,0x7c,0x0c,
+ 0x10,0xe8,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x41,0x76,0xe6,0x1c,0x06,0xa6,
+ 0x75,0x12,0x65,0x05,0x5c,0x0d,0x06,0xe0,0x07,0x24,0x67,0x05,0x7c,0x12,0x6c,0x01,
+ 0x01,0xf0,0x7c,0x12,0xa0,0x85,0x05,0xa7,0x72,0x0c,0x0f,0xe8,0x2e,0xa6,0x3e,0xa7,
+ 0x87,0x3c,0x67,0x1e,0xb0,0x83,0x03,0xa6,0x75,0x12,0x65,0x05,0x52,0x0d,0x06,0xe0,
+ 0x07,0x24,0x67,0x05,0x72,0x12,0x62,0x01,0x01,0xf0,0x72,0x12,0xe0,0x84,0x04,0xa7,
+ 0x07,0x2a,0xb7,0x12,0xb7,0x1c,0x86,0x2c,0x26,0x16,0x85,0x2c,0xc5,0x16,0xb7,0x1c,
+ 0x77,0x1c,0xa7,0x1c,0x07,0xe8,0x47,0xb6,0x82,0x3e,0x57,0xb2,0x67,0xb5,0x8c,0x3e,
+ 0x77,0xbc,0x06,0xf0,0x47,0xb5,0x8c,0x3e,0x57,0xbc,0x67,0xb6,0x82,0x3e,0x77,0xb2,
+ 0xb7,0x12,0xb7,0x1c,0xb7,0x1c,0x77,0x1c,0xa7,0x1c,0xd6,0x12,0x36,0x3c,0xd6,0x1c,
+ 0x1d,0x75,0x56,0x1c,0x96,0xa5,0xa6,0xa6,0x86,0x3c,0x56,0x1e,0x46,0x3e,0x87,0xb6,
+ 0x0d,0x20,0x4d,0x01,0x1c,0x77,0x1c,0x76,0x06,0xa6,0x6d,0x0c,0x74,0xee,0x05,0x60,
+ 0x1b,0x72,0x53,0x12,0x12,0xf0,0x56,0x12,0x36,0x3c,0x56,0x1c,0x76,0x1c,0x18,0x74,
+ 0x46,0x1c,0x06,0xa4,0x46,0x12,0x46,0x1c,0x46,0x1c,0x66,0x1c,0xa6,0x1c,0x96,0xa4,
+ 0x44,0x2a,0x01,0xe0,0x86,0xb3,0x05,0x20,0x45,0x01,0x02,0xa6,0x65,0x0c,0xeb,0xef,
+ 0x0e,0x73,0x03,0xa7,0x74,0x12,0x34,0x3c,0x74,0x1c,0x0c,0x72,0x04,0x20,0x0d,0x7f,
+ 0x01,0x65,0x10,0x1c,0x68,0x00,0xf0,0x21,0xcf,0x00,0x00,0x00,0xd0,0x83,0x00,0x00,
+ 0x00,0xff,0xff,0xff,0xa2,0xa0,0x00,0x00,0xa0,0x00,0x00,0x00,0x80,0xff,0xff,0xff,
+ 0x9a,0x00,0x00,0x00,0x8a,0x9e,0x00,0x00,0xa5,0xa0,0x00,0x00,0x4a,0xa0,0x00,0x00,
+ 0xc1,0x01,0x00,0x00,0x8c,0x82,0x00,0x00,0x70,0x25,0x7a,0x00,0x86,0x7e,0x86,0x77,
+ 0x07,0xa7,0x07,0x2a,0x56,0xe8,0x0e,0xa7,0x1e,0xa5,0x85,0x3c,0x75,0x1e,0x1b,0x60,
+ 0x5b,0x0c,0x4f,0xe0,0x82,0x77,0x07,0xa7,0x82,0x76,0x06,0xaa,0x8a,0x3c,0x7a,0x1e,
+ 0x81,0x77,0x07,0x8d,0x67,0x67,0xd7,0x1c,0x07,0xa7,0x77,0x1c,0x7a,0x0d,0x48,0xe8,
+ 0x57,0x67,0xd7,0x1c,0x07,0xa7,0x7a,0x0c,0x0e,0xe8,0xba,0x0b,0x7b,0x7c,0x2c,0xba,
+ 0x7b,0x7f,0x0e,0xa6,0x1e,0xa7,0x87,0x3c,0x67,0x1e,0x7b,0x0c,0x39,0xe8,0x3c,0xbb,
+ 0x0c,0xba,0x1c,0xba,0x2e,0xf0,0x76,0x77,0x07,0xac,0x76,0x77,0x07,0xad,0x03,0x60,
+ 0xf2,0x67,0x36,0x12,0x5c,0x01,0x5d,0x01,0x16,0xf0,0x67,0x12,0x67,0x1c,0xe7,0x1c,
+ 0x27,0xa4,0x54,0x01,0xc4,0x05,0xe4,0x01,0x37,0xa7,0x57,0x01,0xd7,0x05,0xe7,0x01,
+ 0x47,0x1c,0x47,0x01,0x74,0x12,0x54,0x01,0x24,0x0d,0x27,0x0a,0x01,0xe8,0x63,0x12,
+ 0x06,0x20,0x46,0x01,0x72,0x12,0x56,0x0c,0x5f,0x77,0xe7,0xef,0x16,0x60,0x07,0xb6,
+ 0x06,0x60,0x17,0xb6,0x36,0x12,0x36,0x1c,0x76,0x1c,0x26,0xa5,0x27,0xb5,0x36,0xa6,
+ 0x37,0xb6,0x59,0x77,0x07,0xa6,0x60,0x77,0x07,0xb6,0x6a,0x00,0x70,0x21,0xcf,0x00,
+ 0x5a,0x77,0x66,0x67,0xd6,0x1c,0x06,0xa6,0x07,0xb6,0x56,0x67,0xd6,0x1c,0x06,0xa6,
+ 0x17,0xb6,0x46,0x67,0x6d,0x1c,0x0d,0xa6,0x27,0xb6,0x06,0x60,0x4f,0x77,0x07,0xb6,
+ 0xe8,0xf7,0xf0,0x25,0x78,0x00,0xf0,0x24,0x42,0x01,0x03,0x01,0x26,0x12,0x06,0x24,
+ 0x46,0x01,0x4c,0x77,0x07,0x87,0x0f,0x60,0x1a,0x12,0x0a,0x24,0x4a,0x01,0x4f,0x7b,
+ 0xac,0x61,0x6d,0x67,0x7d,0x1c,0x5e,0x67,0x7e,0x1c,0x43,0x67,0x73,0x1c,0x4c,0x75,
+ 0x75,0x1c,0x00,0x95,0x4c,0x75,0x75,0x1c,0x10,0x95,0x29,0xf0,0xa5,0x12,0x69,0x12,
+ 0xf1,0x11,0xc9,0x03,0xf9,0x11,0x20,0x96,0x1d,0xf0,0x97,0x12,0x57,0x1c,0x77,0x1c,
+ 0xb7,0x1c,0x07,0xc4,0x74,0x01,0x0d,0xa7,0x47,0x0d,0x12,0xe0,0x0e,0xa7,0x47,0x0d,
+ 0x0f,0xe0,0x03,0xa7,0x47,0x0d,0x0c,0xe0,0x00,0x86,0x06,0xa8,0x10,0x86,0x06,0xa7,
+ 0x87,0x3c,0x87,0x1e,0x07,0x3d,0x07,0x3b,0x74,0x0d,0x02,0xe8,0x0f,0x20,0x4f,0x01,
+ 0x05,0x20,0x45,0x01,0x51,0x0d,0xe1,0xef,0x20,0x86,0x06,0x20,0x46,0x01,0x62,0x0d,
+ 0xd5,0xef,0x27,0x60,0xf7,0x0c,0x22,0x00,0xf0,0x20,0x68,0x00,0xf0,0x21,0xcf,0x00,
+ 0xf0,0x24,0x7c,0x00,0x24,0x7c,0x30,0x77,0x07,0xa6,0x30,0x77,0x07,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x1e,0x60,0x07,0x2a,0x16,0xe8,0x0c,0xa7,0x1c,0xae,0x8e,0x3c,0x7e,0x1e,
+ 0x0d,0x60,0x0e,0x2a,0x0b,0xe0,0x0e,0xf0,0xd7,0x12,0xd7,0x1c,0xc7,0x1c,0x27,0xa2,
+ 0x37,0xa3,0x27,0x7f,0x02,0x2a,0x04,0xe0,0x0d,0x20,0x4d,0x01,0xed,0x0c,0xf4,0xef,
+ 0xed,0x0c,0x3e,0x00,0x24,0x77,0x07,0xa7,0x07,0x2a,0x23,0x77,0x0f,0xe8,0x06,0x60,
+ 0x0e,0x2a,0x02,0xe0,0x07,0xa6,0x06,0x20,0x07,0xb6,0x1f,0x77,0x07,0xa5,0xa6,0x60,
+ 0x56,0x0c,0x15,0xe0,0x06,0x60,0x1b,0x75,0x05,0xb6,0x10,0xf0,0x0e,0x2a,0x04,0xe8,
+ 0x07,0xa6,0x06,0x20,0x07,0xb6,0x01,0xf0,0x07,0xbe,0x17,0x77,0x07,0xa5,0xa6,0x60,
+ 0x56,0x0c,0x05,0xe0,0x15,0x60,0x13,0x76,0x06,0xb5,0x06,0x60,0x07,0xb6,0x6c,0x00,
+ 0xf0,0x20,0xcf,0x00,0x8a,0x9e,0x00,0x00,0x63,0xa4,0x00,0x00,0x3e,0xa4,0x00,0x00,
+ 0x3f,0xa4,0x00,0x00,0x1c,0x9e,0x00,0x00,0x86,0x9e,0x00,0x00,0x00,0x28,0x00,0x00,
+ 0x4f,0xa0,0x00,0x00,0x4d,0xa0,0x00,0x00,0x3d,0xaa,0x00,0x00,0x00,0xe0,0x02,0x00,
+ 0xb5,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x6a,0x9f,0x00,0x00,0x6b,0x9f,0x00,0x00,
+ 0xe2,0x4d,0x00,0x00,0x64,0xa4,0x00,0x00,0xda,0xa5,0x00,0x00,0xf0,0x25,0x78,0x00,
+ 0xa4,0x71,0x10,0x05,0xa4,0x77,0x07,0x8e,0x4e,0xa2,0x5e,0xa3,0xa3,0x77,0x00,0x97,
+ 0x34,0x60,0xa2,0x75,0xa3,0x76,0x07,0x60,0xa3,0x7f,0xa3,0x77,0xe7,0x1c,0x07,0xa7,
+ 0x07,0x2a,0x18,0xe8,0xa2,0x77,0x17,0xa6,0x07,0xa7,0x76,0x1c,0x07,0x60,0xa0,0x72,
+ 0xa1,0x73,0xa1,0x74,0x0d,0xf0,0x75,0x12,0x75,0x1c,0x2f,0x12,0x5f,0x1c,0x3d,0x12,
+ 0x5d,0x1c,0x45,0x1c,0x0d,0xcd,0x05,0xc5,0xd5,0x14,0x0f,0xd5,0x07,0x20,0x47,0x01,
+ 0x67,0x0d,0xf1,0xe7,0x9a,0x77,0x57,0xa7,0x17,0x2e,0x07,0x2a,0x07,0xe8,0x98,0x72,
+ 0x4e,0xa3,0x5e,0xa4,0x98,0x7f,0x98,0x72,0x83,0x61,0x98,0x7f,0x94,0x77,0x57,0xa7,
+ 0x47,0x2e,0x07,0x2a,0x09,0xe8,0x87,0x77,0x07,0x87,0x95,0x72,0x47,0xa3,0x57,0xa4,
+ 0x91,0x7f,0x94,0x72,0xa3,0x61,0x91,0x7f,0x8d,0x77,0x57,0xa7,0x27,0x2e,0x07,0x2a,
+ 0x0b,0xe8,0x91,0x72,0x8c,0x7f,0x7f,0x77,0x07,0x87,0x90,0x72,0x47,0xa3,0x57,0xa4,
+ 0x89,0x7f,0x8f,0x72,0xa3,0x61,0x89,0x7f,0x7b,0x77,0x07,0x8e,0xa7,0x67,0xe7,0x1c,
+ 0x07,0xa7,0x07,0x2a,0x06,0xe8,0x7d,0x77,0x07,0xa2,0x17,0xa3,0x89,0x74,0xa5,0x61,
+ 0x89,0x7f,0xd1,0x67,0x1e,0x1c,0x0e,0xa7,0x07,0x2a,0x49,0xe9,0x78,0x77,0x07,0xa3,
+ 0x86,0x72,0x02,0x1c,0x02,0x93,0x17,0xa5,0x85,0x74,0x04,0x1c,0x04,0x95,0x0e,0x60,
+ 0xed,0x12,0x6c,0x78,0x37,0xf1,0x08,0x87,0x26,0x65,0x67,0x1c,0x07,0xae,0xde,0x1c,
+ 0x4e,0x01,0x7d,0x77,0x07,0x1c,0x07,0x87,0x7e,0x0c,0x7e,0x02,0xd2,0x12,0x12,0x28,
+ 0xe2,0x1c,0x42,0x01,0x22,0x1c,0x33,0x60,0x7a,0x7f,0x7a,0x73,0x03,0x1c,0x03,0x92,
+ 0x0c,0x60,0xa9,0x61,0x14,0xf1,0x02,0x12,0x72,0x20,0x03,0x60,0x04,0x61,0x76,0x7f,
+ 0xd7,0x12,0x04,0x60,0x05,0x61,0x31,0xf0,0x76,0x12,0xf1,0x11,0x96,0x03,0xf9,0x11,
+ 0xc6,0x1c,0x66,0x1c,0x6b,0x71,0x16,0x1c,0x06,0xc6,0x76,0x01,0x70,0x72,0x62,0x0d,
+ 0x05,0xe8,0xc6,0x61,0x06,0x1c,0x76,0x1c,0x06,0xb5,0x12,0xf0,0x6d,0x73,0x36,0x0d,
+ 0x05,0xe8,0xc6,0x61,0x06,0x1c,0x76,0x1c,0x06,0xb4,0x0a,0xf0,0xc3,0x61,0x03,0x1c,
+ 0x73,0x1c,0xf6,0x37,0x02,0xe8,0xff,0x63,0xf6,0x1c,0x66,0x3a,0x76,0x20,0x03,0xb6,
+ 0xc6,0x61,0x06,0x1c,0x76,0x1c,0x06,0xa6,0x56,0x01,0x81,0x60,0x01,0x1c,0x16,0x1c,
+ 0x06,0xa3,0x03,0x20,0x06,0xb3,0x07,0x20,0x47,0x01,0xe7,0x0c,0xcd,0xef,0x07,0x60,
+ 0x75,0x12,0x76,0x12,0x07,0x01,0x84,0x60,0x04,0x1c,0x74,0x1c,0x04,0xa4,0x46,0x0c,
+ 0x15,0x0a,0x46,0x0a,0x07,0x20,0x07,0x2b,0xf5,0xe7,0x07,0x60,0x56,0x72,0x02,0x1c,
+ 0x02,0x97,0x76,0x12,0x07,0x01,0x84,0x60,0x04,0x1c,0x74,0x1c,0x04,0xa4,0x46,0x0c,
+ 0x06,0xe0,0x51,0x0f,0x04,0xe8,0x4f,0x73,0x03,0x1c,0x03,0x91,0x46,0x12,0x07,0x20,
+ 0x07,0x2b,0xf0,0xe7,0xd6,0x12,0x02,0x60,0x2b,0x12,0x16,0xf0,0xc7,0x61,0x07,0x1c,
+ 0x67,0x1c,0x07,0xa7,0x57,0x01,0x57,0x0f,0x0d,0xe0,0x67,0x12,0xf1,0x11,0x97,0x03,
+ 0xf9,0x11,0xc7,0x1c,0x77,0x1c,0x3a,0x74,0x47,0x1c,0x07,0xc7,0x77,0x01,0x72,0x1c,
+ 0x0b,0x20,0x4b,0x01,0x06,0x20,0x46,0x01,0xe6,0x0c,0xe8,0xef,0x0b,0x2a,0x04,0xe8,
+ 0xb3,0x12,0x37,0x7f,0x2a,0x12,0x01,0xf0,0xba,0x12,0x36,0x75,0x05,0x1c,0x05,0x85,
+ 0x5b,0x0d,0x2b,0xe8,0x08,0x87,0x66,0x67,0x67,0x1c,0x07,0xa7,0xa7,0x0d,0x1b,0xe0,
+ 0x24,0xf0,0xc7,0x61,0x07,0x1c,0x67,0x1c,0x07,0xa7,0x57,0x01,0x32,0x71,0x01,0x1c,
+ 0x01,0x81,0x17,0x0f,0x0d,0xe0,0x67,0x12,0xf1,0x11,0x97,0x03,0xf9,0x11,0xc7,0x1c,
+ 0x77,0x1c,0x23,0x74,0x47,0x1c,0x07,0xc7,0x77,0x01,0x72,0x1c,0x03,0x20,0x43,0x01,
+ 0x06,0x20,0x46,0x01,0x03,0xf0,0xd6,0x12,0x02,0x60,0x23,0x12,0xe6,0x0c,0xe1,0xef,
+ 0x03,0x2a,0x03,0xe8,0x1f,0x7f,0x2a,0x0d,0x2a,0x0a,0x08,0x87,0xe5,0x67,0x57,0x1c,
+ 0x07,0xa6,0xf1,0x11,0xa6,0x03,0xf9,0x11,0x46,0x3a,0xd5,0x12,0x02,0x60,0x53,0xf0,
+ 0xd0,0x00,0x00,0x00,0x1c,0x9e,0x00,0x00,0x36,0x00,0x05,0x1a,0x36,0x00,0x04,0x1a,
+ 0x00,0x00,0x02,0x18,0xb4,0x26,0x00,0x00,0xc8,0x00,0x00,0x00,0x20,0x9e,0x00,0x00,
+ 0xdc,0xa5,0x00,0x00,0x5e,0xa5,0x00,0x00,0xec,0xa4,0x00,0x00,0x44,0xaa,0x00,0x00,
+ 0x10,0x07,0x00,0x00,0x7a,0x25,0x00,0x00,0x00,0x80,0x02,0x00,0xc0,0x2b,0x00,0x00,
+ 0x24,0x07,0x00,0x00,0x36,0xc0,0x02,0x00,0x38,0x07,0x00,0x00,0x44,0x07,0x00,0x00,
+ 0x36,0xe0,0x02,0x00,0x48,0x2a,0x00,0x00,0xb0,0x00,0x00,0x00,0xb4,0x00,0x00,0x00,
+ 0x3e,0x84,0x00,0x00,0xbc,0x00,0x00,0x00,0x22,0x83,0x00,0x00,0xc0,0x01,0x00,0x00,
+ 0x40,0xfe,0xff,0xff,0xb8,0x00,0x00,0x00,0x57,0x12,0xf1,0x11,0x97,0x03,0xf9,0x11,
+ 0xc7,0x1c,0x77,0x1c,0xad,0x71,0x17,0x1c,0x07,0xc4,0x43,0x12,0x73,0x01,0xf3,0x37,
+ 0x02,0xe0,0x36,0x0d,0x01,0xf0,0x63,0x0d,0x03,0xe8,0x64,0x05,0x07,0xd4,0x01,0xf0,
+ 0x07,0xd2,0x05,0x20,0x45,0x01,0xe5,0x0c,0xe7,0xef,0x0c,0x20,0x4c,0x01,0xa3,0x72,
+ 0x02,0x1c,0x02,0x82,0x2c,0x0f,0xe7,0xe6,0x08,0x87,0x23,0x65,0x37,0x1c,0x07,0xa7,
+ 0x7d,0x1c,0x4d,0x01,0x9f,0x74,0x04,0x1c,0x04,0x84,0x4e,0x0c,0xc4,0xee,0x9d,0x77,
+ 0x57,0xa7,0x27,0x2e,0x07,0x2a,0x05,0xe8,0x9c,0x72,0x9c,0x7f,0x97,0x72,0xa3,0x61,
+ 0x9c,0x7f,0x9c,0x77,0x07,0x8e,0x4e,0xa2,0x5e,0xa3,0x57,0x67,0xe7,0x1c,0x07,0xa7,
+ 0x0d,0x60,0x00,0x9d,0xc4,0x60,0x98,0x75,0xd6,0x12,0x98,0x7f,0x99,0x7c,0x0c,0x87,
+ 0x99,0x76,0x06,0xb7,0x17,0x01,0x98,0x76,0x06,0xb1,0x27,0x01,0x98,0x76,0x06,0xb1,
+ 0x87,0x3f,0x97,0x76,0x06,0xb7,0x4e,0xa2,0x5e,0xa3,0x96,0x7b,0xeb,0x1c,0x0b,0xa6,
+ 0x96,0x7a,0xea,0x1c,0x0a,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x3d,0x00,0x9d,0xd4,0x60,
+ 0x8a,0x75,0xd6,0x12,0x07,0x3b,0x89,0x7f,0x0c,0x87,0x90,0x76,0x06,0xb7,0x17,0x01,
+ 0x90,0x76,0x06,0xb1,0x27,0x01,0x8f,0x76,0x06,0xb1,0x87,0x3f,0x8f,0x76,0x06,0xb7,
+ 0x4e,0xa2,0x5e,0xa3,0x47,0x67,0xe7,0x1c,0x07,0xa7,0x00,0x9d,0xa4,0x60,0x7e,0x75,
+ 0xd6,0x12,0x7e,0x7f,0x0c,0x87,0x89,0x75,0x05,0xb7,0x17,0x01,0x89,0x76,0x06,0xb1,
+ 0x27,0x01,0x88,0x78,0x08,0xb1,0x87,0x3f,0x88,0x71,0x01,0xb7,0x4e,0xa2,0x5e,0xa3,
+ 0x0b,0xa6,0x0a,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x3d,0x00,0x9d,0xb4,0x60,0x72,0x75,
+ 0xd6,0x12,0x07,0x3b,0x72,0x7f,0x0c,0x87,0xe7,0x01,0x80,0x7b,0x0b,0xb7,0x17,0x01,
+ 0x80,0x7a,0x0a,0xb1,0x27,0x01,0x7f,0x79,0x09,0xb1,0x87,0x3f,0x7f,0x78,0x08,0xb7,
+ 0x4e,0xa2,0x5e,0xa3,0x37,0x67,0xe7,0x1c,0x07,0xa7,0x00,0x9d,0xc4,0x60,0x66,0x75,
+ 0xd6,0x12,0x66,0x7f,0x0c,0x87,0x79,0x76,0x06,0xb7,0x17,0x01,0x79,0x76,0x06,0xb1,
+ 0x27,0x01,0x78,0x76,0x06,0xb1,0x87,0x3f,0x78,0x76,0x06,0xb7,0x6c,0x72,0x02,0xa6,
+ 0x6c,0x73,0x03,0xa7,0x87,0x3c,0x67,0x1e,0x6b,0x74,0x04,0xa6,0x06,0x3d,0x76,0x1e,
+ 0x6a,0x75,0x05,0xa7,0x87,0x3d,0x67,0x1e,0x71,0x76,0x06,0x86,0x76,0x05,0x70,0x73,
+ 0x36,0x0d,0x20,0xe0,0x70,0x74,0x64,0x0d,0x1d,0xe0,0x0b,0xa5,0x0a,0xa6,0x86,0x3c,
+ 0x56,0x1e,0x09,0xa5,0x05,0x3d,0x65,0x1e,0x08,0xa6,0x86,0x3d,0x56,0x1e,0x6a,0x75,
+ 0x05,0x85,0x65,0x05,0x35,0x0d,0x0e,0xe0,0x54,0x0d,0x0c,0xe0,0x68,0x76,0x06,0xa7,
+ 0x45,0x61,0x75,0x0c,0x04,0xe0,0x16,0x60,0x66,0x77,0x07,0xb6,0x1a,0xf0,0x07,0x20,
+ 0x06,0xb7,0x17,0xf0,0x06,0x60,0x61,0x75,0x05,0xb6,0x61,0x75,0x05,0xb6,0x5b,0x76,
+ 0x06,0x97,0x52,0x77,0x07,0xa6,0x52,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x51,0x76,
+ 0x06,0xa6,0x06,0x3d,0x76,0x1e,0x50,0x77,0x07,0xa7,0x87,0x3d,0x67,0x1e,0x56,0x76,
+ 0x06,0x97,0x27,0x67,0xe7,0x1c,0x07,0xac,0x0c,0x2a,0x09,0xe0,0x56,0x77,0x07,0xa6,
+ 0x06,0x2a,0xd2,0xe8,0x15,0x60,0x54,0x76,0x36,0xb5,0x07,0xbc,0xcd,0xf0,0x1c,0x2a,
+ 0x09,0xe0,0x50,0x77,0x07,0xa6,0x26,0x2a,0xc7,0xe8,0x4f,0x76,0x36,0xbc,0x26,0x60,
+ 0x07,0xb6,0xc2,0xf0,0x2c,0x2a,0xc0,0xe0,0x4b,0x7b,0x0b,0xad,0x0d,0x2a,0x15,0xe0,
+ 0x4b,0x76,0x06,0xa7,0x27,0x2a,0x4a,0x77,0x0e,0xe0,0x36,0xa6,0x26,0x2a,0x0b,0xe8,
+ 0x07,0xc6,0x06,0x20,0x66,0x01,0x07,0xd6,0x55,0x60,0x65,0x0c,0xad,0xe0,0x07,0xdd,
+ 0x17,0x60,0x0b,0xb7,0xa9,0xf0,0x07,0xdd,0xa7,0xf0,0x1d,0x2a,0x94,0xe0,0x40,0x76,
+ 0x06,0xc7,0x07,0x20,0x67,0x01,0x06,0xd7,0x46,0x66,0x76,0x0c,0x85,0xe8,0x3d,0x77,
+ 0x06,0x60,0x97,0xb6,0x3d,0x76,0x07,0xb6,0x16,0x01,0x17,0xb1,0x26,0x01,0x27,0xb1,
+ 0x86,0x3f,0x37,0xb6,0x36,0x67,0x6e,0x1c,0x0e,0xa6,0x06,0x24,0x66,0x01,0xa7,0xb6,
+ 0x86,0x3e,0xb7,0xb6,0x36,0x7f,0x36,0x77,0x07,0xa6,0x17,0xa7,0x87,0x3c,0x67,0x1e,
+ 0x07,0x2a,0x34,0x77,0x6d,0xe0,0x07,0xc6,0x06,0x20,0x66,0x01,0x07,0xd6,0x57,0x60,
+ 0x67,0x0c,0x7a,0xe0,0x61,0xf0,0x00,0x00,0x36,0xe0,0x02,0x00,0xb4,0x00,0x00,0x00,
+ 0xb0,0x00,0x00,0x00,0x44,0xaa,0x00,0x00,0x58,0x07,0x00,0x00,0x7a,0x25,0x00,0x00,
+ 0xc0,0x2b,0x00,0x00,0x1c,0x9e,0x00,0x00,0x36,0x00,0x05,0x1a,0xb4,0x26,0x00,0x00,
+ 0x34,0x0a,0x04,0x00,0x46,0xa4,0x00,0x00,0x47,0xa4,0x00,0x00,0x48,0xa4,0x00,0x00,
+ 0x49,0xa4,0x00,0x00,0xb5,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x4a,0xa4,0x00,0x00,
+ 0x4b,0xa4,0x00,0x00,0x4c,0xa4,0x00,0x00,0x4d,0xa4,0x00,0x00,0x52,0xa4,0x00,0x00,
+ 0x53,0xa4,0x00,0x00,0x54,0xa4,0x00,0x00,0x55,0xa4,0x00,0x00,0x56,0xa4,0x00,0x00,
+ 0x57,0xa4,0x00,0x00,0x58,0xa4,0x00,0x00,0x59,0xa4,0x00,0x00,0x4e,0xa4,0x00,0x00,
+ 0x4f,0xa4,0x00,0x00,0x50,0xa4,0x00,0x00,0x51,0xa4,0x00,0x00,0x4c,0xa6,0x00,0x00,
+ 0x39,0xff,0xff,0xff,0xc7,0x00,0x00,0x00,0x50,0xa6,0x00,0x00,0x54,0xa6,0x00,0x00,
+ 0x72,0xa4,0x00,0x00,0x63,0xa4,0x00,0x00,0x86,0x9e,0x00,0x00,0x66,0x9e,0x00,0x00,
+ 0x56,0xa6,0x00,0x00,0x7a,0x9e,0x00,0x00,0x00,0xe0,0x02,0x00,0x88,0x27,0x00,0x00,
+ 0x00,0x08,0x03,0x00,0x58,0xa6,0x00,0x00,0xad,0x77,0x37,0xbd,0x0b,0xbc,0x14,0xf0,
+ 0x06,0x60,0x07,0xd6,0x11,0xf0,0x2d,0x2a,0x0f,0xe0,0xa9,0x77,0x07,0xa6,0x37,0xa7,
+ 0x27,0x2a,0x02,0xe8,0x26,0x2a,0x08,0xe8,0x07,0x60,0xa6,0x76,0x06,0xd7,0x15,0x60,
+ 0xa3,0x76,0x36,0xb5,0xa5,0x76,0x06,0xb7,0xa1,0x77,0x37,0xa6,0x06,0x2a,0x1a,0xe8,
+ 0x06,0x60,0x37,0xb6,0xa1,0x76,0x06,0xa6,0x26,0x2a,0xa0,0x76,0x06,0x86,0x06,0xe0,
+ 0x38,0x67,0x86,0x1c,0x06,0xa6,0x07,0xb6,0x17,0xb6,0x0b,0xf0,0x65,0x67,0x65,0x1c,
+ 0x05,0xa5,0x07,0xb5,0x55,0x67,0x65,0x1c,0x05,0xa5,0x17,0xb5,0x4d,0x67,0xd6,0x1c,
+ 0x06,0xa6,0x27,0xb6,0x97,0x77,0x07,0xa5,0x97,0x76,0x06,0xb5,0x97,0x71,0x15,0x16,
+ 0x07,0xb5,0x92,0x75,0x05,0x85,0x29,0x64,0x59,0x1c,0x09,0xa4,0x04,0x2a,0x8e,0xe8,
+ 0x06,0xa8,0x06,0x60,0x92,0x7a,0x8b,0x64,0x5b,0x1c,0x9c,0x64,0x5c,0x1c,0x90,0x73,
+ 0x53,0x1c,0x90,0x72,0x02,0x1c,0x02,0x93,0x90,0x74,0x7f,0x12,0x41,0xf0,0xa7,0x12,
+ 0x67,0x1c,0x07,0xa2,0x27,0x12,0x87,0x16,0x07,0x2a,0x02,0xe8,0x0c,0xad,0x01,0xf0,
+ 0x0b,0xad,0x8a,0x73,0x63,0x1c,0x23,0xa7,0x63,0xa3,0xae,0x61,0xf1,0x11,0xe7,0x03,
+ 0xf9,0x11,0x37,0x1c,0x77,0x1c,0x86,0x73,0x73,0x1c,0x03,0xce,0xe3,0x12,0x73,0x01,
+ 0x3d,0x0d,0x04,0xe8,0x0f,0xa7,0x72,0x1e,0x0f,0xb2,0x20,0xf0,0x82,0x72,0x62,0x1c,
+ 0x02,0xad,0x0d,0x20,0x4d,0x01,0x02,0xbd,0x51,0x60,0xd1,0x0c,0x17,0xe0,0x0d,0x60,
+ 0x02,0xbd,0x78,0x7d,0x0d,0x1c,0x0d,0x8d,0x0d,0xa2,0x32,0x0d,0x04,0xe8,0x47,0x1c,
+ 0x07,0xc3,0x32,0x14,0x07,0xf0,0x2d,0x12,0x0d,0x28,0x47,0x1c,0xd3,0x0d,0x07,0xc3,
+ 0x03,0xe8,0x32,0x1c,0x07,0xd2,0x02,0xf0,0x3e,0x14,0x07,0xde,0x06,0x20,0x46,0x01,
+ 0x09,0xa7,0x76,0x0c,0xbc,0xef,0x3f,0x64,0xf5,0x1c,0x05,0xa7,0x37,0x2a,0x6b,0x7e,
+ 0x0e,0xe0,0x2e,0xa6,0x65,0x12,0x65,0x1c,0x65,0x1c,0x57,0x12,0x47,0x3c,0x57,0x1c,
+ 0x67,0x1c,0x69,0x71,0x17,0x1e,0x00,0x97,0x12,0x60,0x83,0x61,0x22,0xf0,0x47,0x2a,
+ 0x06,0xe0,0x6e,0xa7,0x97,0x21,0x77,0x1c,0x64,0x72,0x27,0x1e,0x17,0xf0,0x2e,0xa6,
+ 0x65,0x12,0x65,0x1c,0x65,0x1c,0x57,0x12,0x47,0x3c,0x57,0x1c,0x67,0x1c,0x5e,0x73,
+ 0x37,0x1e,0x00,0x97,0x12,0x60,0x83,0x61,0x84,0x60,0x05,0x60,0x56,0x12,0x57,0x12,
+ 0x5b,0x7f,0x6e,0xa7,0x97,0x21,0x77,0x1c,0x58,0x74,0x47,0x1e,0x00,0x97,0x42,0x62,
+ 0x13,0x60,0x84,0x60,0x05,0x60,0x56,0x12,0x57,0x12,0x54,0x7f,0x55,0x7f,0x55,0x7f,
+ 0x56,0x7f,0x09,0x60,0x56,0x7d,0x4e,0x7b,0x10,0xf2,0x97,0x12,0x97,0x1c,0x54,0x75,
+ 0x57,0x1c,0x27,0xae,0x54,0x76,0x76,0xbe,0x37,0xac,0x86,0xbc,0x56,0xa8,0x52,0x77,
+ 0x07,0x1c,0x07,0x98,0x8d,0xb8,0x66,0xa2,0x43,0x71,0x01,0x1c,0x01,0x92,0x9d,0xb2,
+ 0x17,0x60,0xad,0xb7,0xbd,0xb7,0x07,0x60,0x0d,0xb7,0x1d,0xb7,0x2d,0xb7,0x3d,0xb7,
+ 0xa8,0x61,0xf1,0x11,0xe8,0x03,0xf9,0x11,0x87,0x12,0xc7,0x1c,0x77,0x1c,0xb7,0x1c,
+ 0x07,0xa6,0x4d,0xb6,0x17,0xa7,0x5d,0xb7,0x6d,0xbe,0x7d,0xbc,0xe2,0x12,0xc3,0x12,
+ 0x43,0x7f,0xca,0x12,0x05,0xf0,0x0a,0x20,0x4a,0x01,0xe2,0x12,0xa3,0x12,0x3f,0x7f,
+ 0x87,0x12,0xa7,0x1c,0x07,0x20,0x77,0x1c,0xb7,0x1c,0x07,0xc6,0x76,0x01,0x3c,0x73,
+ 0x03,0xa5,0x3c,0x74,0x04,0xa7,0x87,0x3c,0x57,0x1e,0x76,0x0d,0x05,0xe0,0x29,0x75,
+ 0x05,0x1c,0x05,0x85,0xa5,0x0c,0xe7,0xe7,0x0d,0xa6,0x1d,0xa7,0x87,0x3c,0x67,0x1e,
+ 0xd7,0x1c,0x46,0x65,0x67,0x1c,0x07,0xba,0xca,0x12,0xa8,0x61,0xf1,0x11,0xe8,0x03,
+ 0xf9,0x11,0x05,0xf0,0x0a,0x24,0x4a,0x01,0xe2,0x12,0xa3,0x12,0x2c,0x7f,0x87,0x12,
+ 0xa7,0x1c,0x07,0x24,0x77,0x1c,0xb7,0x1c,0x07,0xc6,0x76,0x01,0x29,0x77,0x07,0xa5,
+ 0x29,0x71,0x01,0xa7,0x87,0x3c,0x57,0x1e,0x76,0x0d,0x02,0xe0,0x0a,0x2a,0xea,0xe7,
+ 0x0d,0xa6,0x1d,0xa7,0x87,0x3c,0x67,0x1e,0xd6,0x12,0x76,0x1c,0x05,0x63,0x65,0x1c,
+ 0x05,0xba,0xc6,0xbe,0x07,0x20,0x67,0x01,0x0d,0xb7,0x87,0x3e,0x1d,0xb7,0xc8,0x12,
+ 0xea,0x12,0x1d,0x72,0x02,0x1c,0x02,0x99,0xe9,0x12,0xbf,0xf0,0x86,0x9e,0x00,0x00,
+ 0x66,0x9e,0x00,0x00,0x56,0xa6,0x00,0x00,0x63,0xa4,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0x2c,0xa4,0x00,0x00,0x2d,0xa4,0x00,0x00,0xf0,0xff,0xff,0xff,0x88,0x05,0x00,0x00,
+ 0xbb,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,0x00,0xc0,0x02,0x00,0x20,0x9e,0x00,0x00,
+ 0x00,0xe0,0x02,0x00,0x5c,0xa6,0x00,0x00,0x00,0x00,0x05,0x1a,0xb4,0x26,0x00,0x00,
+ 0x00,0x28,0x00,0x00,0xf8,0x4c,0x00,0x00,0x80,0x4e,0x00,0x00,0x73,0xa4,0x00,0x00,
+ 0x8a,0x9e,0x00,0x00,0x7a,0x9e,0x00,0x00,0xb8,0x00,0x00,0x00,0xf8,0x3c,0x00,0x00,
+ 0x44,0xa4,0x00,0x00,0x45,0xa4,0x00,0x00,0xb4,0x00,0x00,0x00,0xa7,0x12,0x07,0x24,
+ 0xb7,0x73,0x03,0xa5,0xb7,0x74,0x04,0xa6,0x86,0x3c,0x56,0x1e,0xa5,0x61,0xf1,0x11,
+ 0x57,0x03,0xf9,0x11,0x87,0x1c,0x75,0x12,0x75,0x1c,0xb5,0x1c,0x05,0xc5,0x75,0x01,
+ 0x65,0x0d,0x12,0xe8,0x75,0x12,0x05,0x24,0x55,0x1c,0xb5,0x1c,0x05,0xc5,0x75,0x01,
+ 0x65,0x0d,0x98,0x00,0x08,0xe8,0x07,0x20,0x77,0x1c,0xb7,0x1c,0x07,0xc7,0x77,0x01,
+ 0x67,0x0d,0xef,0xe0,0x08,0x20,0x48,0x01,0x0a,0x24,0x4a,0x01,0xa2,0x12,0x83,0x12,
+ 0xa5,0x7f,0x8e,0x12,0xa7,0x61,0xf1,0x11,0xa7,0x03,0xf9,0x11,0xa3,0x76,0x06,0x1c,
+ 0x06,0x97,0x05,0xf0,0x0e,0x20,0x4e,0x01,0xa2,0x12,0xe3,0x12,0x9e,0x7f,0x9e,0x71,
+ 0x01,0x1c,0x01,0x87,0xe7,0x1c,0x07,0x20,0x77,0x1c,0xb7,0x1c,0x07,0xc6,0x76,0x01,
+ 0x97,0x72,0x02,0xa5,0x97,0x73,0x03,0xa7,0x87,0x3c,0x57,0x1e,0x76,0x0d,0x05,0xe0,
+ 0x97,0x74,0x04,0x1c,0x04,0x84,0xe4,0x0c,0xe5,0xe7,0x0d,0xa6,0x1d,0xa7,0x87,0x3c,
+ 0x67,0x1e,0xd7,0x1c,0x45,0x65,0x57,0x1c,0x07,0xbe,0x8e,0x12,0xa7,0x61,0xf1,0x11,
+ 0xa7,0x03,0xf9,0x11,0x8d,0x76,0x06,0x1c,0x06,0x97,0x05,0xf0,0x0e,0x24,0x4e,0x01,
+ 0xa2,0x12,0xe3,0x12,0x88,0x7f,0x88,0x71,0x01,0x1c,0x01,0x87,0xe7,0x1c,0x07,0x24,
+ 0x77,0x1c,0xb7,0x1c,0x07,0xc6,0x76,0x01,0x81,0x72,0x02,0xa5,0x81,0x73,0x03,0xa7,
+ 0x87,0x3c,0x57,0x1e,0x76,0x0d,0x02,0xe0,0x0e,0x2a,0xe8,0xe7,0x0d,0xa6,0x1d,0xa7,
+ 0x87,0x3c,0x67,0x1e,0xd6,0x12,0x76,0x1c,0x05,0x63,0x65,0x1c,0x05,0xbe,0xc6,0xba,
+ 0x07,0x20,0x67,0x01,0x0d,0xb7,0x87,0x3e,0x1d,0xb7,0x0a,0x2a,0x77,0xe7,0x9e,0x12,
+ 0x78,0x74,0x04,0x1c,0x04,0x89,0x89,0xf0,0xe7,0x12,0x07,0x20,0x70,0x76,0x06,0xa5,
+ 0x08,0xa6,0x86,0x3c,0x56,0x1e,0xaf,0x61,0xf1,0x11,0xf7,0x03,0xf9,0x11,0xc7,0x1c,
+ 0x75,0x12,0x75,0x1c,0xb5,0x1c,0x05,0xc5,0x75,0x01,0x65,0x0d,0x12,0xe8,0x75,0x12,
+ 0x05,0x24,0x55,0x1c,0xb5,0x1c,0x05,0xc5,0x75,0x01,0x65,0x0d,0x9c,0x00,0x08,0xe8,
+ 0x07,0x20,0x77,0x1c,0xb7,0x1c,0x07,0xc7,0x77,0x01,0x67,0x0d,0x6c,0xe0,0x0c,0x20,
+ 0x4c,0x01,0x0e,0x20,0x4e,0x01,0xe2,0x12,0xc3,0x12,0x5e,0x7f,0xca,0x12,0xa2,0x61,
+ 0xf1,0x11,0xe2,0x03,0xf9,0x11,0x5e,0x71,0x01,0x1c,0x01,0x92,0x05,0xf0,0x0a,0x20,
+ 0x4a,0x01,0xe2,0x12,0xa3,0x12,0x57,0x7f,0x5a,0x73,0x03,0x1c,0x03,0x87,0xa7,0x1c,
+ 0x07,0x20,0x77,0x1c,0xb7,0x1c,0x07,0xc6,0x76,0x01,0x50,0x74,0x04,0xa5,0x08,0xa7,
+ 0x87,0x3c,0x57,0x1e,0x76,0x0d,0x05,0xe0,0x51,0x75,0x05,0x1c,0x05,0x85,0xa5,0x0c,
+ 0xe6,0xe7,0x0d,0xa6,0x1d,0xa7,0x87,0x3c,0x67,0x1e,0xd7,0x1c,0x46,0x65,0x67,0x1c,
+ 0x07,0xba,0xca,0x12,0xaf,0x61,0xf1,0x11,0xef,0x03,0xf9,0x11,0x49,0x77,0x07,0x1c,
+ 0x07,0x9f,0x05,0xf0,0x0a,0x24,0x4a,0x01,0xe2,0x12,0xa3,0x12,0x42,0x7f,0x44,0x71,
+ 0x01,0x1c,0x01,0x87,0xa7,0x1c,0x07,0x24,0x77,0x1c,0xb7,0x1c,0x07,0xc6,0x76,0x01,
+ 0x3b,0x72,0x02,0xa5,0x08,0xa7,0x87,0x3c,0x57,0x1e,0x76,0x0d,0x02,0xe0,0x0a,0x2a,
+ 0xe9,0xe7,0x0d,0xa6,0x1d,0xa7,0x87,0x3c,0x67,0x1e,0xd6,0x12,0x76,0x1c,0x05,0x63,
+ 0x65,0x1c,0x05,0xba,0xc6,0xbe,0x07,0x20,0x67,0x01,0x0d,0xb7,0x87,0x3e,0x1d,0xb7,
+ 0x05,0xf0,0x9e,0x12,0x33,0x73,0x03,0x1c,0x03,0x89,0x2d,0x78,0x32,0x74,0x04,0x1c,
+ 0x04,0x84,0xe4,0x0c,0x71,0xe7,0x8d,0xa2,0x97,0x12,0x37,0x3c,0x97,0x05,0x2e,0x75,
+ 0x57,0x1c,0xa6,0x62,0x76,0x1c,0x06,0xb2,0x9d,0xa4,0xb6,0x62,0x76,0x1c,0x06,0xb4,
+ 0xad,0xaf,0xc6,0x62,0x76,0x1c,0x06,0xbf,0xbd,0xa3,0xd6,0x62,0x76,0x1c,0x06,0xb3,
+ 0xe6,0x62,0x76,0x1c,0x2d,0xa5,0x06,0xb5,0x86,0x62,0x67,0x1c,0x4d,0xa5,0x5d,0xa6,
+ 0x86,0x3c,0x56,0x1e,0x77,0xb6,0x86,0x3e,0x87,0xb6,0xf7,0x12,0x27,0x05,0x57,0x01,
+ 0x17,0x22,0x05,0xe8,0x37,0x12,0x47,0x05,0x57,0x01,0x17,0x22,0x0c,0xe0,0x95,0x12,
+ 0x95,0x1c,0x19,0x77,0x75,0x1c,0xf6,0x12,0x26,0x1c,0x16,0x3a,0x25,0xb6,0x37,0x12,
+ 0x47,0x1c,0x17,0x3a,0x35,0xb7,0x09,0x20,0x49,0x01,0x13,0x7c,0x0c,0xa7,0x1c,0xae,
+ 0x8e,0x3c,0x7e,0x1e,0xe9,0x0c,0xe9,0xed,0x02,0x12,0x72,0x20,0x03,0x60,0x44,0x61,
+ 0x0f,0x7f,0x1f,0x60,0xef,0x0c,0x68,0xe0,0x0c,0xa7,0x1c,0xa4,0x84,0x3c,0x74,0x1e,
+ 0x05,0x60,0x0b,0x7b,0xad,0x61,0x2e,0x60,0x5d,0xf0,0x00,0x00,0x44,0xa4,0x00,0x00,
+ 0x45,0xa4,0x00,0x00,0xf8,0x3c,0x00,0x00,0xbc,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,
+ 0xb4,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x8a,0x9e,0x00,0x00,0x22,0x83,0x00,0x00,
+ 0x00,0xe0,0x02,0x00,0x87,0x60,0x07,0x1c,0x57,0x1c,0x07,0xa7,0x07,0x2a,0x40,0xe0,
+ 0x56,0x12,0x56,0x1c,0xc6,0x1c,0x26,0xa7,0x36,0xaa,0x53,0x12,0x03,0x20,0x43,0x01,
+ 0x79,0x12,0x09,0x20,0xa6,0x12,0x56,0x01,0x57,0x01,0xf1,0x11,0xd7,0x03,0xf9,0x11,
+ 0x67,0x1c,0x77,0x1c,0xb7,0x1c,0x2a,0xf0,0x32,0x12,0x32,0x1c,0xc2,0x1c,0x22,0xa6,
+ 0x32,0xa8,0x92,0x12,0x62,0x05,0x42,0x01,0x2e,0x0c,0x1e,0xe8,0xa2,0x12,0x02,0x20,
+ 0x82,0x05,0x42,0x01,0x2e,0x0c,0x18,0xe8,0x58,0x01,0x56,0x01,0xf1,0x11,0xd6,0x03,
+ 0xf9,0x11,0x86,0x1c,0x66,0x1c,0xb6,0x1c,0x07,0xc2,0x72,0x01,0x06,0xc6,0x76,0x01,
+ 0x26,0x0d,0x05,0xe8,0x86,0x60,0x06,0x1c,0x36,0x1c,0x06,0xbf,0x05,0xf0,0x87,0x60,
+ 0x07,0x1c,0x57,0x1c,0x07,0xbf,0x04,0xf0,0x03,0x20,0x43,0x01,0x43,0x0c,0xd4,0xef,
+ 0x05,0x20,0x45,0x01,0x45,0x0c,0xb6,0xef,0x02,0x12,0x72,0x20,0xb0,0x73,0xb0,0x7f,
+ 0xb1,0x77,0x77,0xa7,0x07,0x2a,0x05,0xe8,0xb0,0x77,0x07,0x87,0x78,0x67,0x87,0x1c,
+ 0x11,0xf0,0xae,0x77,0x07,0xa7,0x07,0x2a,0x0c,0xe8,0xad,0x77,0x07,0xa7,0xad,0x76,
+ 0x06,0xae,0x8e,0x3c,0x7e,0x1e,0x2e,0x3e,0xac,0x77,0x17,0xa7,0x7e,0x0d,0x7e,0x02,
+ 0x02,0xf0,0xa9,0x77,0x07,0xae,0x02,0x12,0x72,0x20,0x03,0x60,0x44,0x61,0xa7,0x7f,
+ 0xa8,0x77,0x07,0xa7,0xa8,0x76,0x06,0xa5,0x85,0x3c,0x75,0x1e,0x06,0x60,0x9b,0x73,
+ 0x7e,0x01,0x14,0x60,0x12,0xf0,0x67,0x12,0x37,0x3c,0x67,0x05,0x37,0x1c,0x8d,0x62,
+ 0xd7,0x1c,0x77,0xa2,0x87,0xa7,0x87,0x3c,0x27,0x1e,0xe7,0x0d,0x04,0xe8,0x87,0x60,
+ 0x07,0x1c,0x67,0x1c,0x07,0xb4,0x06,0x20,0x46,0x01,0x56,0x0c,0xec,0xef,0x98,0x7c,
+ 0x02,0x12,0x72,0x20,0xc3,0x12,0x8e,0x7f,0x90,0x77,0x07,0x87,0x97,0x71,0x01,0x1c,
+ 0x01,0x97,0xf2,0x67,0x27,0x1c,0x07,0xa7,0x07,0x2a,0xf1,0xe8,0x02,0x12,0x72,0x20,
+ 0x03,0x60,0x44,0x61,0x8e,0x7f,0x8e,0x67,0x0e,0x1c,0xe2,0x12,0x03,0x60,0x44,0x63,
+ 0x8b,0x7f,0x4d,0x64,0x0d,0x1c,0xd2,0x12,0x03,0x60,0x44,0x63,0x88,0x7f,0x0c,0xa7,
+ 0x89,0x76,0x06,0xa4,0x84,0x3c,0x74,0x1e,0x07,0x60,0x7c,0x7a,0xeb,0x12,0xdc,0x12,
+ 0x7c,0xf0,0x86,0x65,0x76,0x1c,0x66,0x1c,0xa6,0x1c,0x86,0xa9,0x96,0xa6,0x93,0x12,
+ 0x03,0x24,0x43,0x01,0x05,0x60,0x09,0x20,0x62,0x12,0x02,0x24,0x02,0x01,0x7f,0x78,
+ 0x08,0x1c,0x08,0x91,0x12,0x20,0x7e,0x71,0x01,0x1c,0x01,0x92,0x1d,0xf0,0x7b,0x78,
+ 0x08,0x1c,0x08,0x82,0xa8,0x61,0xf1,0x11,0x38,0x03,0xf9,0x11,0x0e,0xf0,0x8f,0x12,
+ 0x2f,0x1c,0xff,0x1c,0x78,0x71,0x1f,0x1c,0x0f,0xcf,0xf1,0x12,0x71,0x01,0x01,0x22,
+ 0x02,0xe0,0xf5,0x1c,0x65,0x01,0x02,0x20,0x42,0x01,0x71,0x71,0x01,0x1c,0x01,0x81,
+ 0x21,0x0d,0xed,0xef,0x03,0x20,0x43,0x01,0x39,0x0d,0xe1,0xef,0x73,0x12,0x73,0x1c,
+ 0xc2,0x61,0x02,0x1c,0x23,0x1c,0x03,0xd5,0x63,0x12,0x63,0x1c,0xb3,0x1c,0x03,0xc2,
+ 0x72,0x01,0x59,0x12,0x79,0x01,0x92,0x0d,0x01,0xe8,0x03,0xd5,0x62,0x12,0x02,0x24,
+ 0x23,0x12,0x23,0x1c,0xe3,0x1c,0x03,0xcf,0x7f,0x01,0x9f,0x0d,0x01,0xe8,0x03,0xd5,
+ 0x63,0x12,0x03,0x20,0x3f,0x12,0x3f,0x1c,0xbf,0x1c,0x0f,0xc8,0x78,0x01,0x98,0x0d,
+ 0x01,0xe8,0x0f,0xd5,0x75,0x12,0x35,0x3c,0x75,0x05,0xa5,0x1c,0x88,0x62,0x85,0x1c,
+ 0x75,0xaf,0x85,0xa5,0x85,0x3c,0xf5,0x1e,0x66,0x1c,0xc6,0x1c,0x06,0xcf,0x7f,0x01,
+ 0x5f,0x0d,0x01,0xe8,0x06,0xd5,0x26,0x12,0x26,0x1c,0xd6,0x1c,0x06,0xc2,0x72,0x01,
+ 0x52,0x0d,0x01,0xe8,0x06,0xd5,0x36,0x12,0x36,0x1c,0xc6,0x1c,0x06,0xc3,0x73,0x01,
+ 0x53,0x0d,0x01,0xe8,0x06,0xd5,0x07,0x20,0x47,0x01,0x47,0x0c,0x82,0xef,0x4a,0x77,
+ 0x17,0xac,0x0c,0x20,0x0d,0x60,0x44,0x71,0x01,0x1c,0x01,0x8b,0x72,0x32,0x2b,0x1c,
+ 0x10,0xf0,0xde,0x12,0xde,0x1c,0x83,0x67,0x03,0x1c,0x3e,0x1c,0x0e,0xc2,0x72,0x01,
+ 0x0b,0xa7,0xf1,0x11,0x72,0x03,0xf9,0x11,0x43,0x66,0x40,0x7f,0x0e,0xd2,0x0d,0x20,
+ 0x4d,0x01,0xdc,0x0d,0xee,0xef,0x36,0x77,0x07,0xa7,0x36,0x76,0x06,0xa5,0x85,0x3c,
+ 0x75,0x1e,0x07,0x60,0x2a,0x72,0x1f,0x60,0x2c,0xf0,0x86,0x65,0x76,0x1c,0x66,0x1c,
+ 0x26,0x1c,0x96,0xa4,0x76,0x12,0x36,0x3c,0x76,0x05,0x26,0x1c,0x88,0x62,0x86,0x1c,
+ 0x76,0xa3,0x86,0xa6,0x86,0x3c,0x36,0x1e,0x44,0x1c,0x43,0x64,0x03,0x1c,0x43,0x1c,
+ 0x03,0xc3,0x73,0x01,0x13,0x3a,0x63,0x0d,0x12,0xe0,0x76,0x12,0x76,0x1c,0xcd,0x61,
+ 0x0d,0x1c,0xd6,0x1c,0x81,0x67,0x01,0x1c,0x14,0x1c,0x06,0xc3,0x73,0x01,0x04,0xc6,
+ 0x76,0x01,0x63,0x0d,0x04,0xe8,0x86,0x60,0x06,0x1c,0x76,0x1c,0x06,0xbf,0x07,0x20,
+ 0x47,0x01,0x57,0x0c,0xd2,0xef,0x02,0x12,0x72,0x20,0x19,0x73,0x11,0x7f,0x20,0x7f,
+ 0x12,0x77,0x07,0x87,0x20,0x72,0x27,0x1c,0x07,0xa7,0x07,0x2a,0x57,0xe8,0x1e,0x77,
+ 0x37,0xa7,0x27,0x2a,0x53,0xe0,0x10,0x77,0x07,0xae,0xee,0x1c,0x02,0x12,0x72,0x20,
+ 0x03,0x60,0x44,0x61,0x0e,0x7f,0x0e,0x77,0x07,0xa7,0x0e,0x76,0x06,0xa5,0x85,0x3c,
+ 0x75,0x1e,0x06,0x60,0x02,0x73,0x7e,0x01,0x14,0x60,0x3a,0xf0,0x8a,0x9e,0x00,0x00,
+ 0x00,0x2d,0x00,0x00,0x20,0xaa,0x00,0x00,0x1c,0x9e,0x00,0x00,0x4a,0xa0,0x00,0x00,
+ 0x3e,0xa4,0x00,0x00,0x3f,0xa4,0x00,0x00,0x86,0x9e,0x00,0x00,0x22,0x83,0x00,0x00,
+ 0x40,0x9f,0x00,0x00,0x41,0x9f,0x00,0x00,0xb0,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,
+ 0xb4,0x00,0x00,0x00,0x00,0xe0,0x02,0x00,0x20,0x9e,0x00,0x00,0x3e,0x84,0x00,0x00,
+ 0x0e,0x46,0x00,0x00,0x86,0x00,0x00,0x00,0x66,0x9e,0x00,0x00,0x67,0x12,0x37,0x3c,
+ 0x67,0x05,0x37,0x1c,0x88,0x62,0x87,0x1c,0x77,0xa2,0x87,0xa7,0x87,0x3c,0x27,0x1e,
+ 0xe7,0x0d,0x04,0xe8,0x87,0x60,0x07,0x1c,0x67,0x1c,0x07,0xb4,0x06,0x20,0x46,0x01,
+ 0x56,0x0c,0xec,0xef,0x02,0x12,0x72,0x20,0xbd,0x73,0xbd,0x7f,0xbc,0x77,0x07,0xa7,
+ 0xbd,0x76,0x06,0xaf,0xbd,0x7d,0x0d,0x1c,0x8f,0x3c,0x7f,0x1e,0x0d,0x9f,0x0d,0xa2,
+ 0xbb,0x71,0x01,0x1c,0x01,0x92,0xba,0x77,0x07,0xb2,0x0d,0x60,0x0b,0xf1,0xd7,0x12,
+ 0x37,0x3c,0xd7,0x05,0xb8,0x73,0x37,0x1c,0xc6,0x62,0x76,0x1c,0x06,0xa5,0xa6,0x62,
+ 0x76,0x1c,0x06,0xa4,0xd6,0x62,0x76,0x1c,0x06,0xa6,0xb8,0x62,0x87,0x1c,0x07,0xa7,
+ 0x53,0x12,0x43,0x05,0x43,0x01,0x1f,0x60,0x3f,0x0c,0x35,0xe8,0x63,0x12,0x73,0x05,
+ 0x43,0x01,0x3f,0x0c,0x30,0xe8,0x87,0x65,0xd7,0x1c,0x77,0x1c,0xaa,0x71,0x17,0x1c,
+ 0x87,0xa7,0xfc,0x12,0x22,0x60,0x72,0x0c,0x03,0xe0,0x7c,0x12,0x0c,0x24,0x4c,0x01,
+ 0xa6,0x73,0x03,0xaa,0xa6,0x12,0x06,0x24,0x67,0x0d,0x03,0xe8,0x07,0x20,0x7a,0x12,
+ 0x4a,0x01,0x87,0x65,0xd7,0x1c,0x77,0x1c,0x9f,0x74,0x47,0x1c,0x97,0xa7,0x16,0x60,
+ 0x9f,0x75,0x05,0x1c,0x05,0x96,0x28,0x60,0x78,0x0c,0x04,0xe0,0x76,0x12,0x06,0x24,
+ 0x06,0x01,0x05,0x91,0x99,0x72,0x12,0xa9,0x96,0x12,0x06,0x24,0x67,0x0d,0x26,0xe8,
+ 0x07,0x20,0x79,0x12,0x22,0xf0,0x1c,0x60,0x23,0x60,0x43,0x0c,0x03,0xe0,0x04,0x24,
+ 0x4c,0x12,0x4c,0x01,0x91,0x74,0x04,0xaa,0xa4,0x12,0x04,0x24,0x45,0x0d,0x03,0xe8,
+ 0x05,0x20,0x5a,0x12,0x4a,0x01,0x18,0x60,0x8d,0x75,0x05,0x1c,0x05,0x98,0x2f,0x60,
+ 0x7f,0x0c,0x03,0xe0,0x07,0x24,0x07,0x01,0x05,0x91,0x87,0x72,0x12,0xa9,0x97,0x12,
+ 0x07,0x24,0x76,0x0d,0x03,0xe8,0x06,0x20,0x69,0x12,0x49,0x01,0x08,0x60,0x84,0x73,
+ 0x03,0x1c,0x03,0x98,0x84,0x74,0x04,0x1c,0x04,0x98,0xdb,0x12,0x35,0xf0,0x82,0x75,
+ 0x05,0x1c,0x05,0x87,0xd7,0x1c,0x77,0x1c,0x81,0x76,0x67,0x1c,0x07,0xce,0x7e,0x01,
+ 0x0e,0x22,0x24,0xe0,0xb2,0x12,0xc3,0x12,0xd4,0x12,0x7d,0x7f,0x72,0x01,0x2e,0x1b,
+ 0x7e,0x01,0x2e,0x3a,0x7c,0x71,0x01,0x1c,0x01,0x87,0xf1,0x11,0xe7,0x03,0xf9,0x11,
+ 0x74,0x72,0x02,0x1c,0x02,0x83,0x73,0x1c,0x72,0x72,0x02,0x1c,0x02,0x93,0xd7,0x12,
+ 0x17,0x3c,0x07,0x24,0x67,0x01,0xf1,0x11,0xe7,0x03,0xf9,0x11,0x6e,0x74,0x04,0x1c,
+ 0x04,0x85,0x75,0x1c,0x6c,0x74,0x04,0x1c,0x04,0x95,0xe8,0x1c,0x0d,0x20,0x4d,0x01,
+ 0xd9,0x0c,0xcd,0xe7,0x0c,0x20,0x4c,0x01,0xca,0x0c,0x12,0xe8,0xc7,0x12,0x17,0x3c,
+ 0x07,0x24,0x67,0x01,0x68,0x76,0x06,0x1c,0x06,0x97,0x60,0x77,0x07,0x1c,0x07,0x8d,
+ 0xa2,0x61,0xf1,0x11,0xc2,0x03,0xf9,0x11,0x60,0x71,0x01,0x1c,0x01,0x92,0xe8,0xf7,
+ 0xbd,0x12,0xbe,0x12,0x3e,0x3c,0xbe,0x1c,0x57,0x73,0x3e,0x1c,0x5f,0x74,0x4e,0x1c,
+ 0x59,0x75,0x05,0x1c,0x05,0x82,0x72,0x3c,0x83,0x12,0x5c,0x7f,0x62,0x01,0x4e,0xb2,
+ 0x82,0x3e,0x5e,0xb2,0x53,0x76,0x06,0x1c,0x06,0x82,0x72,0x3c,0x83,0x12,0x57,0x7f,
+ 0x62,0x01,0x6e,0xb2,0x27,0x12,0x87,0x3e,0x7e,0xb7,0x55,0x78,0x08,0x87,0x55,0x71,
+ 0x17,0x1c,0x07,0xa7,0x27,0x2a,0x06,0xe0,0x54,0x73,0x32,0x1c,0x62,0x01,0x6e,0xb2,
+ 0x82,0x3e,0x7e,0xb2,0xd5,0x12,0x35,0x3c,0x56,0x12,0xd6,0x1c,0x42,0x74,0x46,0x1c,
+ 0x4f,0x77,0x67,0x1c,0x04,0x2c,0x07,0xb4,0x48,0x77,0x76,0x1c,0xd5,0x05,0x3d,0x78,
+ 0x85,0x1c,0x87,0x62,0x57,0x1c,0x77,0xa4,0x87,0xa7,0x87,0x3c,0x47,0x1e,0x86,0xb7,
+ 0x87,0x3e,0x96,0xb7,0xef,0x62,0xf5,0x1c,0x05,0xa7,0xa6,0xb7,0x07,0x60,0xb6,0xb7,
+ 0x0d,0x20,0x4d,0x01,0x31,0x71,0x01,0x1c,0x01,0x81,0x1d,0x0c,0xf0,0xee,0x40,0x77,
+ 0x57,0xa7,0x07,0x2f,0x07,0x2a,0x31,0xe8,0x2d,0x72,0x02,0x1c,0x02,0x82,0x02,0x2a,
+ 0x05,0xe8,0x3c,0x72,0x2a,0x74,0x04,0x1c,0x04,0x83,0x3b,0x7f,0x0e,0x60,0x29,0x7d,
+ 0x28,0x7c,0x1c,0xf0,0xe7,0x12,0x37,0x3c,0xe7,0x1c,0xd7,0x1c,0x2f,0x75,0x57,0x1c,
+ 0x47,0xab,0x57,0xa3,0x83,0x3c,0x67,0xaf,0x77,0xa4,0x84,0x3c,0x87,0xa6,0x97,0xa5,
+ 0x85,0x3c,0x65,0x1e,0x05,0x3d,0xa7,0xa6,0xb7,0xa7,0x87,0x3c,0x30,0x72,0xb3,0x1e,
+ 0xf4,0x1e,0x05,0x3b,0x76,0x1e,0x2c,0x7f,0x0e,0x20,0x4e,0x01,0x0c,0xa7,0x7e,0x0c,
+ 0xe1,0xef,0x07,0x2a,0x02,0xe8,0x2a,0x72,0x28,0x7f,0x21,0x77,0x07,0x87,0x29,0x76,
+ 0x67,0x1c,0x07,0xa7,0x07,0x2a,0xa2,0xe8,0x22,0x77,0x57,0xa7,0x77,0x36,0x10,0xe8,
+ 0x26,0x77,0x07,0xa7,0x26,0x76,0x06,0xa6,0x86,0x3c,0x76,0x1e,0x25,0x77,0x07,0xa7,
+ 0x07,0x3d,0x67,0x1e,0x24,0x76,0x06,0xa3,0x83,0x3d,0x23,0x72,0x73,0x1e,0x1a,0x7f,
+ 0x14,0x77,0x07,0x87,0x1d,0x76,0x06,0xa6,0x1d,0x75,0x40,0xf0,0x40,0x9f,0x00,0x00,
+ 0x00,0x2d,0x00,0x00,0x41,0x9f,0x00,0x00,0xb0,0x00,0x00,0x00,0xb4,0x00,0x00,0x00,
+ 0x94,0x9f,0x00,0x00,0x8a,0x9e,0x00,0x00,0x20,0x9e,0x00,0x00,0xb8,0x00,0x00,0x00,
+ 0xbc,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x00,0xe0,0x02,0x00,
+ 0xe0,0x3d,0x00,0x00,0xc4,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0xd0,0x83,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0x20,0x01,0x00,0x00,0x80,0xff,0xff,0xff,0x0b,0x01,0x00,0x00,
+ 0x44,0xaa,0x00,0x00,0x68,0x07,0x00,0x00,0x7a,0x25,0x00,0x00,0x78,0x07,0x00,0x00,
+ 0x0c,0x07,0x00,0x00,0x8d,0x00,0x00,0x00,0x52,0xa4,0x00,0x00,0x53,0xa4,0x00,0x00,
+ 0x54,0xa4,0x00,0x00,0x55,0xa4,0x00,0x00,0x8c,0x07,0x00,0x00,0x05,0xa5,0x85,0x3c,
+ 0x65,0x1e,0xa6,0x76,0x06,0xa6,0x06,0x3d,0x56,0x1e,0xa5,0x75,0x05,0xa5,0x85,0x3d,
+ 0x65,0x1e,0xa4,0x76,0x76,0x1c,0x06,0xa4,0xa4,0x76,0x76,0x1c,0x06,0xa6,0x86,0x3c,
+ 0x46,0x1e,0xa2,0x74,0x74,0x1c,0x04,0xa4,0x04,0x3d,0x64,0x1e,0xa1,0x76,0x76,0x1c,
+ 0x06,0xa6,0x86,0x3d,0x46,0x1e,0x56,0x0c,0x9f,0x76,0x24,0xe8,0x07,0x60,0x06,0xb7,
+ 0x9e,0x76,0x0c,0xf0,0x9d,0x74,0x04,0xa5,0x05,0x20,0x45,0x01,0x04,0xb5,0x9b,0x78,
+ 0x87,0x1c,0x07,0xa7,0x27,0x3e,0x57,0x0c,0x02,0xe0,0x17,0x60,0x06,0xb7,0x95,0x77,
+ 0x07,0xa7,0x07,0x2a,0x97,0x77,0x02,0xe8,0xa6,0x60,0x04,0xf0,0x07,0xa6,0x06,0x2a,
+ 0x02,0xe8,0x06,0x24,0x07,0xb6,0x07,0xa7,0x07,0x2a,0x08,0xe8,0x16,0x60,0x91,0x77,
+ 0x07,0xb6,0x04,0xf0,0x06,0xa5,0x05,0x2a,0xdd,0xef,0xe9,0xf7,0x8f,0x71,0x10,0x1c,
+ 0x68,0x00,0xf0,0x21,0xcf,0x00,0xf0,0x25,0x78,0x00,0x01,0x64,0x10,0x05,0x8b,0x77,
+ 0x07,0xae,0x3c,0x60,0x0e,0x2a,0x03,0xe8,0xec,0x12,0x0c,0x24,0x4c,0x01,0x88,0x77,
+ 0x07,0xa5,0xa6,0x60,0x56,0x0c,0x01,0xe0,0x07,0xb6,0x0a,0x12,0x1a,0x21,0xa6,0x12,
+ 0x07,0x60,0x04,0x2c,0x75,0x12,0x06,0xd4,0x83,0x60,0x03,0x1c,0x73,0x1c,0x03,0xb5,
+ 0x07,0x20,0x16,0x20,0xa7,0x2a,0xf7,0xe7,0x6d,0x64,0xf1,0x11,0xde,0x03,0xf9,0x11,
+ 0x7d,0x72,0x7d,0x73,0xe3,0x1c,0xd4,0x12,0x7d,0x7f,0x7d,0x76,0xa6,0xa5,0xb6,0xa7,
+ 0x87,0x3c,0x57,0x1e,0x7c,0x71,0x71,0x1c,0x1f,0x12,0x7b,0x72,0x27,0x1c,0x7b,0x12,
+ 0x6b,0x01,0xc6,0xa5,0xd6,0xa7,0x87,0x3c,0x57,0x1e,0x76,0x72,0x72,0x1c,0x76,0x73,
+ 0x37,0x1c,0x79,0x12,0x69,0x01,0x05,0x60,0x75,0x73,0xf1,0x11,0xcd,0x03,0xf9,0x11,
+ 0xf8,0x67,0x57,0x12,0x37,0x1c,0x72,0x74,0x47,0x1c,0x07,0xa7,0x27,0x2a,0x45,0xe0,
+ 0x54,0x12,0x34,0x3c,0x54,0x05,0x47,0x12,0xe7,0x1c,0x37,0x1c,0x6e,0x76,0x67,0x1c,
+ 0x37,0xac,0x47,0xa6,0x86,0x3c,0xc6,0x1e,0x57,0xac,0x67,0xa7,0x87,0x3c,0xc7,0x1e,
+ 0x66,0x1c,0xd4,0x1c,0x34,0x1c,0x67,0x7c,0xc4,0x1c,0x34,0xa1,0x44,0xac,0x8c,0x3c,
+ 0x1c,0x1e,0xc6,0x05,0x66,0x01,0x77,0x1c,0x54,0xac,0x64,0xa4,0x84,0x3c,0xc4,0x1e,
+ 0x47,0x05,0x74,0x12,0x64,0x01,0x67,0x12,0x77,0x01,0x78,0x0d,0x03,0xe8,0xf7,0x0d,
+ 0x03,0xe8,0x03,0xf0,0x76,0x32,0x01,0xf0,0xb6,0x12,0x47,0x12,0x77,0x01,0x78,0x0d,
+ 0x03,0xe8,0x27,0x0d,0x03,0xe8,0x03,0xf0,0x74,0x32,0x01,0xf0,0x94,0x12,0x57,0x12,
+ 0x37,0x3c,0x57,0x05,0x37,0x1c,0x54,0x71,0x17,0x1c,0x66,0x01,0x57,0xb6,0x86,0x3e,
+ 0x67,0xb6,0x64,0x01,0x77,0xb4,0x84,0x3e,0x87,0xb4,0x05,0x20,0xa5,0x2a,0xb1,0xe7,
+ 0x0e,0x60,0x4a,0x78,0xa9,0x12,0x3f,0xf0,0xe7,0x12,0x37,0x3c,0xe7,0x1c,0x87,0x1c,
+ 0x4b,0x76,0x76,0x1c,0x46,0xa5,0x56,0xac,0x8c,0x3c,0x5c,0x1e,0x66,0xa5,0x76,0xab,
+ 0x8b,0x3c,0x5b,0x1e,0x3c,0x7a,0x0d,0x60,0x46,0x72,0x72,0x1c,0xb0,0x92,0x0d,0x01,
+ 0xa0,0x91,0x0a,0xa7,0x07,0x2a,0x21,0xe8,0xd7,0x12,0x37,0x3c,0xd7,0x05,0x87,0x1c,
+ 0x3e,0x72,0x27,0x1c,0x57,0xa6,0x67,0xa2,0x82,0x3c,0x62,0x1e,0xc2,0x14,0x77,0xa6,
+ 0x87,0xa3,0x83,0x3c,0x63,0x1e,0xb3,0x14,0x72,0x01,0x73,0x01,0x3a,0x7f,0xe7,0x12,
+ 0xe7,0x1c,0x23,0x61,0x03,0x1c,0x37,0x1c,0x07,0xc6,0x62,0x0c,0x06,0xe0,0x07,0xd2,
+ 0x84,0x62,0x04,0x1c,0x04,0xa5,0xb0,0x84,0x04,0xb5,0x0d,0x20,0x6a,0x20,0xad,0x2a,
+ 0xd6,0xe7,0x0e,0x20,0x4e,0x01,0x22,0x76,0x06,0xa2,0x2e,0x0c,0xbd,0xef,0x9a,0x12,
+ 0x2e,0x7d,0xd3,0x12,0x05,0x60,0x54,0x12,0x8b,0x2c,0x24,0x7e,0x0c,0x2c,0xe8,0x12,
+ 0x19,0x60,0x6a,0xf0,0x04,0x20,0x44,0x01,0x46,0x12,0x5a,0xf0,0x03,0xaf,0xbf,0x0f,
+ 0x59,0xe8,0x67,0x12,0x37,0x3c,0x67,0x1c,0xe7,0x1c,0x21,0x71,0x17,0x1c,0x07,0xa1,
+ 0xf1,0x0f,0x4c,0xe0,0x6f,0x12,0x6f,0x1c,0x21,0x61,0x01,0x1c,0x1f,0x1c,0x0a,0xc1,
+ 0x0f,0xcf,0x1f,0x0c,0x3b,0xe8,0x07,0xbc,0x41,0xf0,0x00,0x00,0x54,0xa4,0x00,0x00,
+ 0x55,0xa4,0x00,0x00,0x8e,0x00,0x00,0x00,0x8f,0x00,0x00,0x00,0x90,0x00,0x00,0x00,
+ 0x91,0x00,0x00,0x00,0x3c,0xa4,0x00,0x00,0x3d,0xa4,0x00,0x00,0xc6,0x00,0x00,0x00,
+ 0x60,0xa6,0x00,0x00,0x62,0xa4,0x00,0x00,0xd0,0x00,0x00,0x00,0xe5,0xa3,0x00,0x00,
+ 0x94,0x9f,0x00,0x00,0x46,0xa1,0x00,0x00,0x8c,0xa1,0x00,0x00,0x8c,0x82,0x00,0x00,
+ 0x20,0x9e,0x00,0x00,0x81,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x8a,0x9e,0x00,0x00,
+ 0x8e,0x05,0x00,0x00,0x00,0x03,0x00,0x00,0xb8,0x02,0x00,0x00,0x08,0x01,0x00,0x00,
+ 0x0b,0x01,0x00,0x00,0xc0,0x0c,0x00,0x00,0x95,0x9f,0x00,0x00,0x57,0x12,0x37,0x3c,
+ 0x57,0x1c,0x87,0x1c,0xbe,0x76,0x67,0x1c,0x07,0xbc,0x04,0xf0,0x06,0x20,0x46,0x01,
+ 0x26,0x0f,0xa4,0xe7,0x03,0xa7,0xb7,0x0f,0x04,0xe8,0x81,0x60,0x01,0x1c,0x17,0x1c,
+ 0x07,0xb9,0x05,0x20,0x83,0x20,0x1a,0x20,0x24,0x0f,0x94,0xe7,0xb5,0x77,0x07,0xac,
+ 0xb5,0x77,0x07,0xab,0xb5,0x74,0x05,0x60,0x56,0x12,0x8f,0x2c,0xb4,0x7e,0x12,0x60,
+ 0x24,0xf0,0x04,0xa7,0xf7,0x0f,0x1f,0xe0,0x57,0x12,0x37,0x3c,0x57,0x1c,0xe7,0x1c,
+ 0xb0,0x73,0x37,0x1c,0x87,0xa3,0x97,0xa7,0x87,0x3c,0x37,0x1e,0x07,0x3d,0x07,0x3b,
+ 0xb7,0x0d,0x11,0xe0,0x0e,0xf0,0x67,0x12,0x07,0x20,0x47,0x01,0x83,0x60,0x03,0x1c,
+ 0x63,0x1c,0x03,0xaa,0x0a,0x2a,0x04,0xe0,0x04,0xb6,0x03,0xb2,0x76,0x12,0x03,0xf0,
+ 0x76,0x12,0xc6,0x0c,0xf0,0xef,0x05,0x20,0x84,0x20,0x05,0x01,0xc1,0x0c,0xd9,0xef,
+ 0x0e,0x60,0xeb,0x12,0x8a,0x2c,0x9d,0x79,0x11,0xf0,0x0d,0xa7,0xa7,0x0f,0x0b,0xe8,
+ 0xe2,0x12,0x32,0x3c,0xe2,0x1c,0x92,0x1c,0x9b,0x74,0x42,0x1c,0xd3,0x12,0x94,0x60,
+ 0x9a,0x7f,0x0e,0x20,0x4e,0x01,0x0b,0x20,0x4b,0x01,0x8d,0x20,0xcb,0x0f,0xed,0xe7,
+ 0x97,0x77,0x07,0xbe,0x97,0x77,0x07,0xa8,0x97,0x76,0x06,0xb8,0x86,0x12,0x06,0x20,
+ 0x46,0x01,0x07,0xb6,0x35,0x60,0x65,0x0c,0x02,0xe0,0x06,0x60,0x07,0xb6,0x07,0xac,
+ 0x02,0x12,0x72,0x20,0x03,0x60,0xa4,0x60,0x90,0x7f,0x8c,0x77,0x07,0xab,0x8f,0x72,
+ 0x04,0x60,0x86,0x7d,0x63,0x64,0xf1,0x11,0xc3,0x03,0xf9,0x11,0x1e,0x60,0x2c,0xf0,
+ 0x02,0xaf,0xf7,0x12,0x37,0x3c,0xf7,0x05,0x37,0x1c,0xd7,0x1c,0x89,0x76,0x76,0x1c,
+ 0x06,0xbe,0x88,0x76,0x76,0x1c,0x47,0x12,0x37,0x3c,0x47,0x1c,0xd7,0x1c,0x86,0x79,
+ 0x97,0x1c,0x77,0xaa,0x87,0xa5,0x85,0x3c,0xa5,0x1e,0x36,0xb5,0x85,0x3e,0x46,0xb5,
+ 0x97,0xaa,0xa7,0xa5,0x85,0x3c,0xa5,0x1e,0x56,0xb5,0x85,0x3e,0x66,0xb5,0xb7,0xa5,
+ 0xc7,0xa7,0x87,0x3c,0x57,0x1e,0x76,0xb7,0x87,0x3e,0x86,0xb7,0x81,0x60,0x01,0x1c,
+ 0x1f,0x1c,0x0f,0xbe,0x04,0x20,0x82,0x20,0x04,0x01,0xb1,0x0c,0xd1,0xef,0x04,0x60,
+ 0x46,0x12,0x65,0x64,0xf1,0x11,0xc5,0x03,0xf9,0x11,0x68,0x7d,0x5f,0x12,0xdf,0x1c,
+ 0x0e,0x2c,0xf2,0x67,0x87,0x60,0x07,0x1c,0x67,0x1c,0x07,0xa3,0x03,0x2a,0x12,0xe0,
+ 0xf7,0x12,0x47,0x1c,0x6b,0x79,0x97,0x1c,0x07,0xb3,0x67,0x12,0x37,0x3c,0x67,0x05,
+ 0x57,0x1c,0xd7,0x1c,0x68,0x7b,0xb7,0x1c,0x37,0xbe,0x47,0xb2,0x57,0xbe,0x67,0xb2,
+ 0x77,0xb3,0x87,0xb3,0x06,0x20,0x64,0x20,0xa6,0x2a,0xe4,0xe7,0xcd,0x12,0x4d,0x3c,
+ 0xc7,0x12,0x67,0x3c,0x7d,0x1c,0x55,0x79,0x9d,0x1c,0x60,0x71,0x1d,0x1c,0x0b,0x60,
+ 0xba,0x12,0x62,0x64,0xf1,0x11,0xc2,0x03,0xf9,0x11,0xa0,0x92,0x92,0x1c,0xb0,0x92,
+ 0x63,0x64,0xf1,0x11,0x38,0x03,0xf9,0x11,0x94,0x12,0x84,0x1c,0xc0,0x94,0xb0,0x87,
+ 0xb7,0x1c,0x53,0x75,0x57,0x1c,0x07,0xa7,0x07,0x2a,0x52,0xe8,0xc0,0x87,0xb7,0x1c,
+ 0x57,0x1c,0x07,0xa7,0x07,0x2a,0x4c,0xe8,0x17,0x60,0x0d,0xb7,0xce,0x12,0xce,0x1c,
+ 0xc7,0x12,0x37,0x3c,0x7e,0x1c,0xae,0x1c,0x4e,0x76,0x6e,0x1c,0x3e,0x3c,0x9e,0x1c,
+ 0xa7,0x12,0x37,0x3c,0xa7,0x05,0xa0,0x86,0x76,0x1c,0x96,0x1c,0x46,0x71,0x16,0x1c,
+ 0x36,0xa4,0x46,0xa5,0x85,0x3c,0x45,0x1e,0x87,0x1c,0x97,0x1c,0x17,0x1c,0x37,0xa3,
+ 0x47,0xa4,0x84,0x3c,0x34,0x1e,0x45,0x05,0x65,0x01,0x3e,0xb5,0x85,0x3e,0x4e,0xb5,
+ 0x56,0xa5,0x66,0xa3,0x83,0x3c,0x53,0x1e,0x57,0xa6,0x67,0xa7,0x87,0x3c,0x67,0x1e,
+ 0x73,0x05,0x63,0x01,0x5e,0xb3,0x37,0x12,0x87,0x3e,0x6e,0xb7,0x3e,0xa7,0x4e,0xa2,
+ 0x82,0x3c,0x72,0x1e,0x02,0x3d,0x02,0x3b,0x73,0x01,0x36,0x7f,0x7e,0xb2,0x82,0x3e,
+ 0x8e,0xb2,0x3e,0xa7,0x4e,0xa2,0x82,0x3c,0x72,0x1e,0x02,0x3d,0x5e,0xa7,0x6e,0xa3,
+ 0x83,0x3c,0x73,0x1e,0x03,0x3d,0x02,0x3b,0x03,0x3b,0x2f,0x7f,0x7d,0xb2,0x12,0xf0,
+ 0x06,0x60,0x0d,0xb6,0xc7,0x12,0xc7,0x1c,0xc5,0x12,0x35,0x3c,0x57,0x1c,0xa7,0x1c,
+ 0x28,0x72,0x27,0x1c,0x37,0x3c,0x97,0x1c,0x37,0xb6,0x47,0xb6,0x57,0xb6,0x67,0xb6,
+ 0x77,0xb6,0x87,0xb6,0x0a,0x20,0x6b,0x20,0x7d,0x20,0x63,0x64,0x3b,0x0f,0x8f,0xe7,
+ 0x18,0x77,0x07,0xae,0x22,0x77,0x07,0x8c,0x22,0x78,0xa0,0x98,0x22,0x76,0x87,0x12,
+ 0x05,0x60,0x0e,0x73,0x29,0x60,0x6f,0x64,0xe4,0x12,0xf1,0x11,0xf4,0x03,0xf9,0x11,
+ 0x43,0x1c,0x1d,0x7a,0xca,0x1c,0x4b,0x60,0x54,0x12,0x1c,0x71,0x1c,0x1c,0x1d,0x60,
+ 0x07,0xa2,0x42,0x2a,0x35,0xe0,0x07,0xb4,0x56,0xf0,0x00,0x00,0x0b,0x01,0x00,0x00,
+ 0x94,0x9f,0x00,0x00,0x86,0x9e,0x00,0x00,0x95,0x9f,0x00,0x00,0x8a,0x9e,0x00,0x00,
+ 0x08,0x01,0x00,0x00,0x66,0x01,0x00,0x00,0x8c,0x82,0x00,0x00,0xef,0x9f,0x00,0x00,
+ 0xe5,0xa3,0x00,0x00,0xe4,0xa3,0x00,0x00,0xc4,0x09,0x00,0x00,0xf0,0x9f,0x00,0x00,
+ 0x02,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x60,0x01,0x00,0x00,0x1a,0x04,0x00,0x00,
+ 0x83,0x00,0x00,0x00,0xc0,0x0c,0x00,0x00,0xee,0x0b,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0xfa,0xa3,0x00,0x00,0x0e,0xa4,0x00,0x00,0xa1,0x00,0x00,0x00,0xa2,0x00,0x00,0x00,
+ 0x02,0x2a,0x19,0xe0,0x32,0x12,0x52,0x1c,0xb8,0x78,0x82,0x1c,0x02,0xa2,0x02,0x2a,
+ 0x0e,0xe8,0x06,0xa2,0x02,0x2a,0x03,0xe0,0x78,0x12,0x98,0x24,0x08,0xbe,0x02,0x20,
+ 0x42,0x01,0x06,0xb2,0x0c,0xa8,0x28,0x0c,0x19,0xe0,0x07,0xbd,0x17,0xf0,0x07,0xb2,
+ 0x06,0xb2,0xa7,0xb2,0x13,0xf0,0x07,0xb9,0x32,0x12,0x52,0x1c,0xab,0x71,0x12,0x1c,
+ 0x02,0xa2,0x02,0x2a,0x03,0xe8,0x06,0xb4,0xa7,0xb4,0x08,0xf0,0xa7,0xa2,0x02,0x20,
+ 0x42,0x01,0xa7,0xb2,0x0a,0xa8,0x28,0x0c,0x01,0xe0,0x07,0xbb,0x07,0x20,0x06,0x20,
+ 0x65,0x20,0xf5,0x0f,0x95,0xe7,0xa1,0x76,0x06,0x87,0xa2,0x64,0x27,0x1c,0x07,0xac,
+ 0x0e,0x60,0x9f,0x7b,0x87,0xf0,0xb7,0x12,0xe7,0x1c,0x9e,0x73,0x37,0x1c,0x07,0xa7,
+ 0x07,0x2a,0x05,0xe8,0x0e,0x20,0x4e,0x01,0x6e,0x0c,0xf5,0xef,0x7b,0xf0,0x26,0x60,
+ 0x98,0x7d,0xb7,0x12,0xc7,0x1c,0x97,0x74,0x47,0x1c,0x07,0xa5,0x05,0x24,0x45,0x01,
+ 0x56,0x0c,0x6c,0xe8,0xd6,0x12,0xe6,0x1c,0x46,0x1c,0x15,0x60,0x06,0xb5,0x46,0x60,
+ 0x07,0xb6,0xe8,0x12,0x38,0x3c,0xb0,0x98,0x82,0x12,0xe2,0x05,0xd2,0x1c,0xc8,0x12,
+ 0x38,0x3c,0x89,0x12,0xc9,0x05,0xd3,0x12,0x93,0x1c,0x8b,0x71,0x12,0x1c,0x13,0x1c,
+ 0x74,0x60,0x8a,0x7f,0x9d,0x1c,0x84,0x72,0x2d,0x1c,0x0a,0x60,0xc0,0x98,0x09,0x28,
+ 0xd0,0x99,0xb0,0x89,0xb8,0x12,0x98,0x1c,0xb0,0x98,0xd0,0x82,0xd2,0x1c,0x97,0x12,
+ 0xe7,0x05,0x72,0x1c,0xd3,0x12,0x74,0x60,0x81,0x7f,0x7d,0x78,0xb0,0x82,0xa2,0x1c,
+ 0xc0,0x83,0xb3,0x1c,0xa3,0x1c,0x7e,0x71,0x12,0x1c,0x13,0x1c,0x84,0x60,0x7b,0x7f,
+ 0x07,0x60,0x0d,0xb7,0x62,0x64,0x2d,0x1c,0x03,0x65,0x3a,0x1c,0x7a,0x74,0x4a,0x0f,
+ 0xe4,0xe7,0x87,0x12,0xc7,0x1c,0x78,0x76,0x76,0x1c,0x06,0xa5,0xe8,0x1c,0x76,0x76,
+ 0x86,0x1c,0x06,0xb5,0x76,0x76,0x76,0x1c,0x06,0xa5,0x74,0x76,0x86,0x1c,0x06,0xb5,
+ 0x74,0x76,0x76,0x1c,0x06,0xa5,0x72,0x76,0x86,0x1c,0x06,0xb5,0x72,0x76,0x76,0x1c,
+ 0x06,0xa5,0x70,0x76,0x86,0x1c,0x06,0xb5,0x70,0x76,0x76,0x1c,0x06,0xa5,0x6e,0x76,
+ 0x86,0x1c,0x06,0xb5,0x6e,0x76,0x76,0x1c,0x06,0xa5,0x6c,0x76,0x86,0x1c,0x06,0xb5,
+ 0x6c,0x75,0x57,0x1c,0x07,0xa7,0x58,0x1c,0x08,0xb7,0x04,0xf0,0x0c,0x20,0x4c,0x01,
+ 0xac,0x2a,0x86,0xe7,0x5a,0x76,0x06,0x87,0xa8,0x64,0x87,0x1c,0x07,0xa6,0x6e,0x0c,
+ 0x03,0xe0,0x97,0x60,0xc7,0x0c,0x6f,0xe7,0x56,0x76,0x62,0x77,0x07,0xa3,0x62,0x77,
+ 0x07,0xa7,0x69,0x64,0x71,0x12,0xf1,0x11,0x91,0x03,0xf9,0x11,0x62,0x12,0x12,0x1c,
+ 0x4e,0x7b,0xb2,0x1c,0x7a,0x12,0x4a,0x3c,0x75,0x12,0x65,0x3c,0x5a,0x1c,0xa6,0x1c,
+ 0x50,0x7c,0xc6,0x1c,0x05,0x60,0xf1,0x11,0x39,0x03,0xf9,0x11,0x0a,0x28,0x3d,0x12,
+ 0x4d,0x3c,0x34,0x12,0x64,0x3c,0x4d,0x1c,0x46,0x7e,0x54,0x12,0xe4,0x1c,0x45,0x78,
+ 0x84,0x1c,0x04,0xa4,0x14,0x24,0x44,0x01,0x1b,0x60,0x4b,0x0c,0x5c,0xe8,0x02,0xa4,
+ 0x04,0x2a,0x59,0xe0,0x24,0x60,0x02,0xb4,0x5c,0x12,0x3c,0x3c,0x5c,0x05,0xc4,0x12,
+ 0x94,0x1c,0xe4,0x1c,0x4a,0x78,0x84,0x1c,0x34,0xa8,0x44,0xaf,0x8f,0x3c,0x8f,0x1e,
+ 0x1c,0x1c,0xec,0x1c,0x46,0x78,0x8c,0x1c,0x3c,0xbf,0x8f,0x3e,0x4c,0xbf,0x54,0xaf,
+ 0x64,0xa4,0x84,0x3c,0xf4,0x1e,0x5c,0xb4,0x84,0x3e,0x6c,0xb4,0x04,0x67,0x7c,0xb4,
+ 0x34,0x60,0x8c,0xb4,0x34,0x12,0x34,0x1c,0x3f,0x12,0x3f,0x3c,0xf4,0x1c,0x54,0x1c,
+ 0x3c,0x7c,0xc4,0x1c,0x34,0x3c,0xe4,0x1c,0x74,0xac,0x84,0xaf,0x8f,0x3c,0xcf,0x1e,
+ 0xfc,0x63,0x06,0xbb,0xfc,0x0c,0x34,0xaf,0x44,0xac,0x8c,0x3c,0xfc,0x1e,0x7f,0x12,
+ 0x7f,0x1c,0x7b,0x12,0x3b,0x3c,0xbf,0x1c,0x5f,0x1c,0x03,0xe8,0x31,0x78,0x8f,0x1c,
+ 0x02,0xf0,0x2f,0x7b,0xbf,0x1c,0x3f,0x3c,0xef,0x1c,0x3f,0xbc,0x8c,0x3e,0x4f,0xbc,
+ 0x54,0xac,0x64,0xae,0x8e,0x3c,0xce,0x1e,0x5f,0xbe,0x8e,0x3e,0x6f,0xbe,0x74,0xae,
+ 0x84,0xa4,0x84,0x3c,0xe4,0x1e,0x7f,0xb4,0x84,0x3e,0x8f,0xb4,0xa4,0x12,0x64,0x1c,
+ 0xd4,0x1c,0x74,0xa4,0x76,0xb4,0x05,0x20,0x62,0x20,0x76,0x20,0xa5,0x2a,0x94,0xe7,
+ 0x04,0x60,0x0f,0x7e,0x6c,0x64,0x7a,0x12,0xf1,0x11,0xca,0x03,0xf9,0x11,0x42,0x12,
+ 0x3b,0x60,0x46,0x12,0xe6,0x1c,0x0b,0x75,0x65,0x1c,0x05,0xa5,0x15,0x2a,0x5f,0xe0,
+ 0x0f,0x71,0x16,0x1c,0x06,0xa5,0x4d,0x12,0x3d,0x3c,0x4d,0x05,0xd3,0x12,0xa3,0x1c,
+ 0xe3,0x1c,0x12,0x76,0x63,0x1c,0x51,0xf0,0x02,0x03,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0x8a,0x9e,0x00,0x00,0x70,0x05,0x00,0x00,0x76,0x02,0x00,0x00,0x8c,0x82,0x00,0x00,
+ 0x1a,0x04,0x00,0x00,0x40,0x01,0x00,0x00,0x5c,0x05,0x00,0x00,0x66,0x05,0x00,0x00,
+ 0x7a,0x05,0x00,0x00,0x84,0x05,0x00,0x00,0x8e,0x05,0x00,0x00,0x98,0x05,0x00,0x00,
+ 0xa7,0x05,0x00,0x00,0xe4,0xa3,0x00,0x00,0xe5,0xa3,0x00,0x00,0x00,0x03,0x00,0x00,
+ 0x83,0x00,0x00,0x00,0x33,0xa6,0x43,0xaf,0x8f,0x3c,0x6f,0x1e,0x56,0x12,0xf1,0x11,
+ 0xc6,0x03,0xf9,0x11,0xd6,0x1c,0xe6,0x1c,0xc0,0x78,0x86,0x1c,0x36,0xbf,0x8f,0x3e,
+ 0x46,0xbf,0x53,0xa9,0x63,0xaf,0x8f,0x3c,0x9f,0x1e,0x56,0xbf,0x8f,0x3e,0x66,0xbf,
+ 0x56,0x12,0x56,0x1c,0x5f,0x12,0x3f,0x3c,0xf6,0x1c,0x46,0x1c,0xb8,0x79,0x96,0x1c,
+ 0x36,0x3c,0xe6,0x1c,0x36,0xb2,0x46,0xb2,0x56,0xb2,0x66,0xb2,0x76,0xb2,0x86,0xb2,
+ 0x96,0xb2,0x05,0x20,0x45,0x01,0x5b,0x0c,0xd5,0x01,0x75,0x0f,0xd3,0xe7,0x04,0x20,
+ 0xa4,0x2a,0x97,0xe7,0xaf,0x75,0x06,0x60,0x64,0x12,0x2b,0x60,0xae,0x7e,0x3c,0x60,
+ 0x6d,0x12,0x6f,0x64,0xf1,0x11,0x7f,0x03,0xf9,0x11,0x06,0x01,0x57,0x12,0x37,0x21,
+ 0x07,0xa7,0x73,0x12,0x03,0x24,0x43,0x01,0x3b,0x0c,0x3b,0xe8,0x17,0x2a,0x43,0x12,
+ 0x33,0x3c,0x43,0x1c,0xe3,0x1c,0xa4,0x77,0x37,0x1c,0x07,0xb1,0x0a,0xe0,0xa3,0x71,
+ 0x13,0x1c,0x67,0x12,0x37,0x3c,0x67,0x05,0xf7,0x1c,0xe7,0x1c,0x9b,0x72,0x27,0x1c,
+ 0x09,0xf0,0x9e,0x77,0x73,0x1c,0x67,0x12,0x37,0x3c,0x67,0x05,0xf7,0x1c,0xe7,0x1c,
+ 0x96,0x78,0x87,0x1c,0x37,0xaa,0x47,0xa2,0x82,0x3c,0xa2,0x1e,0x53,0xb2,0x82,0x3e,
+ 0x63,0xb2,0x57,0xaa,0x67,0xa2,0x82,0x3c,0xa2,0x1e,0x73,0xb2,0x82,0x3e,0x83,0xb2,
+ 0x77,0xa2,0x87,0xa7,0x87,0x3c,0x27,0x1e,0x93,0xb7,0x87,0x3e,0xa3,0xb7,0xa5,0xa7,
+ 0x05,0xb7,0x07,0x20,0x47,0x01,0xa5,0xb7,0x7c,0x0c,0x01,0xe0,0xa5,0xbd,0x04,0x20,
+ 0x44,0x01,0x06,0x20,0x05,0x20,0xa6,0x2a,0xb8,0xe7,0x89,0x77,0x07,0xb4,0x89,0x7d,
+ 0x0e,0x60,0x84,0x7c,0x6b,0x64,0xe7,0x12,0xc7,0x1c,0x87,0x76,0x76,0x1c,0x06,0xa6,
+ 0x16,0x2a,0x10,0xe0,0x86,0x79,0x97,0x1c,0x07,0xa7,0xe3,0x12,0x33,0x3c,0xe3,0x05,
+ 0xf1,0x11,0xb7,0x03,0xf9,0x11,0x73,0x1c,0xc3,0x1c,0xd2,0x12,0x81,0x71,0x13,0x1c,
+ 0x74,0x60,0x80,0x7f,0x0e,0x20,0x6d,0x20,0xae,0x2a,0xe5,0xe7,0x7f,0x7e,0x0d,0x60,
+ 0x19,0x60,0xd8,0x12,0x74,0x7c,0x7d,0x7b,0xe7,0x12,0xd7,0x25,0x07,0xa7,0x76,0x12,
+ 0x16,0x24,0x46,0x01,0x69,0x0c,0x73,0xe8,0x7a,0x72,0x02,0xaa,0x7a,0x73,0x03,0xa7,
+ 0xd5,0x12,0x35,0x3c,0xd5,0x05,0x64,0x64,0xa6,0x12,0xf1,0x11,0x46,0x03,0xf9,0x11,
+ 0x56,0x1c,0xc6,0x1c,0x65,0x71,0x16,0x1c,0x36,0xa3,0x46,0xa2,0x82,0x3c,0x32,0x1e,
+ 0xf1,0x11,0x47,0x03,0xf9,0x11,0x57,0x1c,0xc7,0x1c,0x17,0x1c,0x37,0xa4,0x47,0xa5,
+ 0x85,0x3c,0x45,0x1e,0x52,0x05,0x56,0xa5,0x66,0xa3,0x83,0x3c,0x53,0x1e,0x57,0xa6,
+ 0x67,0xa7,0x87,0x3c,0x67,0x1e,0x73,0x05,0x72,0x01,0x73,0x01,0x67,0x7f,0x72,0x01,
+ 0x0b,0x87,0x66,0x76,0x76,0x1c,0x06,0xa6,0x66,0x73,0x37,0x1c,0x07,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x27,0x0d,0x03,0xe8,0x27,0x60,0x0e,0xb7,0x04,0xf0,0x0e,0xa7,0x27,0x2a,
+ 0x01,0xe0,0x0e,0xb9,0x0e,0xa7,0x07,0x2a,0x37,0xe0,0xd6,0x12,0x36,0x3c,0xd6,0x05,
+ 0x67,0x64,0xf1,0x11,0xa7,0x03,0xf9,0x11,0x67,0x1c,0xc7,0x1c,0x47,0x74,0x47,0x1c,
+ 0x37,0xa5,0x47,0xa2,0x82,0x3c,0x52,0x1e,0xc6,0x1c,0x56,0x75,0x56,0x1c,0x76,0xa4,
+ 0x86,0xa5,0x85,0x3c,0x45,0x1e,0x52,0x05,0x57,0xa5,0x67,0xa3,0x83,0x3c,0x53,0x1e,
+ 0x96,0xa5,0xa6,0xa7,0x87,0x3c,0x57,0x1e,0x73,0x05,0x72,0x01,0x73,0x01,0x4a,0x7f,
+ 0x72,0x01,0x0b,0x87,0x4a,0x76,0x76,0x1c,0x06,0xa6,0x49,0x71,0x17,0x1c,0x07,0xa7,
+ 0x87,0x3c,0x67,0x1e,0x27,0x0d,0x08,0xe8,0x17,0x60,0x0e,0xb7,0x05,0xf0,0x17,0x2a,
+ 0x02,0xe8,0x47,0x2a,0x01,0xe0,0x0e,0xb8,0x0d,0x20,0x0e,0x20,0xad,0x2a,0x7c,0xe7,
+ 0x42,0x7f,0x3a,0x77,0x07,0x87,0xb0,0x97,0x41,0x72,0x27,0x1c,0x07,0xa7,0x07,0x2a,
+ 0x06,0xe9,0x38,0x77,0x07,0xac,0x36,0x77,0x07,0xa7,0x76,0x12,0x46,0x3c,0x75,0x12,
+ 0x65,0x3c,0x56,0x1c,0x3b,0x7b,0x6b,0x1c,0x3b,0x7a,0x09,0x60,0x78,0x12,0x78,0x1c,
+ 0x37,0x3c,0x78,0x1c,0xc0,0x98,0x06,0x28,0xe0,0x96,0xc8,0x12,0x48,0x3c,0xc7,0x12,
+ 0x67,0x3c,0x78,0x1c,0xd0,0x98,0xa0,0x88,0x08,0xa7,0x17,0x24,0x47,0x01,0x1d,0x60,
+ 0x7d,0x0c,0xcb,0xe8,0x0a,0xa7,0x07,0x2a,0x76,0xe0,0x0b,0xa7,0x07,0x2a,0xc5,0xe8,
+ 0x19,0x7e,0xc0,0x87,0x97,0x1c,0x15,0x71,0x17,0x1c,0x37,0x3c,0xe7,0x1c,0x77,0xa6,
+ 0x87,0xa2,0x82,0x3c,0x62,0x1e,0xb0,0x87,0x28,0x73,0x37,0x1c,0x07,0xa6,0xb0,0x87,
+ 0x27,0x74,0x47,0x1c,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x27,0x0c,0xae,0xe0,0xe0,0x87,
+ 0xb7,0x1c,0xd0,0x88,0x87,0x1c,0x07,0xa7,0x07,0x2a,0xa7,0xe8,0xb0,0x87,0x20,0x71,
+ 0x17,0x1c,0x07,0xa3,0x20,0x7f,0x62,0x01,0xc7,0x12,0xc7,0x1c,0xc6,0x12,0x36,0x3c,
+ 0x67,0x1c,0x97,0x1c,0x02,0x73,0x38,0xf0,0x00,0x03,0x00,0x00,0x83,0x00,0x00,0x00,
+ 0xe6,0xa3,0x00,0x00,0x8a,0x9e,0x00,0x00,0x1c,0x02,0x00,0x00,0x18,0x02,0x00,0x00,
+ 0xa5,0xa0,0x00,0x00,0x00,0xa1,0x00,0x00,0x70,0x05,0x00,0x00,0x5c,0x05,0x00,0x00,
+ 0x02,0x03,0x00,0x00,0x8c,0x82,0x00,0x00,0x18,0xa4,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0xe5,0xa3,0x00,0x00,0xe4,0xa3,0x00,0x00,0xc0,0x0c,0x00,0x00,0xae,0x00,0x00,0x00,
+ 0xaf,0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x34,0x47,0x00,0x00,0xaa,0x00,0x00,0x00,
+ 0xa4,0xa2,0x00,0x00,0x22,0xa4,0x00,0x00,0xac,0x00,0x00,0x00,0xad,0x00,0x00,0x00,
+ 0xab,0x00,0x00,0x00,0x3e,0x84,0x00,0x00,0x37,0x1c,0x37,0x3c,0xe7,0x1c,0x77,0xa6,
+ 0x87,0xa7,0x87,0x3c,0x67,0x1e,0x26,0x12,0xd6,0x0b,0x67,0x0c,0x53,0xe8,0x27,0x0c,
+ 0x4e,0xe0,0x0a,0xbd,0x52,0xf0,0x17,0x2a,0x50,0xe0,0x0b,0xa7,0x07,0x2a,0x4d,0xe8,
+ 0xae,0x77,0xc0,0x8d,0x9d,0x1c,0xad,0x71,0x1d,0x1c,0x3d,0x3c,0x7d,0x1c,0x7d,0xa6,
+ 0x8d,0xa8,0x88,0x3c,0x68,0x1e,0xce,0x12,0xce,0x1c,0xc6,0x12,0x36,0x3c,0x6e,0x1c,
+ 0x9e,0x1c,0x1e,0x1c,0x3e,0x3c,0x7e,0x1c,0x7e,0xa2,0x8e,0xa6,0x86,0x3c,0xb0,0x87,
+ 0xa4,0x73,0x37,0x1c,0x62,0x1e,0x07,0xa3,0xa3,0x7f,0x62,0x01,0x28,0x0c,0x2a,0xe8,
+ 0x3e,0xa7,0x4e,0xa5,0x85,0x3c,0x75,0x1e,0x05,0x3d,0x05,0x3b,0x5e,0xa7,0x6e,0xa6,
+ 0x86,0x3c,0x76,0x1e,0x06,0x3d,0x06,0x3b,0x54,0x12,0xe4,0x01,0x67,0x12,0xe7,0x01,
+ 0x47,0x0d,0x0a,0xe8,0x3d,0xa6,0x4d,0xa7,0x87,0x3c,0x67,0x1e,0x07,0x3d,0x07,0x3b,
+ 0xf1,0x11,0x57,0x03,0xf9,0x11,0x09,0xf0,0x5d,0xa5,0x6d,0xa7,0x87,0x3c,0x57,0x1e,
+ 0x07,0x3d,0x07,0x3b,0xf1,0x11,0x67,0x03,0xf9,0x11,0x07,0x22,0x03,0xe0,0x27,0x60,
+ 0x0a,0xb7,0x03,0xf0,0x47,0x60,0xa0,0x88,0x08,0xb7,0x09,0x20,0xa0,0x88,0x08,0x20,
+ 0xa0,0x98,0x7b,0x20,0x0a,0x20,0xa9,0x2a,0x26,0xe7,0x07,0x60,0x83,0x73,0x74,0x12,
+ 0x76,0x12,0x36,0x1c,0x85,0x75,0x65,0x1c,0x05,0xa5,0x15,0x2a,0x02,0xe8,0x45,0x2a,
+ 0x03,0xe0,0x82,0x79,0x96,0x1c,0x06,0xb4,0x07,0x20,0xa7,0x2a,0xf1,0xe7,0x80,0x77,
+ 0x07,0x87,0x80,0x7b,0xb7,0x1c,0x07,0xa7,0x0d,0x60,0x07,0x2a,0x18,0xe1,0x1b,0xf1,
+ 0xd7,0x12,0x37,0x3c,0xd7,0x1c,0x74,0x7c,0xc7,0x1c,0x7b,0x71,0x17,0x1c,0x07,0xae,
+ 0xc7,0x12,0xe7,0x1c,0x75,0x72,0x27,0x1c,0x07,0xa7,0x17,0x2a,0x07,0xe8,0x47,0x2a,
+ 0x05,0xe8,0x76,0x73,0x03,0xa5,0x76,0x76,0x07,0x60,0xfc,0xf0,0xee,0x1c,0x75,0x76,
+ 0xe6,0x1c,0x07,0x60,0x06,0xd7,0x74,0x74,0x4e,0x1c,0x0e,0xd7,0xf6,0xf0,0x74,0x12,
+ 0x06,0xa3,0x07,0x20,0x86,0x20,0x3e,0x0f,0xed,0xe0,0xd7,0x12,0x37,0x3c,0xd7,0x1c,
+ 0x62,0x75,0x57,0x1c,0x6e,0x76,0x67,0x1c,0x57,0xa6,0x67,0xa5,0x85,0x3c,0x65,0x1e,
+ 0x77,0xa3,0x87,0xa6,0x86,0x3c,0x36,0x1e,0x47,0x12,0x37,0x3c,0x47,0x1c,0x5a,0x78,
+ 0x87,0x1c,0x67,0x79,0x97,0x1c,0x27,0xa4,0x37,0xab,0x8b,0x3c,0x4b,0x1e,0xbc,0x12,
+ 0x6c,0x01,0xa0,0x9c,0x47,0xa4,0x57,0xa9,0x89,0x3c,0x49,0x1e,0x98,0x12,0x68,0x01,
+ 0xb0,0x98,0x75,0x01,0xc0,0x95,0x8c,0x62,0x0c,0x1c,0x0c,0xc8,0x78,0x01,0xd0,0x98,
+ 0x5a,0x12,0x8a,0x05,0xea,0x01,0x6a,0x01,0x76,0x01,0xe0,0x96,0xcc,0x62,0x0c,0x1c,
+ 0x0c,0xc8,0x78,0x01,0xf0,0x98,0x86,0x05,0x68,0x12,0xe8,0x01,0x68,0x01,0xa2,0x12,
+ 0x72,0x01,0x83,0x12,0x73,0x01,0x53,0x7f,0x2c,0x12,0x49,0x77,0x07,0x87,0x49,0x76,
+ 0x76,0x1c,0x06,0xa6,0x26,0x2a,0x33,0xe0,0x50,0x76,0x76,0x1c,0x06,0xa6,0x4f,0x71,
+ 0x17,0x1c,0x07,0xa3,0x83,0x3c,0x63,0x1e,0x82,0x32,0x3c,0x0c,0x08,0xe0,0xc2,0x12,
+ 0x82,0x3c,0x4b,0x7f,0x62,0x01,0xf7,0x60,0x27,0x0c,0x01,0xe8,0x02,0x61,0xe7,0x12,
+ 0xe7,0x1c,0x41,0x76,0x76,0x1c,0x06,0xc6,0x6c,0x0c,0x04,0xe0,0x3e,0x72,0x27,0x1c,
+ 0x07,0xc2,0x03,0xf0,0x3c,0x73,0x37,0x1c,0x07,0xd2,0xe7,0x12,0xe7,0x1c,0x3a,0x74,
+ 0x47,0x1c,0x07,0xdc,0x87,0x32,0x72,0x0c,0x72,0x02,0xf1,0x11,0x2a,0x03,0xf9,0x11,
+ 0x8a,0x3e,0x6a,0x01,0xf1,0x11,0x28,0x03,0xf9,0x11,0x88,0x3e,0x1e,0xf0,0x16,0x2a,
+ 0x1d,0xe0,0x35,0x76,0x76,0x1c,0x06,0xa6,0x35,0x75,0x57,0x1c,0x07,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x75,0x12,0x35,0x3e,0x46,0x60,0x52,0x0c,0x0c,0xe8,0x75,0x12,0x25,0x3e,
+ 0x36,0x60,0x52,0x0c,0x07,0xe8,0x75,0x12,0x15,0x3e,0x26,0x60,0x52,0x0c,0x02,0xe8,
+ 0x72,0x0c,0x36,0x00,0x6a,0x1a,0x6a,0x01,0x68,0x1a,0x68,0x01,0x17,0x76,0x6e,0x1c,
+ 0x1b,0x77,0x7e,0x1c,0x0e,0xa7,0x17,0x2a,0x12,0xe8,0xc0,0x8c,0xd0,0x81,0xc1,0x0d,
+ 0x02,0xe8,0xab,0x1c,0x01,0xf0,0xab,0x05,0x6b,0x01,0xa0,0x9b,0xe0,0x8b,0xf0,0x8c,
+ 0xbc,0x0d,0x02,0xe8,0x89,0x1c,0x01,0xf0,0x89,0x05,0x69,0x01,0xb0,0x99,0xd7,0x12,
+ 0x37,0x3c,0xd7,0x1c,0x09,0x71,0x17,0x1c,0x15,0x72,0x27,0x1c,0x83,0x62,0x03,0x1c,
+ 0x03,0xcb,0x57,0xbb,0x8b,0x3e,0x67,0xbb,0xc4,0x62,0x04,0x1c,0x04,0xc9,0x77,0xb9,
+ 0x89,0x3e,0x87,0xb9,0x2a,0xf0,0x00,0x00,0x8a,0x9e,0x00,0x00,0x83,0x00,0x00,0x00,
+ 0xab,0x00,0x00,0x00,0x3e,0x84,0x00,0x00,0x70,0x05,0x00,0x00,0x98,0x05,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0xa3,0x00,0x00,0x00,0x1c,0x02,0x00,0x00,0x4a,0xa0,0x00,0x00,
+ 0x4b,0xa0,0x00,0x00,0x62,0xa6,0x00,0x00,0x76,0xa6,0x00,0x00,0x18,0x02,0x00,0x00,
+ 0xc0,0x01,0x00,0x00,0xc0,0x0c,0x00,0x00,0xa4,0x00,0x00,0x00,0xa5,0x00,0x00,0x00,
+ 0xd0,0x83,0x00,0x00,0x07,0x01,0x51,0x0c,0x0a,0xef,0x0d,0x20,0x4d,0x01,0xa9,0x75,
+ 0x05,0xa7,0x7d,0x0c,0xe5,0xee,0xa8,0x77,0x07,0x87,0xa8,0x76,0x67,0x1c,0x07,0xa5,
+ 0x16,0x60,0x56,0x0c,0x92,0xe0,0x56,0x60,0x56,0x0c,0x01,0xe0,0x07,0xb6,0x0c,0x60,
+ 0xa4,0x78,0xa4,0x7a,0xa5,0x79,0x85,0xf0,0xc3,0x12,0x33,0x3c,0xc3,0x1c,0x83,0x1c,
+ 0xa3,0x77,0x37,0x1c,0x07,0xad,0x87,0x12,0xd7,0x1c,0xa1,0x76,0x76,0x1c,0x06,0xa6,
+ 0x16,0x2a,0x21,0xe0,0xa7,0x12,0xd7,0x1c,0x06,0x60,0x07,0xb6,0x0e,0x60,0x3b,0x12,
+ 0x9b,0x77,0x7b,0x1c,0x10,0xf0,0xe2,0x12,0x32,0x3c,0xe2,0x1c,0xd7,0x12,0xd7,0x1c,
+ 0xd7,0x1c,0x76,0x12,0x46,0x3c,0x76,0x05,0x62,0x1c,0x92,0x1c,0xb3,0x12,0x94,0x60,
+ 0x95,0x7f,0x0e,0x20,0x4e,0x01,0x8c,0x71,0x01,0x87,0x8c,0x72,0x27,0x1c,0x07,0xa7,
+ 0x7e,0x0c,0xe9,0xef,0x54,0xf0,0x46,0x2a,0x52,0xe8,0x8f,0x74,0x47,0x1c,0x07,0xa7,
+ 0x17,0x2a,0x4d,0xe8,0xa7,0x12,0xd7,0x1c,0x07,0xa7,0xa0,0x97,0x72,0x12,0x32,0x3c,
+ 0x72,0x1c,0xd7,0x12,0xd7,0x1c,0xd7,0x1c,0x7e,0x12,0x4e,0x3c,0x7e,0x05,0xe2,0x1c,
+ 0x92,0x1c,0x82,0x75,0x53,0x1c,0x94,0x60,0x83,0x7f,0x7b,0x76,0x06,0x87,0x7b,0x7b,
+ 0xb7,0x1c,0x07,0xab,0x06,0x60,0xb0,0x96,0x62,0x12,0x64,0x12,0x10,0xf0,0x67,0x12,
+ 0x37,0x3c,0x67,0x1c,0xe7,0x1c,0x97,0x1c,0x17,0xa3,0x27,0xa5,0x85,0x3c,0x35,0x1e,
+ 0x52,0x1c,0x37,0xa5,0x47,0xa7,0x87,0x3c,0x57,0x1e,0x74,0x1c,0x06,0x20,0x06,0x01,
+ 0xb1,0x0c,0xed,0xef,0xb0,0x94,0xce,0x12,0x3e,0x3c,0xce,0x1c,0x8e,0x1c,0x73,0x71,
+ 0x1e,0x1c,0xb3,0x12,0x73,0x7f,0x62,0x01,0x5e,0xb2,0x82,0x3e,0x6e,0xb2,0xb0,0x82,
+ 0xb3,0x12,0x6f,0x7f,0x62,0x01,0x7e,0xb2,0x82,0x3e,0x8e,0xb2,0xa0,0x87,0x07,0x20,
+ 0x47,0x01,0xad,0x1c,0x0d,0xb7,0xb7,0x0c,0x02,0xe8,0x02,0x60,0x0d,0xb2,0x0c,0x20,
+ 0x4c,0x01,0x5c,0x73,0x03,0xa7,0x7c,0x0c,0x77,0xef,0x5b,0x77,0x07,0x87,0x65,0x74,
+ 0x47,0x1c,0x07,0xa7,0x07,0x2a,0x02,0xe9,0x5a,0x7a,0x56,0x77,0x07,0xa1,0x11,0x2a,
+ 0xfd,0xe0,0x61,0x77,0x07,0xa9,0x61,0x76,0x62,0x77,0x07,0xa7,0x62,0x75,0x05,0xab,
+ 0x8b,0x3c,0x7b,0x1e,0x61,0x77,0x07,0xa7,0x61,0x75,0x05,0xac,0x8c,0x3c,0x7c,0x1e,
+ 0x60,0x77,0x07,0xa7,0x60,0x75,0x05,0xad,0x8d,0x3c,0x7d,0x1e,0x5f,0x77,0x07,0xa7,
+ 0x5f,0x75,0x05,0xae,0x8e,0x3c,0x7e,0x1e,0xa6,0xa5,0xb6,0xa7,0x87,0x3c,0x57,0x1e,
+ 0x5c,0x75,0x75,0x1c,0xa0,0x95,0x5b,0x78,0x87,0x1c,0x67,0x01,0xb0,0x97,0xc6,0xa5,
+ 0xd6,0xa7,0x87,0x3c,0x57,0x1e,0x56,0x72,0x72,0x1c,0xc0,0x92,0x87,0x1c,0x67,0x01,
+ 0xd0,0x97,0x55,0x77,0x07,0xa7,0x55,0x76,0x06,0xa3,0x83,0x3c,0x73,0x1e,0x54,0x77,
+ 0x07,0xa7,0x54,0x76,0x06,0xa4,0x84,0x3c,0x74,0x1e,0x53,0x77,0x07,0xa8,0x67,0x64,
+ 0xf1,0x11,0x78,0x03,0xf9,0x11,0xa2,0x12,0x82,0x1c,0x50,0x75,0x52,0x1c,0x06,0x60,
+ 0xf1,0x11,0x79,0x03,0xf9,0x11,0x02,0xa7,0x27,0x2a,0xa2,0xe0,0x67,0x12,0xa7,0x1c,
+ 0x34,0x75,0x75,0x1c,0x05,0xa5,0x15,0x24,0x45,0x01,0x51,0x0c,0x99,0xe8,0x48,0x75,
+ 0x57,0x1c,0x07,0xa7,0x07,0x24,0x47,0x01,0x71,0x0c,0x92,0xe8,0x67,0x12,0x37,0x3c,
+ 0x67,0x05,0x87,0x1c,0xa7,0x1c,0x43,0x75,0x57,0x1c,0x37,0xaf,0x47,0xa5,0x85,0x3c,
+ 0xf5,0x1e,0xb5,0x0c,0x0a,0xe8,0x5c,0x0c,0x08,0xe8,0x57,0xaf,0x67,0xa7,0x87,0x3c,
+ 0xf7,0x1e,0xd7,0x0c,0x02,0xe8,0x7e,0x0c,0x7b,0xe0,0x55,0x1c,0x6f,0x12,0x3f,0x3c,
+ 0x6f,0x05,0xf7,0x12,0x97,0x1c,0xa7,0x1c,0x37,0x73,0x37,0x1c,0x37,0xa3,0x47,0xa4,
+ 0x84,0x3c,0x34,0x1e,0x45,0x05,0x65,0x01,0x8f,0x1c,0xaf,0x1c,0x32,0x74,0x4f,0x1c,
+ 0x5f,0xa3,0x6f,0xa4,0x84,0x3c,0x34,0x1e,0x44,0x1c,0x57,0xa3,0x67,0xa7,0x87,0x3c,
+ 0x37,0x1e,0x74,0x05,0x64,0x01,0x57,0x12,0x77,0x01,0xf3,0x67,0x73,0x0d,0x04,0xe8,
+ 0xa0,0x83,0x37,0x0d,0x03,0xe8,0x03,0xf0,0x75,0x32,0x01,0xf0,0xb0,0x85,0x47,0x12,
+ 0x77,0x01,0xf3,0x67,0x73,0x0d,0x04,0xe8,0xc0,0x83,0x37,0x0d,0x45,0xe8,0x45,0xf0,
+ 0x74,0x32,0x43,0xf0,0xa5,0xa0,0x00,0x00,0x1c,0x9e,0x00,0x00,0x21,0x01,0x00,0x00,
+ 0x8a,0x9e,0x00,0x00,0x8c,0xa6,0x00,0x00,0x96,0xa6,0x00,0x00,0x1c,0x02,0x00,0x00,
+ 0x70,0x05,0x00,0x00,0x8c,0x82,0x00,0x00,0x98,0x05,0x00,0x00,0x18,0x02,0x00,0x00,
+ 0x3e,0x84,0x00,0x00,0xa6,0x00,0x00,0x00,0xe4,0xa3,0x00,0x00,0x20,0x9e,0x00,0x00,
+ 0x36,0x9e,0x00,0x00,0x37,0x9e,0x00,0x00,0x3a,0x9e,0x00,0x00,0x3b,0x9e,0x00,0x00,
+ 0x38,0x9e,0x00,0x00,0x39,0x9e,0x00,0x00,0x3c,0x9e,0x00,0x00,0x3d,0x9e,0x00,0x00,
+ 0x81,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0xa7,0xa0,0x00,0x00,0xa8,0xa0,0x00,0x00,
+ 0xa9,0xa0,0x00,0x00,0xaa,0xa0,0x00,0x00,0xe5,0xa3,0x00,0x00,0x02,0x03,0x00,0x00,
+ 0x8e,0x05,0x00,0x00,0x00,0x03,0x00,0x00,0xd0,0x84,0x53,0x12,0x63,0x01,0x64,0x01,
+ 0x06,0x20,0x62,0x20,0xa6,0x2a,0x57,0xe7,0x9f,0x77,0x07,0xb3,0x83,0x3e,0x9e,0x77,
+ 0x07,0xb3,0x9e,0x77,0x07,0xb4,0x84,0x3e,0x9e,0x77,0x07,0xb4,0x9e,0x77,0x07,0xa6,
+ 0x9e,0x77,0x07,0xa5,0x17,0x60,0x57,0x0c,0x9d,0x77,0x07,0x87,0x03,0xe0,0x9c,0x74,
+ 0x47,0x1c,0x02,0xf0,0x9c,0x75,0x57,0x1c,0x07,0xab,0x1b,0x3c,0x0a,0x60,0xac,0x12,
+ 0x9a,0x7d,0x68,0x64,0xf1,0x11,0x68,0x03,0xf9,0x11,0xb9,0x12,0x8a,0xf0,0xc7,0x12,
+ 0x37,0x3c,0xc7,0x1c,0xd7,0x1c,0x95,0x76,0x67,0x1c,0x07,0xab,0xd7,0x12,0xb7,0x1c,
+ 0x94,0x76,0x76,0x1c,0x06,0xa6,0x26,0x2a,0x74,0xe0,0x92,0x77,0x07,0xa5,0x92,0x77,
+ 0x06,0x60,0x2e,0xf0,0x74,0x12,0x87,0x20,0x04,0xa4,0xb4,0x0f,0x27,0xe0,0xb7,0x12,
+ 0x37,0x3c,0xb7,0x05,0x87,0x1c,0xd7,0x1c,0x8d,0x71,0x17,0x1c,0x37,0xa5,0x47,0xa2,
+ 0x82,0x3c,0x52,0x1e,0x6e,0x12,0x3e,0x3c,0x6e,0x1c,0xde,0x1c,0x89,0x73,0x3e,0x1c,
+ 0x2e,0xa5,0x3e,0xa6,0x86,0x3c,0x56,0x1e,0x62,0x05,0x57,0xa6,0x67,0xa3,0x83,0x3c,
+ 0x63,0x1e,0x4e,0xa6,0x5e,0xa7,0x87,0x3c,0x67,0x1e,0x73,0x05,0x72,0x01,0x73,0x01,
+ 0x81,0x7f,0x92,0x0c,0xc7,0x12,0x37,0x3c,0x06,0xe8,0x3a,0xf0,0x06,0x20,0x46,0x01,
+ 0x56,0x0f,0xd0,0xe7,0x44,0xf0,0xc7,0x1c,0xd7,0x1c,0x74,0x76,0x76,0x1c,0x06,0xa6,
+ 0xd6,0x1c,0x79,0x74,0x46,0x1c,0x06,0xa4,0x6d,0x75,0x05,0x85,0x78,0x73,0x53,0x1c,
+ 0x03,0xa3,0x34,0x0c,0x77,0x71,0x15,0x1c,0x08,0xe0,0x04,0x20,0x06,0xb4,0x05,0xa6,
+ 0x26,0x2a,0x2d,0xe0,0x74,0x72,0x27,0x1c,0x0c,0xf0,0x0a,0x20,0x4a,0x01,0xdb,0x1c,
+ 0x68,0x73,0x3b,0x1c,0x36,0x60,0x0b,0xb6,0x05,0xa6,0x06,0x2a,0x20,0xe8,0x6d,0x74,
+ 0x47,0x1c,0x2e,0xa5,0x3e,0xa6,0x86,0x3c,0x56,0x1e,0x57,0xb6,0x86,0x3e,0x67,0xb6,
+ 0x4e,0xa5,0x5e,0xa6,0x86,0x3c,0x56,0x1e,0x77,0xb6,0x86,0x3e,0x87,0xb6,0x0f,0xf0,
+ 0xc7,0x1c,0xd7,0x1c,0x5a,0x75,0x57,0x1c,0x07,0xa7,0xd7,0x1c,0x5f,0x76,0x67,0x1c,
+ 0x04,0xf0,0x16,0x2a,0x04,0xe0,0x5c,0x7b,0xb7,0x1c,0x06,0x60,0x07,0xb6,0x0c,0x20,
+ 0x4c,0x01,0x4d,0x71,0x01,0xa7,0x7c,0x0c,0x72,0xef,0x0a,0x2a,0x08,0xe8,0x5a,0x77,
+ 0x07,0xa6,0x5a,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x7a,0x0c,0x2a,0x00,0x58,0x77,
+ 0x07,0xba,0x46,0x77,0x07,0x87,0x57,0x76,0x76,0x1c,0x06,0xa6,0x06,0x2a,0x05,0xe0,
+ 0x56,0x77,0x07,0xa6,0xf2,0x67,0x26,0x16,0xd4,0xf0,0x4f,0x76,0x06,0xa6,0x4f,0x75,
+ 0x05,0xa5,0x85,0x3c,0x65,0x1e,0x05,0x01,0x04,0x60,0x3f,0x7c,0x50,0x7d,0x7d,0x1c,
+ 0x50,0x7e,0x7e,0x1c,0x50,0x73,0x73,0x1c,0x3f,0x12,0x4f,0x72,0x72,0x1c,0x16,0xf0,
+ 0x86,0x65,0x46,0x1c,0x66,0x1c,0xc6,0x1c,0x86,0xa3,0x96,0xa6,0x0d,0xab,0xb3,0x0c,
+ 0x0b,0xe8,0x0e,0xab,0x3b,0x0c,0x08,0xe8,0x0f,0xa3,0x36,0x0c,0x05,0xe8,0x02,0xa3,
+ 0x63,0x0c,0x02,0xe8,0x01,0x24,0x41,0x01,0x04,0x20,0x44,0x01,0x54,0x0c,0xe8,0xef,
+ 0x15,0x60,0x15,0x0c,0x33,0xe8,0x3d,0x76,0x76,0x1c,0x06,0xa4,0x3d,0x76,0x76,0x1c,
+ 0x06,0xac,0x05,0x60,0x3c,0x7d,0x7d,0x1c,0x3c,0x76,0x76,0x1c,0x6f,0x12,0x3b,0x7e,
+ 0xa2,0x61,0x16,0xf0,0x0d,0xa3,0x0f,0xaa,0x4b,0x12,0xf1,0x11,0x2b,0x03,0xf9,0x11,
+ 0x0b,0xf0,0xb6,0x12,0x36,0x1c,0x66,0x1c,0xe6,0x1c,0x06,0xc6,0x76,0x01,0x06,0x22,
+ 0x01,0xe0,0x65,0x1c,0x03,0x20,0x43,0x01,0x3a,0x0c,0xf3,0xe7,0x04,0x20,0x44,0x01,
+ 0x4c,0x0c,0xe8,0xe7,0x11,0x2a,0x05,0xe0,0x66,0x67,0x76,0x1c,0x06,0xa6,0x66,0x1c,
+ 0x03,0xf0,0x2b,0x76,0x76,0x1c,0x06,0xa6,0x56,0x0c,0x35,0x00,0x2a,0x76,0x06,0xa6,
+ 0x76,0x36,0x29,0x76,0x59,0xe8,0x05,0x2a,0x02,0xe8,0x07,0x60,0x53,0xf0,0x06,0xa5,
+ 0x27,0x78,0x87,0x1c,0x07,0xa7,0x57,0x0c,0x4b,0xe0,0x1b,0x77,0x07,0xa6,0xf9,0x67,
+ 0x96,0x16,0x56,0xf0,0xa7,0xa0,0x00,0x00,0xa8,0xa0,0x00,0x00,0xa9,0xa0,0x00,0x00,
+ 0xaa,0xa0,0x00,0x00,0xe5,0xa3,0x00,0x00,0xa5,0xa0,0x00,0x00,0x1c,0x9e,0x00,0x00,
+ 0xb3,0x00,0x00,0x00,0xb2,0x00,0x00,0x00,0x8a,0x9e,0x00,0x00,0x1c,0x02,0x00,0x00,
+ 0x70,0x05,0x00,0x00,0x4a,0xa0,0x00,0x00,0x4b,0xa0,0x00,0x00,0x00,0x03,0x00,0x00,
+ 0xc0,0x01,0x00,0x00,0xc0,0x0c,0x00,0x00,0xa7,0x05,0x00,0x00,0xb1,0x00,0x00,0x00,
+ 0xb0,0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x40,0x9f,0x00,0x00,0x41,0x9f,0x00,0x00,
+ 0x3b,0xa4,0x00,0x00,0x11,0x01,0x00,0x00,0x2c,0xa4,0x00,0x00,0x12,0x01,0x00,0x00,
+ 0x13,0x01,0x00,0x00,0x14,0x01,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0xe0,0x02,0x00,
+ 0x17,0x01,0x00,0x00,0x2d,0xa4,0x00,0x00,0x58,0xa8,0x00,0x00,0x18,0x01,0x00,0x00,
+ 0x57,0x12,0x07,0x20,0x06,0xb7,0x0f,0xf0,0x05,0x2a,0x0c,0xe8,0x06,0xa5,0xa4,0x7b,
+ 0xb7,0x1c,0x07,0xa7,0x57,0x0c,0xf4,0xe7,0xa3,0x77,0x07,0xa6,0xa3,0x7c,0xc6,0x1e,
+ 0x07,0xb6,0x01,0xf0,0x06,0xb5,0x9f,0x77,0x07,0xa7,0x77,0x36,0x03,0xe8,0x16,0x60,
+ 0x9f,0x77,0x07,0xb6,0x9f,0x7e,0x0e,0xa7,0x07,0x2a,0x42,0xe8,0x9e,0x7f,0x9e,0x77,
+ 0x07,0xa2,0x17,0xa3,0x9e,0x74,0x85,0x61,0x9e,0x7f,0x9e,0x77,0x07,0xb2,0x12,0x01,
+ 0x9e,0x77,0x07,0xb1,0x22,0x01,0x9d,0x77,0x07,0xb1,0x82,0x3f,0x9d,0x77,0x07,0xb2,
+ 0x9d,0x77,0x07,0xa6,0x9d,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x9c,0x76,0x06,0xa6,
+ 0x06,0x3d,0x76,0x1e,0x9b,0x77,0x07,0xa7,0x87,0x3d,0x67,0x1e,0x07,0x2a,0x1a,0xe0,
+ 0x99,0x77,0x07,0xa6,0x99,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x98,0x76,0x06,0xa6,
+ 0x06,0x3d,0x76,0x1e,0x97,0x77,0x07,0xa7,0x87,0x3d,0x67,0x1e,0x07,0x2a,0x0a,0xe0,
+ 0x95,0x75,0x05,0xa6,0x06,0x20,0x46,0x01,0x05,0xb6,0x25,0x60,0x65,0x0c,0x05,0xe0,
+ 0x0e,0xb7,0x03,0xf0,0x06,0x60,0x8f,0x77,0x07,0xb6,0x16,0x60,0x7c,0x77,0x07,0xb6,
+ 0x8e,0x76,0x06,0x87,0x07,0x20,0x06,0x97,0x8d,0x7e,0x0e,0xa7,0x07,0x2a,0x7f,0xe0,
+ 0x81,0x77,0x07,0xa6,0x81,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x80,0x76,0x06,0xa6,
+ 0x06,0x3d,0x76,0x1e,0x7f,0x77,0x07,0xa7,0x87,0x3d,0x67,0x1e,0x07,0x2a,0x10,0xe0,
+ 0x7d,0x77,0x07,0xa7,0x7d,0x76,0x06,0xa6,0x86,0x3c,0x76,0x1e,0x7c,0x77,0x07,0xa7,
+ 0x07,0x3d,0x67,0x1e,0x7b,0x76,0x06,0xad,0x8d,0x3d,0x7d,0x1e,0x0d,0x2a,0x05,0xe8,
+ 0x69,0x7f,0x06,0x60,0x7a,0x77,0x17,0xb6,0x23,0xf3,0x1e,0xa7,0x26,0x60,0x76,0x0c,
+ 0x04,0xe8,0x07,0x20,0x1e,0xb7,0x63,0x7f,0x1b,0xf3,0x56,0x60,0x76,0x0c,0x0f,0xe8,
+ 0x07,0x20,0x1e,0xb7,0x17,0x60,0xae,0xb7,0xbe,0xbd,0xce,0xbd,0x71,0x77,0x07,0x87,
+ 0x71,0x71,0x17,0x1c,0x07,0xa7,0xde,0xb7,0x07,0x60,0xee,0xb7,0x09,0xf3,0x5a,0x77,
+ 0x07,0xa2,0x17,0xa3,0x5a,0x74,0x85,0x61,0x5a,0x7f,0x86,0x2c,0x26,0x16,0x59,0x77,
+ 0x07,0xb6,0x12,0x01,0x17,0x12,0x58,0x75,0x05,0xb1,0x22,0x01,0x58,0x75,0x05,0xb1,
+ 0x82,0x3f,0x57,0x75,0x05,0xb2,0x64,0x75,0x05,0xb6,0x64,0x75,0x05,0xb7,0x64,0x75,
+ 0x05,0xb1,0x64,0x75,0x05,0xb2,0x64,0x75,0x05,0xb6,0x64,0x76,0x06,0xb7,0x64,0x77,
+ 0x07,0xb1,0x64,0x77,0x07,0xb2,0xae,0xbd,0x64,0x76,0x06,0xa6,0x64,0x77,0x07,0xa7,
+ 0x87,0x3c,0x67,0x1e,0x63,0x76,0x06,0xa6,0x06,0x3d,0x76,0x1e,0x62,0x77,0x07,0xa7,
+ 0x87,0x3d,0x67,0x1e,0x4e,0xb7,0x17,0x01,0x5e,0xb1,0x27,0x01,0x6e,0xb1,0x87,0x3f,
+ 0x7e,0xb7,0x1e,0xbd,0x17,0x60,0x0e,0xb7,0x5c,0x77,0x67,0xbd,0xc9,0xf2,0x4c,0x76,
+ 0x06,0x8d,0x5a,0x76,0xd6,0x1c,0x06,0xa6,0x06,0x2a,0x03,0xe0,0x27,0x60,0x0e,0xb7,
+ 0x31,0xf3,0x16,0x2a,0xdd,0xe1,0x17,0x2a,0xb4,0xe2,0x07,0x60,0xae,0xb7,0x54,0x77,
+ 0xd7,0x1c,0x07,0xa6,0x06,0x2a,0x50,0x77,0x03,0xe0,0x26,0x60,0x67,0xb6,0xb1,0xf1,
+ 0x16,0x2a,0x0a,0xe1,0x67,0xa6,0x06,0x2a,0xec,0xe0,0x4e,0x77,0x07,0xa7,0x07,0x2a,
+ 0x07,0xe0,0x4d,0x77,0x07,0xa7,0x17,0x2a,0x3c,0xe0,0x4c,0x76,0x06,0xb7,0x3c,0xf0,
+ 0x17,0x2a,0x37,0xe0,0x49,0x77,0x07,0xa7,0x17,0x2a,0x36,0xe8,0x49,0x77,0x07,0xa6,
+ 0x06,0x2a,0x2f,0xe0,0x46,0x77,0x07,0xa5,0x15,0x2a,0x2d,0xe0,0x46,0x77,0x07,0xa7,
+ 0x66,0x64,0xf1,0x11,0x67,0x03,0xf9,0x11,0x44,0x76,0x67,0x1c,0x44,0x72,0x27,0x1c,
+ 0x37,0xa6,0x47,0xa2,0x82,0x3c,0x62,0x1e,0x42,0x76,0x06,0xa5,0x42,0x76,0x06,0xa6,
+ 0x86,0x3c,0x56,0x1e,0x62,0x05,0x57,0xa6,0x67,0xa3,0x83,0x3c,0x63,0x1e,0x3e,0x77,
+ 0x07,0xa6,0x3e,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x73,0x05,0x72,0x01,0x73,0x01,
+ 0x3c,0x7f,0x72,0x01,0x97,0x32,0x27,0x0d,0x07,0xe8,0x26,0x60,0x2b,0x77,0x67,0xb6,
+ 0x03,0xf0,0x06,0x60,0x2e,0x77,0x07,0xb6,0x1a,0x77,0x07,0x85,0x36,0x77,0x6c,0xf0,
+ 0x18,0x01,0x00,0x00,0x2c,0xa4,0x00,0x00,0x80,0xff,0xff,0xff,0x62,0xa4,0x00,0x00,
+ 0x31,0xaa,0x00,0x00,0xac,0x2c,0x00,0x00,0x20,0x9e,0x00,0x00,0x00,0x80,0x02,0x00,
+ 0x58,0x3e,0x00,0x00,0x65,0xa4,0x00,0x00,0x66,0xa4,0x00,0x00,0x67,0xa4,0x00,0x00,
+ 0x68,0xa4,0x00,0x00,0x46,0xa4,0x00,0x00,0x47,0xa4,0x00,0x00,0x48,0xa4,0x00,0x00,
+ 0x49,0xa4,0x00,0x00,0x4a,0xa4,0x00,0x00,0x4b,0xa4,0x00,0x00,0x4c,0xa4,0x00,0x00,
+ 0x4d,0xa4,0x00,0x00,0x59,0xa8,0x00,0x00,0x5c,0xa8,0x00,0x00,0x66,0x9e,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0xba,0x00,0x00,0x00,0x69,0xa4,0x00,0x00,0x6a,0xa4,0x00,0x00,
+ 0x6b,0xa4,0x00,0x00,0x6c,0xa4,0x00,0x00,0x6d,0xa4,0x00,0x00,0x6e,0xa4,0x00,0x00,
+ 0x6f,0xa4,0x00,0x00,0x70,0xa4,0x00,0x00,0x37,0xaa,0x00,0x00,0x38,0xaa,0x00,0x00,
+ 0x39,0xaa,0x00,0x00,0x3a,0xaa,0x00,0x00,0x20,0xaa,0x00,0x00,0xb4,0x00,0x00,0x00,
+ 0xbc,0x00,0x00,0x00,0x4a,0xa0,0x00,0x00,0xa5,0xa0,0x00,0x00,0x60,0xa8,0x00,0x00,
+ 0xef,0x9f,0x00,0x00,0xe4,0xa3,0x00,0x00,0x8a,0x9e,0x00,0x00,0x00,0x03,0x00,0x00,
+ 0x01,0xa1,0x00,0x00,0x02,0xa1,0x00,0x00,0x03,0xa1,0x00,0x00,0x04,0xa1,0x00,0x00,
+ 0xc0,0x0c,0x00,0x00,0x42,0xa4,0x00,0x00,0x07,0xa6,0xb7,0x77,0x07,0xa7,0x87,0x3c,
+ 0x67,0x1e,0x56,0x67,0x56,0x1c,0x06,0xa6,0xb5,0x73,0x35,0x1c,0x05,0xa5,0x56,0x1b,
+ 0x76,0x0d,0xe7,0xe8,0xb3,0x77,0x07,0xa7,0xb3,0x76,0x06,0xa6,0x86,0x3c,0x76,0x1e,
+ 0xb2,0x77,0x07,0xa7,0x07,0x3d,0x67,0x1e,0xb1,0x76,0x06,0xa6,0x86,0x3d,0x76,0x1e,
+ 0xb0,0x77,0x07,0xa5,0xb0,0x77,0x07,0xa7,0x87,0x3c,0x57,0x1e,0xaf,0x75,0x05,0xa5,
+ 0x05,0x3d,0x75,0x1e,0xae,0x77,0x07,0xa7,0x87,0x3d,0x57,0x1e,0x67,0x0c,0x06,0xe0,
+ 0x06,0x60,0xab,0x77,0x17,0xb6,0x16,0x60,0xab,0x77,0x67,0xb6,0x06,0x60,0xaa,0x77,
+ 0x19,0xf0,0x67,0xa7,0x17,0x2a,0xbd,0xe0,0x1e,0xa6,0x06,0x20,0x46,0x01,0x1e,0xb6,
+ 0xa7,0x74,0x4d,0x1c,0x0d,0xa7,0x17,0x3e,0x67,0x0c,0x04,0xe8,0xa5,0x77,0x07,0xa7,
+ 0x07,0x2a,0x06,0xe8,0x16,0x60,0xa3,0x77,0x07,0xb6,0x06,0x60,0x9e,0x77,0x67,0xb6,
+ 0x16,0x60,0xa1,0x77,0x07,0xb6,0xa5,0xf0,0x26,0x2a,0xa3,0xe0,0x67,0xa6,0x06,0x2a,
+ 0x34,0xe0,0x9e,0x77,0x07,0xa6,0x8c,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x56,0x67,
+ 0xd6,0x1c,0x06,0xa6,0x8a,0x75,0x5d,0x1c,0x0d,0xa5,0x56,0x1b,0x76,0x0d,0x65,0xe8,
+ 0x88,0x77,0x07,0xa7,0x88,0x76,0x06,0xa6,0x86,0x3c,0x76,0x1e,0x87,0x77,0x07,0xa7,
+ 0x07,0x3d,0x67,0x1e,0x86,0x76,0x06,0xa6,0x86,0x3d,0x76,0x1e,0x85,0x77,0x07,0xa5,
+ 0x85,0x77,0x07,0xa7,0x87,0x3c,0x57,0x1e,0x84,0x75,0x05,0xa5,0x05,0x3d,0x75,0x1e,
+ 0x83,0x77,0x07,0xa7,0x87,0x3d,0x57,0x1e,0x67,0x0c,0x47,0xe0,0x06,0x60,0x80,0x77,
+ 0x17,0xb6,0x16,0x60,0x80,0x77,0x67,0xb6,0x40,0xf0,0x67,0xa7,0x17,0x2a,0x3d,0xe0,
+ 0x1e,0xa6,0x06,0x20,0x46,0x01,0x1e,0xb6,0x7d,0x77,0xd7,0x1c,0x07,0xa7,0x17,0x3e,
+ 0x67,0x0c,0x04,0xe8,0x7b,0x77,0x07,0xa7,0x07,0x2a,0x2c,0xe8,0x76,0x73,0x16,0x60,
+ 0x79,0x77,0x07,0xb6,0x7b,0x76,0x06,0xa6,0x7b,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,
+ 0x7a,0x76,0x06,0xa6,0x06,0x3d,0x76,0x1e,0x79,0x77,0x07,0xa7,0x87,0x3d,0x67,0x1e,
+ 0x6c,0x74,0x44,0xa5,0x54,0xa6,0x86,0x3c,0x56,0x1e,0x64,0xa5,0x05,0x3d,0x65,0x1e,
+ 0x74,0xa6,0x86,0x3d,0x56,0x1e,0x67,0x05,0x72,0x76,0xd6,0x1c,0x06,0xa5,0x71,0x76,
+ 0x6d,0x1c,0x0d,0xa6,0x86,0x3c,0x56,0x1e,0x76,0x0c,0x02,0xe0,0x27,0x60,0x01,0xf0,
+ 0x07,0x60,0x63,0xb7,0x06,0x60,0x60,0x77,0x07,0xb6,0x5e,0x73,0x63,0xa7,0x07,0x2a,
+ 0x28,0xe0,0x63,0x76,0x06,0xa6,0x63,0x77,0x07,0xa7,0x87,0x3c,0x67,0x1e,0x62,0x76,
+ 0x06,0xa6,0x06,0x3d,0x76,0x1e,0x61,0x77,0x07,0xa7,0x87,0x3d,0x67,0x1e,0x54,0x74,
+ 0x44,0xa5,0x54,0xa6,0x86,0x3c,0x56,0x1e,0x64,0xa5,0x05,0x3d,0x65,0x1e,0x74,0xa6,
+ 0x86,0x3d,0x56,0x1e,0x67,0x05,0x5c,0x76,0x06,0x86,0x59,0x75,0x65,0x1c,0x05,0xa5,
+ 0x59,0x78,0x86,0x1c,0x06,0xa6,0x86,0x3c,0x56,0x1e,0x76,0x0c,0x02,0xe0,0x27,0x60,
+ 0x63,0xb7,0x48,0x75,0x65,0xa5,0x25,0x2a,0xfb,0xe0,0x45,0x76,0x07,0x60,0x16,0xb7,
+ 0x36,0xb7,0x4b,0x74,0x04,0xa4,0x4b,0x77,0x07,0xa7,0x87,0x3c,0x47,0x1e,0x4a,0x74,
+ 0x04,0xa4,0x04,0x3d,0x74,0x1e,0x49,0x77,0x07,0xa7,0x87,0x3d,0x47,0x1e,0x46,0xb7,
+ 0x17,0x01,0x56,0xb1,0x27,0x01,0x66,0xb1,0x87,0x3f,0x76,0xb7,0x06,0xb5,0xe0,0xf0,
+ 0x26,0x2a,0xdb,0xe0,0x2e,0xa6,0x06,0x2a,0xda,0xe8,0x17,0x2a,0xd2,0xe0,0x43,0x7c,
+ 0x0c,0xa2,0x5d,0xa3,0x43,0x7b,0x00,0x9b,0x34,0x60,0x42,0x75,0x43,0x76,0x07,0x60,
+ 0x43,0x7f,0x0c,0xa2,0x5d,0xa3,0x49,0x67,0xd9,0x1c,0x09,0xa7,0x0e,0x60,0x00,0x9e,
+ 0xc4,0x60,0xb5,0x12,0xe6,0x12,0x3d,0x7f,0x3e,0x7a,0x0a,0x88,0xb0,0x98,0x0c,0xa2,
+ 0x5d,0xa3,0x3c,0x78,0xd8,0x1c,0x08,0xa6,0x3c,0x71,0xd1,0x1c,0xa0,0x91,0x01,0xa7,
+ 0x87,0x3c,0x67,0x1e,0x07,0x3d,0x00,0x9e,0xd4,0x60,0xb5,0x12,0xe6,0x12,0x07,0x3b,
+ 0x33,0x7f,0x0a,0x82,0xc0,0x92,0x0c,0xa2,0x1c,0xa3,0x34,0x74,0x85,0x61,0x34,0x7f,
+ 0x0c,0xa2,0x5d,0xa3,0x09,0xa7,0x00,0x9e,0xc4,0x60,0xb5,0x12,0xe6,0x12,0x2b,0x7f,
+ 0x0a,0x89,0x0c,0xa2,0x5d,0xa3,0x08,0xa6,0xa0,0x88,0x08,0xa7,0x87,0x3c,0x67,0x1e,
+ 0x07,0x3d,0x00,0x9e,0xd4,0x60,0xb5,0x12,0xe6,0x12,0x07,0x3b,0x24,0x7f,0x0a,0x87,
+ 0xb0,0x8b,0x0b,0x2a,0x03,0xe0,0xc0,0x8c,0x0c,0x2a,0x04,0xe8,0x09,0x2a,0xd3,0xe0,
+ 0x07,0x2a,0xd1,0xe0,0x0b,0x7e,0x46,0xf0,0x43,0xa4,0x00,0x00,0xbd,0x00,0x00,0x00,
+ 0x56,0xa4,0x00,0x00,0x57,0xa4,0x00,0x00,0x58,0xa4,0x00,0x00,0x59,0xa4,0x00,0x00,
+ 0x52,0xa4,0x00,0x00,0x53,0xa4,0x00,0x00,0x54,0xa4,0x00,0x00,0x55,0xa4,0x00,0x00,
+ 0x66,0x9e,0x00,0x00,0x20,0xaa,0x00,0x00,0xa5,0xa0,0x00,0x00,0xb9,0x00,0x00,0x00,
+ 0x72,0xa4,0x00,0x00,0x31,0xaa,0x00,0x00,0x62,0xa4,0x00,0x00,0x42,0xa4,0x00,0x00,
+ 0x37,0xaa,0x00,0x00,0x38,0xaa,0x00,0x00,0x39,0xaa,0x00,0x00,0x3a,0xaa,0x00,0x00,
+ 0xbe,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x1c,0x9e,0x00,0x00,0x20,0x9e,0x00,0x00,
+ 0x00,0x00,0x03,0x18,0x00,0x00,0x06,0x18,0x00,0x00,0x02,0x18,0xb4,0x26,0x00,0x00,
+ 0x34,0x0a,0x04,0x00,0xb5,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xa0,0x02,0x00,
+ 0x48,0x2a,0x00,0x00,0x1e,0xa7,0x07,0x20,0x47,0x01,0x1e,0xb7,0x2d,0x60,0x7d,0x0c,
+ 0x37,0xe0,0x07,0x60,0x3e,0xb7,0x1e,0xb7,0x56,0x7f,0x56,0x76,0x06,0xa6,0x56,0x77,
+ 0x07,0xa7,0x87,0x3c,0x67,0x1e,0x55,0x76,0x06,0xa6,0x06,0x3d,0x76,0x1e,0x54,0x77,
+ 0x07,0xa7,0x87,0x3d,0x67,0x1e,0x4e,0xb7,0x17,0x01,0x5e,0xb1,0x27,0x01,0x6e,0xb1,
+ 0x87,0x3f,0x7e,0xb7,0x0e,0xbd,0x1c,0xf0,0x37,0xbf,0x0c,0xac,0x0e,0xa6,0x86,0x3c,
+ 0xc6,0x1e,0x02,0xa5,0x05,0x3d,0x65,0x1e,0x03,0xa6,0x86,0x3d,0x56,0x1e,0x47,0xb6,
+ 0x16,0x01,0x57,0xb1,0x26,0x01,0x67,0xb1,0x86,0x3f,0x77,0xb6,0x26,0x60,0x07,0xb6,
+ 0x07,0xf0,0x27,0x2a,0x05,0xe0,0x43,0x7f,0x03,0xf0,0x36,0x2a,0x01,0xe0,0x42,0x7f,
+ 0x43,0x76,0xd6,0xa5,0xe6,0xa7,0x87,0x3c,0x57,0x1e,0x7f,0x12,0x6f,0x01,0xa6,0xa5,
+ 0x05,0x2a,0x68,0xe8,0x3f,0x75,0x05,0x82,0x3f,0x75,0x25,0x1c,0x05,0xa4,0x04,0x2a,
+ 0x61,0xe0,0x3d,0x75,0x05,0xc3,0x03,0x20,0x63,0x01,0x05,0xd3,0xb6,0xae,0xc6,0xa6,
+ 0x86,0x3c,0xe6,0x1e,0x63,0x0c,0x56,0xe8,0x05,0xd4,0x42,0xaa,0x15,0x60,0x37,0x7b,
+ 0xac,0x61,0x7f,0x01,0x37,0x73,0xfd,0x12,0x0d,0x28,0x1c,0xf0,0x96,0x12,0x46,0x1c,
+ 0x66,0x1c,0xbe,0x12,0x6e,0x1c,0x0e,0xce,0xe1,0x12,0x71,0x01,0x36,0x1c,0x1f,0x0d,
+ 0x03,0xe8,0x06,0xce,0x7e,0x05,0x07,0xf0,0xd1,0x0d,0x03,0xe8,0x06,0xce,0x7e,0x1c,
+ 0x02,0xf0,0x06,0xc1,0x1e,0x14,0x06,0xde,0x04,0x20,0x44,0x01,0x48,0x0c,0xe6,0xe7,
+ 0x05,0x20,0x45,0x01,0x5a,0x0c,0x2e,0xe8,0x52,0xa8,0x14,0x60,0x59,0x12,0xf1,0x11,
+ 0xc9,0x03,0xf9,0x11,0xf3,0xf7,0x1d,0x77,0x0f,0x60,0x17,0xbf,0x16,0x7c,0x0c,0xa5,
+ 0x16,0x7e,0x0e,0xa6,0x86,0x3c,0x56,0x1e,0x15,0x72,0x02,0xa5,0x05,0x3d,0x65,0x1e,
+ 0x14,0x73,0x03,0xa6,0x86,0x3d,0x56,0x1e,0x47,0xa4,0x57,0xa5,0x85,0x3c,0x45,0x1e,
+ 0x67,0xa4,0x04,0x3d,0x54,0x1e,0x77,0xa5,0x85,0x3d,0x45,0x1e,0x56,0x05,0x15,0x75,
+ 0xd5,0x1c,0x05,0xa4,0x15,0x71,0x1d,0x1c,0x0d,0xa5,0x85,0x3c,0x45,0x1e,0x65,0x0c,
+ 0x73,0xef,0x8e,0xf7,0x01,0x64,0x10,0x1c,0x68,0x00,0xf0,0x21,0xcf,0x00,0x00,0x00,
+ 0xac,0x2c,0x00,0x00,0x37,0xaa,0x00,0x00,0x38,0xaa,0x00,0x00,0x39,0xaa,0x00,0x00,
+ 0x3a,0xaa,0x00,0x00,0xaa,0x41,0x00,0x00,0x94,0x3f,0x00,0x00,0x66,0x9e,0x00,0x00,
+ 0x1c,0x9e,0x00,0x00,0x11,0x01,0x00,0x00,0x62,0xa8,0x00,0x00,0x00,0xe0,0x02,0x00,
+ 0x00,0xc0,0x02,0x00,0xb7,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x70,0x24,0x7e,0x00,
+ 0xf1,0x60,0x41,0x0c,0x38,0xe0,0x37,0x12,0x27,0x1e,0x37,0x2e,0x07,0x2a,0x3f,0xe0,
+ 0x36,0x12,0x27,0x12,0x45,0x12,0x06,0x8f,0x07,0x9f,0x16,0x8f,0x17,0x9f,0x26,0x8f,
+ 0x27,0x9f,0x36,0x8f,0x37,0x9f,0xf7,0x20,0xf6,0x20,0xf5,0x24,0x51,0x0c,0xf3,0xef,
+ 0x46,0x12,0xf6,0x24,0x46,0x3e,0x06,0x20,0x46,0x3c,0x27,0x12,0x67,0x1c,0x63,0x1c,
+ 0xf4,0x2e,0x3e,0x60,0x4e,0x0c,0x1e,0xe0,0x31,0x12,0x75,0x12,0x46,0x12,0x01,0x8f,
+ 0x05,0x9f,0x35,0x20,0x31,0x20,0x36,0x24,0x6e,0x0c,0xf9,0xef,0x45,0x12,0x35,0x24,
+ 0x25,0x3e,0x05,0x20,0x25,0x3c,0x34,0x2e,0x53,0x1c,0x57,0x1c,0x04,0x2a,0x05,0xe0,
+ 0x6e,0x00,0x70,0x20,0xcf,0x00,0x27,0x12,0x05,0xf0,0x03,0xa6,0x07,0xb6,0x07,0x20,
+ 0x03,0x20,0x04,0x24,0x04,0x2a,0xf9,0xe7,0x6e,0x00,0x70,0x20,0xcf,0x00,0x27,0x12,
+ 0xf4,0xf7,0x70,0x24,0x00,0x9f,0x37,0x60,0x72,0x0e,0x4c,0xe8,0x04,0x2a,0x47,0xe8,
+ 0x04,0x24,0x03,0x01,0x27,0x12,0x05,0xf0,0x46,0x12,0x06,0x24,0x04,0x2a,0x3f,0xe8,
+ 0x64,0x12,0x07,0xb1,0x07,0x20,0x36,0x60,0x67,0x0e,0xf6,0xe7,0x36,0x60,0x46,0x0c,
+ 0x2e,0xe0,0x85,0x2c,0x35,0x16,0x56,0x12,0x86,0x3c,0x56,0x1e,0x65,0x12,0x05,0x3d,
+ 0x65,0x1e,0xff,0x60,0x4f,0x0c,0x30,0xe0,0x76,0x12,0x41,0x12,0x06,0x95,0x16,0x95,
+ 0x26,0x95,0x36,0x95,0xf6,0x20,0xf1,0x24,0x1f,0x0c,0xf8,0xef,0x41,0x12,0xf1,0x24,
+ 0x41,0x3e,0x01,0x20,0x41,0x3c,0x71,0x1c,0xf4,0x2e,0x37,0x60,0x47,0x0c,0x1e,0xe0,
+ 0x16,0x12,0x47,0x12,0x3f,0x60,0x06,0x95,0x36,0x20,0x37,0x24,0x7f,0x0c,0xfb,0xef,
+ 0x47,0x12,0x37,0x24,0x27,0x3e,0x07,0x20,0x27,0x3c,0x34,0x2e,0x17,0x1c,0x04,0x2a,
+ 0x06,0xe8,0x43,0x01,0x07,0xb3,0x07,0x20,0x04,0x24,0x04,0x2a,0xfb,0xe7,0x00,0x8f,
+ 0x70,0x20,0xcf,0x00,0x27,0x12,0xc2,0xf7,0x71,0x12,0xe2,0xf7,0x17,0x12,0xef,0xf7,
+ 0x02,0x2a,0x19,0xe8,0x03,0x2a,0x02,0xe0,0x0b,0x00,0x15,0xf0,0x07,0x60,0x21,0x12,
+ 0x34,0x12,0xf1,0x37,0x03,0xe8,0xf1,0x33,0x01,0xf0,0x14,0x3c,0x41,0x0f,0x02,0xe8,
+ 0x41,0x0c,0xfb,0xe7,0x01,0xf0,0x14,0x3e,0x42,0x0c,0x01,0xe8,0x42,0x05,0x77,0x06,
+ 0x43,0x0c,0xf9,0xef,0x72,0x12,0xcf,0x00,0x02,0x2a,0x18,0xe8,0x03,0x2a,0x02,0xe0,
+ 0x0b,0x00,0x14,0xf0,0x07,0x60,0x21,0x12,0x34,0x12,0xf1,0x37,0x03,0xe8,0xf1,0x33,
+ 0x01,0xf0,0x14,0x3c,0x41,0x0f,0x02,0xe8,0x41,0x0c,0xfb,0xe7,0x01,0xf0,0x14,0x3e,
+ 0x42,0x0c,0x01,0xe8,0x42,0x05,0x77,0x06,0x43,0x0c,0xf9,0xef,0xcf,0x00,0x03,0x2a,
+ 0x02,0xe0,0x0b,0x00,0x16,0xf0,0x07,0x60,0x31,0x12,0x21,0x17,0xe2,0x01,0xe3,0x01,
+ 0x34,0x12,0x01,0xf0,0x14,0x3c,0x42,0x0c,0xfd,0xe7,0x01,0xf0,0x14,0x3e,0x42,0x0c,
+ 0x01,0xe8,0x42,0x05,0x77,0x06,0x43,0x0c,0xf9,0xef,0xf1,0x37,0x01,0xe8,0x07,0x28,
+ 0x72,0x12,0xcf,0x00,0x25,0x12,0xe2,0x01,0xe3,0x01,0x01,0x60,0x14,0x60,0x03,0x2a,
+ 0x01,0xe0,0x0b,0x00,0x27,0x12,0x07,0x2a,0x02,0xe0,0x02,0x60,0xcf,0x00,0x30,0x24,
+ 0x00,0x91,0xf6,0x61,0x71,0x12,0x61,0x0b,0x01,0x2a,0x03,0xe8,0xf6,0x29,0x67,0x12,
+ 0x08,0xf0,0x06,0x24,0x71,0x12,0x61,0x0b,0x01,0x2a,0xf8,0xe7,0x06,0x2a,0xf2,0xe7,
+ 0x07,0x62,0x00,0x81,0x30,0x20,0x74,0x1b,0x72,0x1b,0x02,0x3c,0x11,0x06,0x31,0x0c,
+ 0x01,0xe8,0x31,0x05,0x44,0x06,0xf9,0xef,0x12,0x12,0xf5,0x37,0x01,0xe8,0x02,0x28,
+ 0xcf,0x00,0xf0,0x25,0x60,0x92,0x70,0x93,0x40,0x94,0x50,0x95,0x07,0x2d,0x27,0x16,
+ 0x25,0x12,0x05,0x3f,0x06,0x2d,0x46,0x16,0x41,0x12,0x01,0x3f,0x63,0x12,0x73,0x03,
+ 0x17,0x03,0x56,0x03,0x15,0x03,0x67,0x1c,0x31,0x12,0x01,0x3f,0x17,0x1c,0x67,0x0c,
+ 0x02,0xe0,0x06,0x33,0x65,0x1c,0x76,0x12,0x06,0x3f,0x56,0x1c,0x10,0x96,0x07,0x3d,
+ 0x06,0x2d,0x63,0x16,0x37,0x1c,0x00,0x97,0x00,0x86,0x10,0x87,0x20,0x96,0x30,0x97,
+ 0x50,0x87,0x27,0x03,0x70,0x86,0x46,0x03,0x67,0x1c,0x20,0x82,0x30,0x83,0x73,0x1c,
+ 0xf0,0x21,0xcf,0x00,0xf0,0x25,0x78,0x00,0x81,0x62,0x10,0x05,0x40,0x92,0x50,0x93,
+ 0x20,0x94,0x30,0x95,0x4d,0x12,0x57,0x12,0x2b,0x12,0x3e,0x12,0x05,0x2a,0x58,0xe0,
+ 0x43,0x0c,0x76,0xe0,0x06,0x2d,0x46,0x0c,0xd4,0xe0,0x87,0x2d,0x47,0x0c,0x8b,0xe9,
+ 0x07,0x61,0x75,0x12,0x9f,0x76,0xd3,0x12,0x73,0x0b,0x36,0x1c,0x06,0xa6,0x56,0x1c,
+ 0x07,0x62,0x67,0x05,0x07,0x2a,0x06,0xe8,0x7d,0x1b,0x7e,0x1b,0xb4,0x12,0x64,0x0b,
+ 0x4e,0x1e,0x7b,0x1b,0xdc,0x12,0x0c,0x3f,0x09,0x2d,0xd9,0x16,0xe2,0x12,0xc3,0x12,
+ 0x95,0x7f,0x28,0x12,0xe2,0x12,0xc3,0x12,0x94,0x7f,0x2a,0x12,0x2e,0x12,0x9e,0x03,
+ 0x08,0x3d,0xb7,0x12,0x07,0x3f,0x78,0x1e,0xe8,0x0c,0x08,0xe0,0x27,0x12,0x07,0x24,
+ 0xd8,0x1c,0xd8,0x0c,0x02,0xe8,0xe8,0x0c,0x8d,0xe9,0x7a,0x12,0x8e,0x14,0xe2,0x12,
+ 0xc3,0x12,0x88,0x7f,0x28,0x12,0xe2,0x12,0xc3,0x12,0x87,0x7f,0x27,0x12,0x29,0x03,
+ 0x08,0x3d,0x05,0x2d,0x5b,0x16,0xb8,0x1e,0x98,0x0c,0x08,0xe0,0x26,0x12,0x06,0x24,
+ 0xd8,0x1c,0xd8,0x0c,0x4b,0xe9,0x98,0x0c,0x49,0xe1,0x17,0x24,0xa2,0x12,0x02,0x3d,
+ 0x72,0x1e,0x0c,0x60,0xc3,0x12,0x81,0x62,0x10,0x1c,0x68,0x00,0xf0,0x21,0xcf,0x00,
+ 0x53,0x0c,0x77,0xe8,0x06,0x2d,0x56,0x0c,0x76,0xe0,0x86,0x2d,0x56,0x0c,0x45,0xe9,
+ 0x05,0x61,0x54,0x12,0x73,0x76,0x73,0x12,0x53,0x0b,0x36,0x1c,0x06,0xa6,0x46,0x1c,
+ 0x0c,0x62,0x6c,0x05,0x0c,0x2a,0x79,0xe0,0x12,0x60,0xe7,0x0c,0x03,0xe8,0xdb,0x0c,
+ 0x01,0xe0,0xc2,0x12,0xc3,0x12,0x81,0x62,0x10,0x1c,0x68,0x00,0xf0,0x21,0xcf,0x00,
+ 0x04,0x2a,0x04,0xe0,0x12,0x60,0x43,0x12,0x68,0x7f,0x2d,0x12,0x07,0x2d,0xd7,0x0c,
+ 0x5e,0xe0,0x87,0x2d,0xd7,0x0c,0x27,0xe9,0x06,0x61,0x65,0x12,0x61,0x77,0xd3,0x12,
+ 0x63,0x0b,0x37,0x1c,0x07,0xa6,0x56,0x1c,0x07,0x62,0x67,0x05,0x07,0x2a,0xbe,0xe0,
+ 0xde,0x05,0xda,0x12,0x0a,0x3f,0x08,0x2d,0xd8,0x16,0x1c,0x60,0xe2,0x12,0xa3,0x12,
+ 0x59,0x7f,0x60,0x92,0xe2,0x12,0xa3,0x12,0x58,0x7f,0x29,0x12,0x27,0x12,0x87,0x03,
+ 0x60,0x8e,0x0e,0x3d,0xb6,0x12,0x06,0x3f,0x6e,0x1e,0x7e,0x0c,0x08,0xe0,0x26,0x12,
+ 0x06,0x24,0xde,0x1c,0xde,0x0c,0x02,0xe8,0x7e,0x0c,0x11,0xe9,0x69,0x12,0x7e,0x05,
+ 0xe2,0x12,0xa3,0x12,0x4c,0x7f,0x60,0x92,0xe2,0x12,0xa3,0x12,0x4b,0x7f,0x26,0x12,
+ 0x28,0x03,0x60,0x87,0x07,0x3d,0x03,0x2d,0x3b,0x16,0xb7,0x1e,0x87,0x0c,0x08,0xe0,
+ 0x25,0x12,0x05,0x24,0xd7,0x1c,0xd7,0x0c,0xd3,0xe8,0x87,0x0c,0xd1,0xe0,0x16,0x24,
+ 0x92,0x12,0x02,0x3d,0x62,0x1e,0xc3,0x12,0x81,0x62,0x10,0x1c,0x68,0x00,0xf0,0x21,
+ 0xcf,0x00,0x0c,0x60,0x9e,0xf7,0x86,0x2c,0x56,0x0c,0xd2,0xe8,0x05,0x60,0x54,0x12,
+ 0x89,0xf7,0x86,0x2c,0x46,0x0c,0xb8,0xe0,0x87,0x60,0x75,0x12,0x2b,0xf7,0x87,0x2c,
+ 0xd7,0x0c,0xcc,0xe8,0x06,0x60,0x65,0x12,0xa1,0xf7,0x7a,0x12,0xca,0x1b,0xd7,0x12,
+ 0x67,0x0b,0x7a,0x1e,0xcd,0x1b,0x60,0x9d,0xe8,0x12,0x68,0x0b,0xe9,0x12,0xc9,0x1b,
+ 0xb7,0x12,0x67,0x0b,0x79,0x1e,0xad,0x12,0x0d,0x3f,0x04,0x2d,0xa4,0x16,0x70,0x94,
+ 0x82,0x12,0xd3,0x12,0x28,0x7f,0x2e,0x12,0x82,0x12,0xd3,0x12,0x27,0x7f,0x28,0x12,
+ 0x70,0x87,0x27,0x03,0x0e,0x3d,0x96,0x12,0x06,0x3f,0x6e,0x1e,0x7e,0x0c,0x06,0xe0,
+ 0x26,0x12,0x06,0x24,0xae,0x1c,0xae,0x0c,0xa9,0xe0,0x68,0x12,0x7e,0x05,0xe2,0x12,
+ 0xd3,0x12,0x1c,0x7f,0x80,0x92,0xe2,0x12,0xd3,0x12,0x1b,0x7f,0x70,0x86,0x26,0x03,
+ 0x80,0x87,0x07,0x3d,0x05,0x2d,0x59,0x16,0x97,0x1e,0x67,0x0c,0x06,0xe0,0x25,0x12,
+ 0x05,0x24,0xa7,0x1c,0xa7,0x0c,0x8d,0xe0,0x52,0x12,0x76,0x14,0x08,0x3d,0x28,0x1e,
+ 0x07,0x2d,0x87,0x16,0x84,0x12,0x04,0x3f,0x60,0x85,0x03,0x2d,0x35,0x16,0x60,0x8d,
+ 0x0d,0x3f,0x73,0x12,0x53,0x03,0xd7,0x03,0x45,0x03,0x4d,0x03,0x57,0x1c,0x34,0x12,
+ 0x04,0x3f,0x47,0x1c,0x57,0x0c,0x02,0xe0,0x04,0x33,0x4d,0x1c,0x75,0x12,0x05,0x3f,
+ 0xd5,0x1c,0x56,0x0c,0x5e,0xe8,0x56,0x0f,0x55,0xe8,0x82,0x12,0x0c,0x60,0x2a,0xf7,
+ 0x8c,0x05,0x00,0x00,0x08,0x84,0x00,0x00,0xd0,0x83,0x00,0x00,0x7d,0x1b,0xe9,0x12,
+ 0x69,0x0b,0xec,0x12,0x7c,0x1b,0xb4,0x12,0x64,0x0b,0x4c,0x1e,0x7b,0x1b,0xda,0x12,
+ 0x0a,0x3f,0x08,0x2d,0xd8,0x16,0x92,0x12,0xa3,0x12,0x34,0x7f,0x2e,0x12,0x92,0x12,
+ 0xa3,0x12,0x33,0x7f,0x29,0x12,0x27,0x12,0x87,0x03,0x0e,0x3d,0xc6,0x12,0x06,0x3f,
+ 0x6e,0x1e,0x7e,0x0c,0x09,0xe0,0x26,0x12,0x06,0x24,0xde,0x1c,0xde,0x0c,0x4d,0xe8,
+ 0x7e,0x0c,0x4b,0xe0,0x19,0x24,0xde,0x1c,0x7e,0x05,0xe2,0x12,0xa3,0x12,0x27,0x7f,
+ 0x60,0x92,0xe2,0x12,0xa3,0x12,0x26,0x7f,0x27,0x12,0x87,0x03,0x60,0x8e,0x0e,0x3d,
+ 0x05,0x2d,0x5c,0x16,0xce,0x1e,0x7e,0x0c,0x09,0xe0,0x26,0x12,0x06,0x24,0xde,0x1c,
+ 0xde,0x0c,0x31,0xe8,0x7e,0x0c,0x2f,0xe0,0x12,0x24,0xde,0x1c,0x7e,0x05,0x9c,0x12,
+ 0x0c,0x3d,0x2c,0x1e,0x03,0xf7,0x87,0x61,0x75,0x12,0x74,0xf6,0x67,0x12,0xb6,0xf6,
+ 0x56,0x12,0x2e,0xf7,0xcb,0x1b,0x07,0x3d,0x05,0x2d,0x53,0x16,0x37,0x1c,0x7b,0x0c,
+ 0xa4,0xe7,0x82,0x12,0x02,0x24,0x0c,0x60,0xcd,0xf6,0x85,0x61,0x54,0x12,0xba,0xf6,
+ 0x85,0x60,0x54,0x12,0xb7,0xf6,0x86,0x61,0x65,0x12,0xd8,0xf6,0x86,0x60,0x65,0x12,
+ 0xd5,0xf6,0x67,0x0c,0x71,0xe7,0x12,0x24,0xa7,0x1c,0x6f,0xf7,0x7e,0x0c,0x55,0xe7,
+ 0x18,0x24,0xae,0x1c,0x53,0xf7,0x62,0x12,0xd1,0xf7,0x69,0x12,0xb5,0xf7,0x19,0x24,
+ 0xde,0x1c,0xed,0xf6,0x1a,0x24,0xd8,0x1c,0x71,0xf6,0x00,0x00,0x08,0x84,0x00,0x00,
+ 0xd0,0x83,0x00,0x00,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0x70,0x24,0x7e,0x00,
+ 0x0e,0x7f,0x06,0x66,0x0e,0x77,0x07,0xb6,0x0e,0x7f,0x0e,0x7f,0x0f,0x72,0x43,0x60,
+ 0x0f,0x7f,0x0f,0x7f,0x10,0x7f,0x10,0x7f,0x11,0x7e,0x0e,0xa7,0x17,0x2a,0x07,0xe8,
+ 0x07,0x2a,0x03,0xe8,0x27,0x2a,0xf9,0xe7,0x04,0xf0,0x0d,0x7f,0xf6,0xf7,0x0d,0x7f,
+ 0xf4,0xf7,0x0d,0x7f,0xf2,0xf7,0x00,0x00,0x40,0x08,0x00,0x00,0x20,0x01,0x04,0x00,
+ 0xaa,0x08,0x00,0x00,0x6c,0x26,0x00,0x00,0x80,0xc3,0xc9,0x01,0x9a,0x23,0x00,0x00,
+ 0x40,0x1b,0x00,0x00,0xec,0x0d,0x00,0x00,0x98,0x09,0x00,0x00,0x20,0xaa,0x00,0x00,
+ 0x88,0x1b,0x00,0x00,0x88,0x1c,0x00,0x00,0xfc,0x1c,0x00,0x00,0xbe,0xbe,0xbe,0xbe,
+ 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+}; \ No newline at end of file
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
new file mode 100644
index 00000000..c0044175
--- /dev/null
+++ b/drivers/input/touchscreen/ili210x.c
@@ -0,0 +1,360 @@
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/input/ili210x.h>
+
+#define MAX_TOUCHES 2
+#define DEFAULT_POLL_PERIOD 20
+
+/* Touchscreen commands */
+#define REG_TOUCHDATA 0x10
+#define REG_PANEL_INFO 0x20
+#define REG_FIRMWARE_VERSION 0x40
+#define REG_CALIBRATE 0xcc
+
+struct finger {
+ u8 x_low;
+ u8 x_high;
+ u8 y_low;
+ u8 y_high;
+} __packed;
+
+struct touchdata {
+ u8 status;
+ struct finger finger[MAX_TOUCHES];
+} __packed;
+
+struct panel_info {
+ struct finger finger_max;
+ u8 xchannel_num;
+ u8 ychannel_num;
+} __packed;
+
+struct firmware_version {
+ u8 id;
+ u8 major;
+ u8 minor;
+} __packed;
+
+struct ili210x {
+ struct i2c_client *client;
+ struct input_dev *input;
+ bool (*get_pendown_state)(void);
+ unsigned int poll_period;
+ struct delayed_work dwork;
+};
+
+static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
+ size_t len)
+{
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ }
+ };
+
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void ili210x_report_events(struct input_dev *input,
+ const struct touchdata *touchdata)
+{
+ int i;
+ bool touch;
+ unsigned int x, y;
+ const struct finger *finger;
+
+ for (i = 0; i < MAX_TOUCHES; i++) {
+ input_mt_slot(input, i);
+
+ finger = &touchdata->finger[i];
+
+ touch = touchdata->status & (1 << i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+ if (touch) {
+ x = finger->x_low | (finger->x_high << 8);
+ y = finger->y_low | (finger->y_high << 8);
+
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+ }
+ }
+
+ input_mt_report_pointer_emulation(input, false);
+ input_sync(input);
+}
+
+static bool get_pendown_state(const struct ili210x *priv)
+{
+ bool state = false;
+
+ if (priv->get_pendown_state)
+ state = priv->get_pendown_state();
+
+ return state;
+}
+
+static void ili210x_work(struct work_struct *work)
+{
+ struct ili210x *priv = container_of(work, struct ili210x,
+ dwork.work);
+ struct i2c_client *client = priv->client;
+ struct touchdata touchdata;
+ int error;
+
+ error = ili210x_read_reg(client, REG_TOUCHDATA,
+ &touchdata, sizeof(touchdata));
+ if (error) {
+ dev_err(&client->dev,
+ "Unable to get touchdata, err = %d\n", error);
+ return;
+ }
+
+ ili210x_report_events(priv->input, &touchdata);
+
+ if ((touchdata.status & 0xf3) || get_pendown_state(priv))
+ schedule_delayed_work(&priv->dwork,
+ msecs_to_jiffies(priv->poll_period));
+}
+
+static irqreturn_t ili210x_irq(int irq, void *irq_data)
+{
+ struct ili210x *priv = irq_data;
+
+ schedule_delayed_work(&priv->dwork, 0);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t ili210x_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ili210x *priv = i2c_get_clientdata(client);
+ unsigned long calibrate;
+ int rc;
+ u8 cmd = REG_CALIBRATE;
+
+ if (kstrtoul(buf, 10, &calibrate))
+ return -EINVAL;
+
+ if (calibrate > 1)
+ return -EINVAL;
+
+ if (calibrate) {
+ rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
+ if (rc != sizeof(cmd))
+ return -EIO;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate);
+
+static struct attribute *ili210x_attributes[] = {
+ &dev_attr_calibrate.attr,
+ NULL,
+};
+
+static const struct attribute_group ili210x_attr_group = {
+ .attrs = ili210x_attributes,
+};
+
+static int __devinit ili210x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ const struct ili210x_platform_data *pdata = dev->platform_data;
+ struct ili210x *priv;
+ struct input_dev *input;
+ struct panel_info panel;
+ struct firmware_version firmware;
+ int xmax, ymax;
+ int error;
+
+ dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+
+ if (!pdata) {
+ dev_err(dev, "No platform data!\n");
+ return -EINVAL;
+ }
+
+ if (client->irq <= 0) {
+ dev_err(dev, "No IRQ!\n");
+ return -EINVAL;
+ }
+
+ /* Get firmware version */
+ error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
+ &firmware, sizeof(firmware));
+ if (error) {
+ dev_err(dev, "Failed to get firmware version, err: %d\n",
+ error);
+ return error;
+ }
+
+ /* get panel info */
+ error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
+ if (error) {
+ dev_err(dev, "Failed to get panel informations, err: %d\n",
+ error);
+ return error;
+ }
+
+ xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
+ ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!priv || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ priv->client = client;
+ priv->input = input;
+ priv->get_pendown_state = pdata->get_pendown_state;
+ priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+ INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+
+ /* Setup input device */
+ input->name = "ILI210x Touchscreen";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = dev;
+
+ __set_bit(EV_SYN, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+
+ /* Single touch */
+ input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
+
+ /* Multi touch */
+ input_mt_init_slots(input, MAX_TOUCHES);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
+
+ input_set_drvdata(input, priv);
+ i2c_set_clientdata(client, priv);
+
+ error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
+ client->name, priv);
+ if (error) {
+ dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
+ error);
+ goto err_free_mem;
+ }
+
+ error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
+ if (error) {
+ dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ error = input_register_device(priv->input);
+ if (error) {
+ dev_err(dev, "Cannot regiser input device, err: %d\n", error);
+ goto err_remove_sysfs;
+ }
+
+ device_init_wakeup(&client->dev, 1);
+
+ dev_dbg(dev,
+ "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
+ client->irq, firmware.id, firmware.major, firmware.minor);
+
+ return 0;
+
+err_remove_sysfs:
+ sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
+err_free_irq:
+ free_irq(client->irq, priv);
+err_free_mem:
+ input_free_device(input);
+ kfree(priv);
+ return error;
+}
+
+static int __devexit ili210x_i2c_remove(struct i2c_client *client)
+{
+ struct ili210x *priv = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
+ free_irq(priv->client->irq, priv);
+ cancel_delayed_work_sync(&priv->dwork);
+ input_unregister_device(priv->input);
+ kfree(priv);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ili210x_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int ili210x_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
+ ili210x_i2c_suspend, ili210x_i2c_resume);
+
+static const struct i2c_device_id ili210x_i2c_id[] = {
+ { "ili210x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
+
+static struct i2c_driver ili210x_ts_driver = {
+ .driver = {
+ .name = "ili210x_i2c",
+ .owner = THIS_MODULE,
+ .pm = &ili210x_i2c_pm,
+ },
+ .id_table = ili210x_i2c_id,
+ .probe = ili210x_i2c_probe,
+ .remove = __devexit_p(ili210x_i2c_remove),
+};
+
+module_i2c_driver(ili210x_ts_driver);
+
+MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
+MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c
new file mode 100644
index 00000000..192ade0a
--- /dev/null
+++ b/drivers/input/touchscreen/inexio.c
@@ -0,0 +1,207 @@
+/*
+ * iNexio serial touchscreen driver
+ *
+ * Copyright (c) 2008 Richard Lemon
+ * Based on the mtouch driver (c) Vojtech Pavlik and Dan Streetman
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2008/06/19 Richard Lemon <richard@codelemon.com>
+ * Copied mtouch.c and edited for iNexio protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "iNexio serial touchscreen driver"
+
+MODULE_AUTHOR("Richard Lemon <richard@codelemon.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define INEXIO_FORMAT_TOUCH_BIT 0x01
+#define INEXIO_FORMAT_LENGTH 5
+#define INEXIO_RESPONSE_BEGIN_BYTE 0x80
+
+/* todo: check specs for max length of all responses */
+#define INEXIO_MAX_LENGTH 16
+
+#define INEXIO_MIN_XC 0
+#define INEXIO_MAX_XC 0x3fff
+#define INEXIO_MIN_YC 0
+#define INEXIO_MAX_YC 0x3fff
+
+#define INEXIO_GET_XC(data) (((data[1])<<7) | data[2])
+#define INEXIO_GET_YC(data) (((data[3])<<7) | data[4])
+#define INEXIO_GET_TOUCHED(data) (INEXIO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct inexio {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[INEXIO_MAX_LENGTH];
+ char phys[32];
+};
+
+static void inexio_process_data(struct inexio *pinexio)
+{
+ struct input_dev *dev = pinexio->dev;
+
+ if (INEXIO_FORMAT_LENGTH == ++pinexio->idx) {
+ input_report_abs(dev, ABS_X, INEXIO_GET_XC(pinexio->data));
+ input_report_abs(dev, ABS_Y, INEXIO_GET_YC(pinexio->data));
+ input_report_key(dev, BTN_TOUCH, INEXIO_GET_TOUCHED(pinexio->data));
+ input_sync(dev);
+
+ pinexio->idx = 0;
+ }
+}
+
+static irqreturn_t inexio_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct inexio* pinexio = serio_get_drvdata(serio);
+
+ pinexio->data[pinexio->idx] = data;
+
+ if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0])
+ inexio_process_data(pinexio);
+ else
+ printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * inexio_disconnect() is the opposite of inexio_connect()
+ */
+
+static void inexio_disconnect(struct serio *serio)
+{
+ struct inexio* pinexio = serio_get_drvdata(serio);
+
+ input_get_device(pinexio->dev);
+ input_unregister_device(pinexio->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(pinexio->dev);
+ kfree(pinexio);
+}
+
+/*
+ * inexio_connect() is the routine that is called when someone adds a
+ * new serio device that supports iNexio protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int inexio_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct inexio *pinexio;
+ struct input_dev *input_dev;
+ int err;
+
+ pinexio = kzalloc(sizeof(struct inexio), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pinexio || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pinexio->serio = serio;
+ pinexio->dev = input_dev;
+ snprintf(pinexio->phys, sizeof(pinexio->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "iNexio Serial TouchScreen";
+ input_dev->phys = pinexio->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_INEXIO;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(pinexio->dev, ABS_X, INEXIO_MIN_XC, INEXIO_MAX_XC, 0, 0);
+ input_set_abs_params(pinexio->dev, ABS_Y, INEXIO_MIN_YC, INEXIO_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, pinexio);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pinexio->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pinexio);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id inexio_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_INEXIO,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, inexio_serio_ids);
+
+static struct serio_driver inexio_drv = {
+ .driver = {
+ .name = "inexio",
+ },
+ .description = DRIVER_DESC,
+ .id_table = inexio_serio_ids,
+ .interrupt = inexio_interrupt,
+ .connect = inexio_connect,
+ .disconnect = inexio_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init inexio_init(void)
+{
+ return serio_register_driver(&inexio_drv);
+}
+
+static void __exit inexio_exit(void)
+{
+ serio_unregister_driver(&inexio_drv);
+}
+
+module_init(inexio_init);
+module_exit(inexio_exit);
diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c
new file mode 100644
index 00000000..3cd7a837
--- /dev/null
+++ b/drivers/input/touchscreen/intel-mid-touch.c
@@ -0,0 +1,671 @@
+/*
+ * Intel MID Resistive Touch Screen Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
+ * Ramesh Agarwal (ramesh.agarwal@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * TODO:
+ * review conversion of r/m/w sequences
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/intel_scu_ipc.h>
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_INT 0x04 /* PMIC interrupt register */
+#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */
+
+/* ADC Interrupt registers */
+#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */
+#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */
+
+/* ADC Control registers */
+#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */
+
+/* ADC Channel Selection registers */
+#define PMICADDR0 0xA4
+#define END_OF_CHANNEL 0x1F
+
+/* ADC Result register */
+#define PMIC_REG_ADCSNS0H 0x64
+
+/* ADC channels for touch screen */
+#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
+#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
+#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
+#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
+
+/* Touch screen channel BIAS constants */
+#define MRST_XBIAS 0x20
+#define MRST_YBIAS 0x40
+#define MRST_ZBIAS 0x80
+
+/* Touch screen coordinates */
+#define MRST_X_MIN 10
+#define MRST_X_MAX 1024
+#define MRST_X_FUZZ 5
+#define MRST_Y_MIN 10
+#define MRST_Y_MAX 1024
+#define MRST_Y_FUZZ 5
+#define MRST_PRESSURE_MIN 0
+#define MRST_PRESSURE_NOMINAL 50
+#define MRST_PRESSURE_MAX 100
+
+#define WAIT_ADC_COMPLETION 10 /* msec */
+
+/* PMIC ADC round robin delays */
+#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
+#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
+
+/* PMIC Vendor Identifiers */
+#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
+#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
+#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
+#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
+
+/* Touch screen device structure */
+struct mrstouch_dev {
+ struct device *dev; /* device associated with touch screen */
+ struct input_dev *input;
+ char phys[32];
+ u16 asr; /* Address selection register */
+ int irq;
+ unsigned int vendor; /* PMIC vendor */
+ unsigned int rev; /* PMIC revision */
+
+ int (*read_prepare)(struct mrstouch_dev *tsdev);
+ int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);
+ int (*read_finish)(struct mrstouch_dev *tsdev);
+};
+
+
+/*************************** NEC and Maxim Interface ************************/
+
+static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+ return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);
+}
+
+/*
+ * Enables PENDET interrupt.
+ */
+static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+ int err;
+
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);
+ if (!err)
+ err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);
+
+ return err;
+}
+
+/*
+ * Reads PMIC ADC touch screen result
+ * Reads ADC storage registers for higher 7 and lower 3 bits and
+ * converts the two readings into a single value and turns off gain bit
+ */
+static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
+{
+ int err;
+ u16 result;
+ u32 res;
+
+ result = PMIC_REG_ADCSNS0H + offset;
+
+ if (chan == MRST_TS_CHAN12)
+ result += 4;
+
+ err = intel_scu_ipc_ioread32(result, &res);
+ if (err)
+ return err;
+
+ /* Mash the bits up */
+
+ *vp = (res & 0xFF) << 3; /* Highest 7 bits */
+ *vp |= (res >> 8) & 0x07; /* Lower 3 bits */
+ *vp &= 0x3FF;
+
+ res >>= 16;
+
+ *vm = (res & 0xFF) << 3; /* Highest 7 bits */
+ *vm |= (res >> 8) & 0x07; /* Lower 3 bits */
+ *vm &= 0x3FF;
+
+ return 0;
+}
+
+/*
+ * Enables X, Y and Z bias values
+ * Enables YPYM for X channels and XPXM for Y channels
+ */
+static int mrstouch_ts_bias_set(uint offset, uint bias)
+{
+ int count;
+ u16 chan, start;
+ u16 reg[4];
+ u8 data[4];
+
+ chan = PMICADDR0 + offset;
+ start = MRST_TS_CHAN10;
+
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = bias | (start + count);
+ }
+
+ return intel_scu_ipc_writev(reg, data, 4);
+}
+
+/* To read touch screen channel values */
+static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,
+ u16 *x, u16 *y, u16 *z)
+{
+ int err;
+ u16 xm, ym, zm;
+
+ /* configure Y bias for X channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read x+ and x- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);
+ if (err)
+ goto ipc_error;
+
+ /* configure x bias for y channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read y+ and y- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);
+ if (err)
+ goto ipc_error;
+
+ /* configure z bias for x and y channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read z+ and z- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);
+ if (err)
+ goto ipc_error;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during adc read\n");
+ return err;
+}
+
+
+/*************************** Freescale Interface ************************/
+
+static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+ int err, count;
+ u16 chan;
+ u16 reg[5];
+ u8 data[5];
+
+ /* Stop the ADC */
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
+ if (err)
+ goto ipc_error;
+
+ chan = PMICADDR0 + tsdev->asr;
+
+ /* Set X BIAS */
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = 0x2A;
+ }
+ reg[count] = chan++; /* Dummy */
+ data[count] = 0;
+
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* Set Y BIAS */
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = 0x4A;
+ }
+ reg[count] = chan++; /* Dummy */
+ data[count] = 0;
+
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* Set Z BIAS */
+ err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,
+ u16 *x, u16 *y, u16 *z)
+{
+ int err;
+ u16 result;
+ u16 reg[4];
+ u8 data[4];
+
+ result = PMIC_REG_ADCSNS0H + tsdev->asr;
+
+ reg[0] = result + 4;
+ reg[1] = result + 5;
+ reg[2] = result + 16;
+ reg[3] = result + 17;
+
+ err = intel_scu_ipc_readv(reg, data, 4);
+ if (err)
+ goto ipc_error;
+
+ *x = data[0] << 3; /* Higher 7 bits */
+ *x |= data[1] & 0x7; /* Lower 3 bits */
+ *x &= 0x3FF;
+
+ *y = data[2] << 3; /* Higher 7 bits */
+ *y |= data[3] & 0x7; /* Lower 3 bits */
+ *y &= 0x3FF;
+
+ /* Read Z value */
+ reg[0] = result + 28;
+ reg[1] = result + 29;
+
+ err = intel_scu_ipc_readv(reg, data, 4);
+ if (err)
+ goto ipc_error;
+
+ *z = data[0] << 3; /* Higher 7 bits */
+ *z |= data[1] & 0x7; /* Lower 3 bits */
+ *z &= 0x3FF;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+ int err, count;
+ u16 chan;
+ u16 reg[5];
+ u8 data[5];
+
+ /* Clear all TS channels */
+ chan = PMICADDR0 + tsdev->asr;
+ for (count = 0; count <= 4; count++) {
+ reg[count] = chan++;
+ data[count] = 0;
+ }
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ for (count = 0; count <= 4; count++) {
+ reg[count] = chan++;
+ data[count] = 0;
+ }
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
+ if (err)
+ goto ipc_error;
+
+ /* Start ADC */
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
+ if (err)
+ goto ipc_error;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static void mrstouch_report_event(struct input_dev *input,
+ unsigned int x, unsigned int y, unsigned int z)
+{
+ if (z > MRST_PRESSURE_NOMINAL) {
+ /* Pen touched, report button touch and coordinates */
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ } else {
+ input_report_key(input, BTN_TOUCH, 0);
+ }
+
+ input_report_abs(input, ABS_PRESSURE, z);
+ input_sync(input);
+}
+
+/* PENDET interrupt handler */
+static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)
+{
+ struct mrstouch_dev *tsdev = dev_id;
+ u16 x, y, z;
+
+ /*
+ * Should we lower thread priority? Probably not, since we are
+ * not spinning but sleeping...
+ */
+
+ if (tsdev->read_prepare(tsdev))
+ goto out;
+
+ do {
+ if (tsdev->read(tsdev, &x, &y, &z))
+ break;
+
+ mrstouch_report_event(tsdev->input, x, y, z);
+ } while (z > MRST_PRESSURE_NOMINAL);
+
+ tsdev->read_finish(tsdev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+/* Utility to read PMIC ID */
+static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)
+{
+ int err;
+ u8 r;
+
+ err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
+ if (err)
+ return err;
+
+ *vendor = r & 0x7;
+ *rev = (r >> 3) & 0x7;
+
+ return 0;
+}
+
+/*
+ * Parse ADC channels to find end of the channel configured by other ADC user
+ * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
+ */
+static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
+{
+ int found = 0;
+ int err, i;
+ u8 r8;
+
+ for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
+ err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
+ if (err)
+ return err;
+
+ if (r8 == END_OF_CHANNEL) {
+ found = i;
+ break;
+ }
+ }
+
+ if (tsdev->vendor == PMIC_VENDOR_FS) {
+ if (found > MRSTOUCH_MAX_CHANNELS - 18)
+ return -ENOSPC;
+ } else {
+ if (found > MRSTOUCH_MAX_CHANNELS - 4)
+ return -ENOSPC;
+ }
+
+ return found;
+}
+
+
+/*
+ * Writes touch screen channels to ADC address selection registers
+ */
+static int __devinit mrstouch_ts_chan_set(uint offset)
+{
+ u16 chan;
+
+ int ret, count;
+
+ chan = PMICADDR0 + offset;
+ for (count = 0; count <= 3; count++) {
+ ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);
+ if (ret)
+ return ret;
+ }
+ return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);
+}
+
+/* Initialize ADC */
+static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)
+{
+ int err, start;
+ u8 ra, rm;
+
+ err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);
+ if (err) {
+ dev_err(tsdev->dev, "Unable to read PMIC id\n");
+ return err;
+ }
+
+ switch (tsdev->vendor) {
+ case PMIC_VENDOR_NEC:
+ case PMIC_VENDOR_MAXIM:
+ tsdev->read_prepare = mrstouch_nec_adc_read_prepare;
+ tsdev->read = mrstouch_nec_adc_read;
+ tsdev->read_finish = mrstouch_nec_adc_read_finish;
+ break;
+
+ case PMIC_VENDOR_FS:
+ tsdev->read_prepare = mrstouch_fs_adc_read_prepare;
+ tsdev->read = mrstouch_fs_adc_read;
+ tsdev->read_finish = mrstouch_fs_adc_read_finish;
+ break;
+
+ default:
+ dev_err(tsdev->dev,
+ "Unsupported touchscreen: %d\n", tsdev->vendor);
+ return -ENXIO;
+ }
+
+ start = mrstouch_chan_parse(tsdev);
+ if (start < 0) {
+ dev_err(tsdev->dev, "Unable to parse channels\n");
+ return start;
+ }
+
+ tsdev->asr = start;
+
+ /*
+ * ADC power on, start, enable PENDET and set loop delay
+ * ADC loop delay is set to 4.5 ms approximately
+ * Loop delay more than this results in jitter in adc readings
+ * Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET
+ * interrupt generation sometimes.
+ */
+
+ if (tsdev->vendor == PMIC_VENDOR_FS) {
+ ra = 0xE0 | ADC_LOOP_DELAY0;
+ rm = 0x5;
+ } else {
+ /* NEC and MAXIm not consistent with loop delay 0 */
+ ra = 0xE0 | ADC_LOOP_DELAY1;
+ rm = 0x0;
+
+ /* configure touch screen channels */
+ err = mrstouch_ts_chan_set(tsdev->asr);
+ if (err)
+ return err;
+ }
+
+ err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
+ if (err)
+ return err;
+
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+/* Probe function for touch screen driver */
+static int __devinit mrstouch_probe(struct platform_device *pdev)
+{
+ struct mrstouch_dev *tsdev;
+ struct input_dev *input;
+ int err;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no interrupt assigned\n");
+ return -EINVAL;
+ }
+
+ tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!tsdev || !input) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsdev->dev = &pdev->dev;
+ tsdev->input = input;
+ tsdev->irq = irq;
+
+ snprintf(tsdev->phys, sizeof(tsdev->phys),
+ "%s/input0", dev_name(tsdev->dev));
+
+ err = mrstouch_adc_init(tsdev);
+ if (err) {
+ dev_err(&pdev->dev, "ADC initialization failed\n");
+ goto err_free_mem;
+ }
+
+ input->name = "mrst_touchscreen";
+ input->phys = tsdev->phys;
+ input->dev.parent = tsdev->dev;
+
+ input->id.vendor = tsdev->vendor;
+ input->id.version = tsdev->rev;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(tsdev->input, ABS_X,
+ MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);
+ input_set_abs_params(tsdev->input, ABS_Y,
+ MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);
+ input_set_abs_params(tsdev->input, ABS_PRESSURE,
+ MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);
+
+ err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq,
+ 0, "mrstouch", tsdev);
+ if (err) {
+ dev_err(tsdev->dev, "unable to allocate irq\n");
+ goto err_free_mem;
+ }
+
+ err = input_register_device(tsdev->input);
+ if (err) {
+ dev_err(tsdev->dev, "unable to register input device\n");
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, tsdev);
+ return 0;
+
+err_free_irq:
+ free_irq(tsdev->irq, tsdev);
+err_free_mem:
+ input_free_device(input);
+ kfree(tsdev);
+ return err;
+}
+
+static int __devexit mrstouch_remove(struct platform_device *pdev)
+{
+ struct mrstouch_dev *tsdev = platform_get_drvdata(pdev);
+
+ free_irq(tsdev->irq, tsdev);
+ input_unregister_device(tsdev->input);
+ kfree(tsdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mrstouch_driver = {
+ .driver = {
+ .name = "pmic_touch",
+ .owner = THIS_MODULE,
+ },
+ .probe = mrstouch_probe,
+ .remove = __devexit_p(mrstouch_remove),
+};
+module_platform_driver(mrstouch_driver);
+
+MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
+MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
new file mode 100644
index 00000000..d9be6eac
--- /dev/null
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -0,0 +1,176 @@
+/*
+ * drivers/input/touchscreen/jornada720_ts.c
+ *
+ * Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
+ *
+ * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
+ * based on HP Jornada 56x touchscreen driver by Alex Lange <chicken@handhelds.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * HP Jornada 710/720/729 Touchscreen Driver
+ */
+
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/jornada720.h>
+#include <mach/irqs.h>
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
+MODULE_LICENSE("GPL v2");
+
+struct jornada_ts {
+ struct input_dev *dev;
+ int x_data[4]; /* X sample values */
+ int y_data[4]; /* Y sample values */
+};
+
+static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
+{
+
+ /* 3 low word X samples */
+ jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
+
+ /* 3 low word Y samples */
+ jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
+ jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
+
+ /* combined x samples bits */
+ jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
+
+ /* combined y samples bits */
+ jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
+}
+
+static int jornada720_ts_average(int coords[4])
+{
+ int coord, high_bits = coords[3];
+
+ coord = coords[0] | ((high_bits & 0x03) << 8);
+ coord += coords[1] | ((high_bits & 0x0c) << 6);
+ coord += coords[2] | ((high_bits & 0x30) << 4);
+
+ return coord / 3;
+}
+
+static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
+ struct input_dev *input = jornada_ts->dev;
+ int x, y;
+
+ /* If GPIO_GPIO9 is set to high then report pen up */
+ if (GPLR & GPIO_GPIO(9)) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_sync(input);
+ } else {
+ jornada_ssp_start();
+
+ /* proper reply to request is always TXDUMMY */
+ if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) {
+ jornada720_ts_collect_data(jornada_ts);
+
+ x = jornada720_ts_average(jornada_ts->x_data);
+ y = jornada720_ts_average(jornada_ts->y_data);
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_sync(input);
+ }
+
+ jornada_ssp_end();
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit jornada720_ts_probe(struct platform_device *pdev)
+{
+ struct jornada_ts *jornada_ts;
+ struct input_dev *input_dev;
+ int error;
+
+ jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+
+ if (!jornada_ts || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ platform_set_drvdata(pdev, jornada_ts);
+
+ jornada_ts->dev = input_dev;
+
+ input_dev->name = "HP Jornada 7xx Touchscreen";
+ input_dev->phys = "jornadats/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
+
+ error = request_irq(IRQ_GPIO9,
+ jornada720_ts_interrupt,
+ IRQF_TRIGGER_RISING,
+ "HP7XX Touchscreen driver", pdev);
+ if (error) {
+ printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
+ goto fail1;
+ }
+
+ error = input_register_device(jornada_ts->dev);
+ if (error)
+ goto fail2;
+
+ return 0;
+
+ fail2:
+ free_irq(IRQ_GPIO9, pdev);
+ fail1:
+ platform_set_drvdata(pdev, NULL);
+ input_free_device(input_dev);
+ kfree(jornada_ts);
+ return error;
+}
+
+static int __devexit jornada720_ts_remove(struct platform_device *pdev)
+{
+ struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
+
+ free_irq(IRQ_GPIO9, pdev);
+ platform_set_drvdata(pdev, NULL);
+ input_unregister_device(jornada_ts->dev);
+ kfree(jornada_ts);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:jornada_ts");
+
+static struct platform_driver jornada720_ts_driver = {
+ .probe = jornada720_ts_probe,
+ .remove = __devexit_p(jornada720_ts_remove),
+ .driver = {
+ .name = "jornada_ts",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(jornada720_ts_driver);
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c
new file mode 100644
index 00000000..afcd0691
--- /dev/null
+++ b/drivers/input/touchscreen/lpc32xx_ts.c
@@ -0,0 +1,400 @@
+/*
+ * LPC32xx built-in touchscreen driver
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/*
+ * Touchscreen controller register offsets
+ */
+#define LPC32XX_TSC_STAT 0x00
+#define LPC32XX_TSC_SEL 0x04
+#define LPC32XX_TSC_CON 0x08
+#define LPC32XX_TSC_FIFO 0x0C
+#define LPC32XX_TSC_DTR 0x10
+#define LPC32XX_TSC_RTR 0x14
+#define LPC32XX_TSC_UTR 0x18
+#define LPC32XX_TSC_TTR 0x1C
+#define LPC32XX_TSC_DXP 0x20
+#define LPC32XX_TSC_MIN_X 0x24
+#define LPC32XX_TSC_MAX_X 0x28
+#define LPC32XX_TSC_MIN_Y 0x2C
+#define LPC32XX_TSC_MAX_Y 0x30
+#define LPC32XX_TSC_AUX_UTR 0x34
+#define LPC32XX_TSC_AUX_MIN 0x38
+#define LPC32XX_TSC_AUX_MAX 0x3C
+
+#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8)
+#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7)
+
+#define LPC32XX_TSC_SEL_DEFVAL 0x0284
+
+#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11)
+#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7)
+#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4)
+#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2)
+#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0)
+
+#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31)
+#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16)
+#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF)
+
+#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF
+
+#define LPC32XX_TSC_MIN_XY_VAL 0x0
+#define LPC32XX_TSC_MAX_XY_VAL 0x3FF
+
+#define MOD_NAME "ts-lpc32xx"
+
+#define tsc_readl(dev, reg) \
+ __raw_readl((dev)->tsc_base + (reg))
+#define tsc_writel(dev, reg, val) \
+ __raw_writel((val), (dev)->tsc_base + (reg))
+
+struct lpc32xx_tsc {
+ struct input_dev *dev;
+ void __iomem *tsc_base;
+ int irq;
+ struct clk *clk;
+};
+
+static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc)
+{
+ while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+ LPC32XX_TSC_STAT_FIFO_EMPTY))
+ tsc_readl(tsc, LPC32XX_TSC_FIFO);
+}
+
+static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id)
+{
+ u32 tmp, rv[4], xs[4], ys[4];
+ int idx;
+ struct lpc32xx_tsc *tsc = dev_id;
+ struct input_dev *input = tsc->dev;
+
+ tmp = tsc_readl(tsc, LPC32XX_TSC_STAT);
+
+ if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
+ /* FIFO overflow - throw away samples */
+ lpc32xx_fifo_clear(tsc);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Gather and normalize 4 samples. Pen-up events may have less
+ * than 4 samples, but its ok to pop 4 and let the last sample
+ * pen status check drop the samples.
+ */
+ idx = 0;
+ while (idx < 4 &&
+ !(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+ LPC32XX_TSC_STAT_FIFO_EMPTY)) {
+ tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO);
+ xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+ LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
+ ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+ LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
+ rv[idx] = tmp;
+ idx++;
+ }
+
+ /* Data is only valid if pen is still down in last sample */
+ if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) {
+ /* Use average of 2nd and 3rd sample for position */
+ input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
+ input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
+ input_report_key(input, BTN_TOUCH, 1);
+ } else {
+ input_report_key(input, BTN_TOUCH, 0);
+ }
+
+ input_sync(input);
+
+ return IRQ_HANDLED;
+}
+
+static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
+{
+ /* Disable auto mode */
+ tsc_writel(tsc, LPC32XX_TSC_CON,
+ tsc_readl(tsc, LPC32XX_TSC_CON) &
+ ~LPC32XX_TSC_ADCCON_AUTO_EN);
+
+ clk_disable(tsc->clk);
+}
+
+static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
+{
+ u32 tmp;
+
+ clk_enable(tsc->clk);
+
+ tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
+
+ /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
+ tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
+ LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
+ LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
+ tsc_writel(tsc, LPC32XX_TSC_CON, tmp);
+
+ /* These values are all preset */
+ tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
+ tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
+
+ /* Aux support is not used */
+ tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0);
+ tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0);
+ tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0);
+
+ /*
+ * Set sample rate to about 240Hz per X/Y pair. A single measurement
+ * consists of 4 pairs which gives about a 60Hz sample rate based on
+ * a stable 32768Hz clock source. Values are in clocks.
+ * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
+ */
+ tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2);
+ tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2);
+ tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10);
+ tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4);
+ tsc_writel(tsc, LPC32XX_TSC_UTR, 88);
+
+ lpc32xx_fifo_clear(tsc);
+
+ /* Enable automatic ts event capture */
+ tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
+}
+
+static int lpc32xx_ts_open(struct input_dev *dev)
+{
+ struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+ lpc32xx_setup_tsc(tsc);
+
+ return 0;
+}
+
+static void lpc32xx_ts_close(struct input_dev *dev)
+{
+ struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+ lpc32xx_stop_tsc(tsc);
+}
+
+static int __devinit lpc32xx_ts_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_tsc *tsc;
+ struct input_dev *input;
+ struct resource *res;
+ resource_size_t size;
+ int irq;
+ int error;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Can't get memory resource\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Can't get interrupt resource\n");
+ return irq;
+ }
+
+ tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!tsc || !input) {
+ dev_err(&pdev->dev, "failed allocating memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsc->dev = input;
+ tsc->irq = irq;
+
+ size = resource_size(res);
+
+ if (!request_mem_region(res->start, size, pdev->name)) {
+ dev_err(&pdev->dev, "TSC registers are not free\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ tsc->tsc_base = ioremap(res->start, size);
+ if (!tsc->tsc_base) {
+ dev_err(&pdev->dev, "Can't map memory\n");
+ error = -ENOMEM;
+ goto err_release_mem;
+ }
+
+ tsc->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tsc->clk)) {
+ dev_err(&pdev->dev, "failed getting clock\n");
+ error = PTR_ERR(tsc->clk);
+ goto err_unmap;
+ }
+
+ input->name = MOD_NAME;
+ input->phys = "lpc32xx/input0";
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0002;
+ input->id.version = 0x0100;
+ input->dev.parent = &pdev->dev;
+ input->open = lpc32xx_ts_open;
+ input->close = lpc32xx_ts_close;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,
+ LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+ input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,
+ LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+
+ input_set_drvdata(input, tsc);
+
+ error = request_irq(tsc->irq, lpc32xx_ts_interrupt,
+ 0, pdev->name, tsc);
+ if (error) {
+ dev_err(&pdev->dev, "failed requesting interrupt\n");
+ goto err_put_clock;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "failed registering input device\n");
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, tsc);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+err_free_irq:
+ free_irq(tsc->irq, tsc);
+err_put_clock:
+ clk_put(tsc->clk);
+err_unmap:
+ iounmap(tsc->tsc_base);
+err_release_mem:
+ release_mem_region(res->start, size);
+err_free_mem:
+ input_free_device(input);
+ kfree(tsc);
+
+ return error;
+}
+
+static int __devexit lpc32xx_ts_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ device_init_wakeup(&pdev->dev, 0);
+ free_irq(tsc->irq, tsc);
+
+ input_unregister_device(tsc->dev);
+
+ clk_put(tsc->clk);
+
+ iounmap(tsc->tsc_base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(tsc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_ts_suspend(struct device *dev)
+{
+ struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+ struct input_dev *input = tsc->dev;
+
+ /*
+ * Suspend and resume can be called when the device hasn't been
+ * enabled. If there are no users that have the device open, then
+ * avoid calling the TSC stop and start functions as the TSC
+ * isn't yet clocked.
+ */
+ mutex_lock(&input->mutex);
+
+ if (input->users) {
+ if (device_may_wakeup(dev))
+ enable_irq_wake(tsc->irq);
+ else
+ lpc32xx_stop_tsc(tsc);
+ }
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static int lpc32xx_ts_resume(struct device *dev)
+{
+ struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+ struct input_dev *input = tsc->dev;
+
+ mutex_lock(&input->mutex);
+
+ if (input->users) {
+ if (device_may_wakeup(dev))
+ disable_irq_wake(tsc->irq);
+ else
+ lpc32xx_setup_tsc(tsc);
+ }
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops lpc32xx_ts_pm_ops = {
+ .suspend = lpc32xx_ts_suspend,
+ .resume = lpc32xx_ts_resume,
+};
+#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops)
+#else
+#define LPC32XX_TS_PM_OPS NULL
+#endif
+
+static struct platform_driver lpc32xx_ts_driver = {
+ .probe = lpc32xx_ts_probe,
+ .remove = __devexit_p(lpc32xx_ts_remove),
+ .driver = {
+ .name = MOD_NAME,
+ .owner = THIS_MODULE,
+ .pm = LPC32XX_TS_PM_OPS,
+ },
+};
+module_platform_driver(lpc32xx_ts_driver);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");
+MODULE_DESCRIPTION("LPC32XX TSC Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lpc32xx_ts");
diff --git a/drivers/input/touchscreen/lw86x0_ts/Kconfig b/drivers/input/touchscreen/lw86x0_ts/Kconfig
new file mode 100755
index 00000000..34cf42cb
--- /dev/null
+++ b/drivers/input/touchscreen/lw86x0_ts/Kconfig
@@ -0,0 +1,11 @@
+config TOUCHSCREEN_LW86X0
+ tristate "LW86X0 Touchscreen Driver"
+ default y
+ depends on ARCH_WMT
+ ---help---
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+ If unsure, say N.
+ To compile this driver as a module, choose M here: the
+ module will be called lw86x0_ts.
+
diff --git a/drivers/input/touchscreen/lw86x0_ts/Makefile b/drivers/input/touchscreen/lw86x0_ts/Makefile
new file mode 100755
index 00000000..a7cbba75
--- /dev/null
+++ b/drivers/input/touchscreen/lw86x0_ts/Makefile
@@ -0,0 +1,34 @@
+#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_lw86x0
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := lw86x0_ts.o wmt_ts.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
+
diff --git a/drivers/input/touchscreen/lw86x0_ts/lw86x0_ts.c b/drivers/input/touchscreen/lw86x0_ts/lw86x0_ts.c
new file mode 100755
index 00000000..ce741c9c
--- /dev/null
+++ b/drivers/input/touchscreen/lw86x0_ts/lw86x0_ts.c
@@ -0,0 +1,1321 @@
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+//#include <linux/earlysuspend.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#include <linux/pm_runtime.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/input/mt.h>
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <mach/hardware.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/async.h>
+#include <linux/wait.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/power/wmt_battery.h>
+#include "../../../video/backlight/wmt_bl.h"
+#include "lw86x0_ts.h"
+#include "wmt_ts.h"
+//#include "wmt_custom_lw86x0.h"
+
+#define TIME_CHECK_CHARGE 3000
+
+#define MAX_MULTI_DATA_SIZE 256
+
+#define HDMI_BASE_ADDR (HDMI_TRANSMITTE_BASE_ADDR + 0xC000)
+#define REG_HDMI_HOTPLUG_DETECT (HDMI_BASE_ADDR + 0x3ec)
+
+struct i2c_client *lw_i2c_client = NULL;
+struct i2c_client *client;//add by jackie
+extern char g_dbgmode;
+extern int COL_NUM;
+extern int ROW_NUM;
+extern int SKIP_ZERO_POINT;
+
+struct wmtts_device lw86x0_tsdev;
+static int tsirq_gpio;
+
+static int skip_zero_num = 0;
+
+u16 mcu_status_old = 0xffff;
+u16 mcu_status = 0xffff;
+
+typedef struct Fw_Version{ //add by jackie
+ u8 magic_num1;
+ u8 magic_num2;
+ u8 mj_ver;
+ u8 mn_ver;
+}Fw_Ver;//add by jackie
+
+//struct for report touch info
+struct ts_event {
+ u16 x[SUPPORT_POINT_NUM_MAX];//point x
+ u16 y[SUPPORT_POINT_NUM_MAX];//point y
+ u16 pressure[SUPPORT_POINT_NUM_MAX];//point pressure
+ u8 touch_point;//touch point number
+};
+
+struct lw86x0_ts_data {
+ struct input_dev *input_dev;
+ struct ts_event event;
+ struct work_struct touch_event_work;
+ struct workqueue_struct *ts_workqueue;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+}l_tsdata;
+
+static struct mutex ts_data_mutex;
+static int l_powermode = -1;
+static int l_hdmimode = -1;
+
+static int l_keylen = 4;
+static int l_baseaxis = 1; //0:x-axis,1:y-axis
+int l_keypos[TS_KEY_NUM+1][2];
+
+unsigned int l_tskey[TS_KEY_NUM][2] = {
+ {0,KEY_MENU},
+ {0,KEY_HOME},
+ {0,KEY_BACK},
+ {0,KEY_SEARCH},
+};
+static int l_early_suspend = 0; // 1:the early suspend function has been excuted
+
+static int stop_timer = 0;
+struct work_struct phone_status_work;
+struct timer_list polling_phone_status_timer;
+static int check_chip_status(void);
+static int first_init_reg = 1;
+static u16 auto_coff_value[20] = {0};
+//static finger_up_status = 1;
+
+u8 get_fw_file_check_sum(void);
+u16 get_fw_check_sum(void);
+
+extern int register_bl_notifier(struct notifier_block *nb);
+
+extern int unregister_bl_notifier(struct notifier_block *nb);
+//static struct ts_event old_event;
+
+void swap_byte_in_buffer(u16* buf, int count )
+{
+ int i;
+ for(i = 0; i < count; i++ )
+ {
+ buf[i] = swap16(buf[i]);
+ }
+}
+
+/**
+** for read register
+** rxbuf:read value
+** txdata:read register address
+** rxlength:read value length
+**/
+
+static int lw86x0_i2c_rxdata(char *rxbuf, char*txdata, int rxlength)
+{
+ int ret;
+ //int reg;//add jackie
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = lw_i2c_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = txdata,
+ },
+ {
+ .addr = lw_i2c_client->addr,
+ .flags = I2C_M_RD,
+ .len = rxlength,
+ .buf = rxbuf,
+ },
+ };
+
+ //ret = wmt_i2c_xfer_continue_if_4(msgs, 2, 1);
+
+ ret = i2c_transfer(lw_i2c_client->adapter, &msgs[0], 2);//add by jackie
+ if (ret != 2)
+ {
+ dbg("msg i2c rxdata error: %d\n", ret);
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+
+#if 0
+ struct i2c_msg xfer_msg[2];
+ if (reg < 0x80) {
+ i2c_transfer(client->adapter, xfer_msg, ARRAY_SIZE(xfer_msg));
+ msleep(5);
+ }
+ return i2c_transfer(client->adapter, xfer_msg, ARRAY_SIZE(xfer_msg)) == ARRAY_SIZE(xfer_msg) ? 0 : -EFAULT;
+#endif
+
+}
+
+/**
+** for write register
+** txdata:register address and value u8
+** length:txdata length
+**/
+
+static int lw86x0_i2c_txdata(char *txdata, int length)
+{
+ int ret;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = lw_i2c_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+
+ ret = i2c_transfer(lw_i2c_client->adapter, &msg[0], 1);//1
+
+ //ret = wmt_i2c_xfer_continue_if_4(msg, 1, 1);
+ if (ret != 1)
+ {
+ dbg("i2c txdata error: %d\n", ret);
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+
+}
+
+/**
+** Interface write register for other functions
+** addr:write register address
+** value:write register value
+**/
+
+int lw86x0_write_reg(u16 addr, u16 value)
+{
+ u8 buf[4];
+ int ret = -1;
+ unsigned char * pVal = (unsigned char *) &value;
+ unsigned char * pOffset = (unsigned char *) &addr;
+ buf[0] = pOffset[1];
+ buf[1] = pOffset[0];
+ buf[2] = pVal[1];
+ buf[3] = pVal[0];
+ ret = lw86x0_i2c_txdata(buf, 4);
+ if (ret < 0)
+ {
+ dbg("lw86x0_write_reg error: %d\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+/*int lw86x0_write_reg_multi(u16 start_addr, u16 value[], u16 num)
+{
+ u8 buf[MAX_MULTI_DATA_SIZE];
+ int ret = -1;
+ int i = 0;
+ unsigned char * pVal = (unsigned char *) &value[0];
+ unsigned char * pOffset = (unsigned char *) &addr;
+ buf[0] = pOffset[1];
+ buf[1] = pOffset[0];
+ //buf[2] = pVal[1];
+ //buf[3] = pVal[0];
+ for(i = 0; i < num; i++)
+ {
+ pVal = (unsigned char *) &value[i];
+ buf[2*i + 2] = pVal[1];
+ buf[2*i + 3] = pVal[0];
+ }
+
+ ret = lw86x0_i2c_txdata(buf, num*2+2);
+
+ if (ret < 0)
+ {
+ dbg("lw86x0_write_reg error: %d\n", ret);
+ return -1;
+ }
+ return 0;
+}*/
+/**
+** Interface read register for other functions
+** addr:read register address
+** pdata:read register value
+** regcnt:read register count
+**/
+
+int lw86x0_read_reg(u16 addr, u16 *pdata, int regcnt)
+{
+ int ret;
+
+ u16 offset_reverse = swap16(addr);
+ ret = lw86x0_i2c_rxdata((char*)pdata, (char*)&offset_reverse, 2*regcnt);
+
+ if (ret < 0)
+ {
+ dbg("lw86x0_read_reg error: %d\n", ret);
+ return -1;
+ }
+ else
+ {
+ swap_byte_in_buffer(pdata, regcnt);
+ return 0;
+ }
+}
+
+int wmt_ts_load_firmware(char* firmwarename, unsigned char** firmdata, int* fwlen)
+{
+ int i;
+ const struct firmware *fw_entry;
+ for (i = 0; i < 3; i++) {
+ if(request_firmware(&fw_entry, firmwarename, &lw_i2c_client->dev)!=0)
+ printk(KERN_ERR "cat't request firmware #%d\n", i);
+ else
+ break;
+ }
+ if (i == 3)
+ return -EINVAL;
+
+ if (fw_entry->size <= 0) {
+ printk(KERN_ERR "load firmware error\n");
+ release_firmware(fw_entry);
+ return -1;
+ }
+
+ *firmdata = kzalloc(fw_entry->size + 1, GFP_KERNEL);
+ memcpy(*firmdata, fw_entry->data, fw_entry->size);
+ *fwlen = fw_entry->size;
+ release_firmware(fw_entry);
+
+ return 0;
+}
+
+static u16 *default_setting_table;
+static int cfg_len;
+
+static int load_cfgfile(void)
+{
+ u32 val[2];
+ u16 temp[200];
+ int i = 0;
+ char cfgname[32] = {0};
+ u8 *pData;
+ int fileLen;
+ char *p;
+ char *s;
+
+ wmt_ts_get_configfilename(cfgname);
+ if (wmt_ts_load_firmware(cfgname, &pData, &fileLen)) {
+ errlog("Load config file failed~ \n");
+ return -1;
+ }
+ s = pData;
+ p = strstr(s, "COL_NUM");
+ sscanf(p, "COL_NUM=%d;", &COL_NUM);
+ p = strstr(s, "ROW_NUM");
+ sscanf(p, "ROW_NUM=%d;", &ROW_NUM);
+ p = strstr(s, "SKIP_ZERO_POINT");
+ sscanf(p, "SKIP_ZERO_POINT=%d;", &SKIP_ZERO_POINT);
+ dbg("COL_NUM=%d;ROW_NUM=%d;SKIP_ZERO_POINT=%d;",COL_NUM,ROW_NUM,SKIP_ZERO_POINT);
+
+ p = pData;
+ while (*p != '{') {
+ p++;
+ if(*p == '\0') {
+ errlog("Bad config file\n");
+ i = -1;
+ goto end;
+ }
+ }
+ while (*p != '}') {
+ if (!strncmp(p, "0x", 2)) {
+ i++;
+ if ((i & 0x0001) != 0) {
+ sscanf(p, "0x%x,0x%x,", val, val+1);
+ temp[i-1] = val[0] & 0x0000FFFF;
+ temp[i] = val[1] & 0x0000FFFF;
+ }
+ }
+ p++;
+ if(*p == '\0') {
+ i = -1;
+ errlog("Bad config file\n");
+ goto end;
+ }
+ };
+
+ dbg("the number of data:0x%x\n", i);
+ default_setting_table = kzalloc(i*2, GFP_KERNEL);
+ memcpy(default_setting_table, temp, i*2);
+ cfg_len = i;
+
+ dbg("paring config file end.\n");
+end:
+ kfree(pData);
+ return i;
+}
+
+void lw86x0_stop_timer(int flags)
+{
+ stop_timer = flags;
+}
+
+
+static u16 get_trim_info(void)
+{
+ u16 trim_info = 0;
+ u8 buf[2] = {0};
+ lw86x0_write_reg(0x00e4, 0x0000);
+ lw86x0_write_reg(0x00e2, 0x0302);
+ lw86x0_write_reg(0x00e3, 0x0000);
+ lw86x0_write_reg(0x00e2, 0x034e);
+ lw86x0_write_reg(0x00e2, 0x0302);
+ lw86x0_read_reg(0x00e4, buf, 1);
+ lw86x0_write_reg(0x00e2, 0x0000);
+ trim_info = buf[1];
+ dbg("trim info is %04x",trim_info);
+ return trim_info;
+}
+
+/**
+** load default register setting
+**/
+
+void lw86x0_load_def_setting(void)
+{
+ int i = 0;
+ u16 trim_value = 0;
+ u16 trim_info = 0;
+
+ lw86x0_write_reg(0x00e6, 0x3311);
+ trim_info = get_trim_info();
+ for(i = 0; i < cfg_len / 2; i++)
+ {
+ if(default_setting_table[2*i] == 0xffff)
+ {
+ msleep(default_setting_table[2*i+1]);
+ }
+ else
+ {
+ if(default_setting_table[2*i] == 0x00ee)
+ {
+ lw86x0_read_reg(0x00ee, &trim_value, 1);
+ if(trim_value == 0x00a0)
+ {
+ trim_value = 0x00c0 + trim_info;
+ }
+ else
+ {
+ trim_value = 0x100 + trim_value;
+ }
+ lw86x0_write_reg(0x00ee, trim_value);
+ }
+ else
+ {
+ lw86x0_write_reg(default_setting_table[2*i], default_setting_table[2*i+1]);
+ }
+ //lw86x0_write_reg(default_setting_table[2*i], default_setting_table[2*i+1]);
+ }
+ /*if(i == 0)
+ {
+ msleep(100);
+ }*/
+ }
+ if(first_init_reg == 1)
+ {
+ for(i = 0; i < 19; i++)
+ {
+ lw86x0_read_reg(0x0092+i, &auto_coff_value[i], 1);
+ }
+ first_init_reg = 0;
+ }
+ else
+ {
+ lw86x0_write_reg(0x0035, 0x0070);
+ lw86x0_write_reg(0x0060, 0x0307);
+ lw86x0_write_reg(0x0091, 0x0200);
+ for(i = 0; i < 19; i++)
+ {
+ lw86x0_write_reg(0x0092+i, auto_coff_value[i]);
+ }
+ lw86x0_write_reg(0x0035, 0x2070);
+ msleep(100);
+ lw86x0_write_reg(0x0060, 0x0306);
+ }
+}
+
+/**
+** set reset pin for lw86x0
+**/
+
+static void lw86x0_hw_reset(void)
+{
+ wmt_rst_output(0);
+ //msleep(500);
+ msleep(30);
+ wmt_rst_output(1);
+}
+
+static void lw86x0_ts_release(void)
+{
+ int i = 0;
+ struct lw86x0_ts_data *data = &l_tsdata;
+ int down = 0;
+
+ // dbg("lw86x0_ts_release");
+
+ for (i = 0; i < l_keylen; i++)
+ {
+ down |= l_tskey[i][0];
+ }
+ if (down != 0)
+ {
+ // if down clear the flag
+ for ( i = 0; i < l_keylen; i++)
+ {
+ l_tskey[i][0] = 0;
+ };
+ //dbg("key up!\n");
+ if (wmt_ts_enable_keyled())
+ wmt_ts_turnoff_light();
+ }
+ else
+ {
+ if (!lw86x0_tsdev.penup)
+ {
+ input_mt_sync(data->input_dev);
+ input_sync(data->input_dev);
+ //dbg("rpt pen\n");
+ }
+ lw86x0_tsdev.penup = 1;
+ //dbg("pen up\n");
+ //wake_up(&ts_penup_wait_queue);
+ }
+}
+
+/**
+**set wmt touch key count
+**/
+
+void wmt_ts_set_keylen(int keylen)
+{
+ l_keylen = keylen;
+}
+
+/**
+**set wmt touch baseaxis
+**axis:0--x axis,1--y axis.
+**/
+
+void wmt_ts_set_baseaxis(int axis)
+{
+ l_baseaxis = axis;
+}
+
+/**
+** set wmt touch key info struct keypos
+** index:set key number
+** min:key min point value
+** max:key max point value
+**/
+
+void wmt_ts_set_keypos(int index, int min,int max)
+{
+ l_keypos[index][0] = min;
+ l_keypos[index][1] = max;
+}
+
+/**
+** report key info to wmt android
+**/
+#if 0
+
+static int lw86x0_report_key_info(void)
+{
+ struct lw86x0_ts_data *data = &l_tsdata;
+ u16 x, y;
+ u16 key_stpos,key_vrpos; // the stable and variable position for touch key
+ int i =0;
+ lw86x0_read_reg(0x0161, &x, 1);
+ lw86x0_read_reg(0x016B, &y, 1);
+ if (wmt_ts_enable_tskey() != 0)
+ {
+ switch (l_baseaxis)
+ {
+ case 0:
+ key_stpos = y;
+ key_vrpos = x;
+ break;
+ case 1:
+ default:
+ key_stpos = x;
+ key_vrpos = y;
+ break;
+ }
+ }
+ for (i=0;i < l_keylen;i++)
+ {
+ if ((key_vrpos>=l_keypos[i][0]) && (key_vrpos<=l_keypos[i][1]))
+ {
+ // report the key
+ if (0 == l_tskey[i][0])
+ {
+ input_report_key(data->input_dev, l_tskey[i][1], 1);
+ input_report_key(data->input_dev, l_tskey[i][1], 0);
+ input_sync(data->input_dev);
+ l_tskey[i][0] = 1;
+ dbg("report tskey:%d\n",i);
+ if (wmt_ts_enable_keyled())
+ wmt_ts_turnon_light();
+ }
+ return 1;//key
+ }
+ }
+ return 0;//no key
+
+}
+#endif
+
+static void check_mode(void)
+{
+ int dcin = wmt_charger_is_dc_plugin();
+ int hdmiin = (REG32_VAL(REG_HDMI_HOTPLUG_DETECT) & BIT31) >> 31;
+
+ if (dcin == l_powermode && hdmiin == l_hdmimode)
+ return;
+ if (!dcin && !hdmiin) {
+ klog("DC and HDMI removed\n");
+ lw86x0_write_reg(0x01e9, 0x0000);
+ } else {
+ klog("DC or HDMI in\n");
+ lw86x0_write_reg(0x01e9, 0x0001);
+ }
+ l_powermode = dcin;
+ l_hdmimode = hdmiin;
+}
+
+/**
+** report touch info to wmt android
+** touch_number: touch count
+**/
+
+static void lw86x0_report_touch_info(u16 touch_number)
+{
+ struct lw86x0_ts_data *data = &l_tsdata;
+ struct ts_event *event = &data->event;
+ u16 i;
+
+ //old_event = *event;
+ //dbg("Enter into lw86x0_report_touch_info");
+ check_mode();
+ if(touch_number == 0)
+ {
+ input_mt_sync(data->input_dev);
+ input_sync(data->input_dev);
+ return;
+ }
+ if(touch_number> wmt_ts_get_fingernum()){
+ //dbg("Invalid Touch point count is found %d",touch_number);
+ return;
+ }
+ event->touch_point = touch_number;
+
+ //memset(event->x, 0, SUPPORT_POINT_NUM*sizeof(u16) );
+ //memset(event->y, 0, SUPPORT_POINT_NUM*sizeof(u16) );
+ //memset(event->pressure, 0, SUPPORT_POINT_NUM*sizeof(u16) );
+ for( i = 0; i <touch_number; i++ )
+ {
+ lw86x0_read_reg(0x0161+i, &event->x[i], 1);
+ lw86x0_read_reg(0x016B+i, &event->y[i], 1);
+ lw86x0_read_reg(0x0175+i, &event->pressure[i], 1);
+ }
+
+ for (i = 0; i < touch_number; i++)
+ {
+ int x = (event->x[i]) & 0x03ff;
+ int y = (event->y[i]) & 0x03ff;
+ int id = ((event->x[i])>>12)&0x000f;
+ int tmp;
+
+ if(x>wmt_ts_get_resolvX())
+ {
+ x = wmt_ts_get_resolvX();
+ }
+
+ if(y>wmt_ts_get_resolvY())
+ {
+ y= wmt_ts_get_resolvY();
+ }
+
+ if (wmt_ts_get_xaxis()) {
+ tmp = x;
+ x = y;
+ y = tmp;
+ }
+ if (wmt_ts_get_xdir())
+ x = wmt_ts_get_resolvX() - x;
+ if (wmt_ts_get_ydir())
+ y = wmt_ts_get_resolvY() - y;
+
+ if (wmt_ts_get_lcdexchg()) {
+ int tmp;
+ tmp = x;
+ x = y;
+ y = wmt_ts_get_resolvX() - tmp;
+ }
+
+ dbg("id %d [%d, %d] p %d",id, x, y, event->pressure[i]);
+ //input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,event->pressure[i]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, id);
+ input_mt_sync(data->input_dev);
+ }
+ /* SYN_REPORT */
+ input_sync(data->input_dev);
+}
+
+/**
+**lw86x0 touch irq work function
+**/
+
+static void lw86x0_ts_touch_irq_work(struct work_struct *work)
+{
+
+ u16 int_touch_status=0;
+ mutex_lock(&ts_data_mutex);
+
+ //dbg("Enter into lw86x0_ts_touch_irq_work");
+
+ //finger_up_status = 0;
+ lw86x0_read_reg(0x01f5, &int_touch_status, 1);
+
+ //dbg("Read 0x1f5 = %d",int_touch_status);
+ if( int_touch_status & 0x0001)
+ {
+ u16 touch_number=0;
+ lw86x0_read_reg(0x0160, &touch_number, 1);
+ //dbg("tn=%d\n",touch_number);
+ if(touch_number==0)
+ {
+ skip_zero_num++;
+ if(SKIP_ZERO_POINT==skip_zero_num)
+ {
+ dbg("tn=%d\n",touch_number);
+ lw86x0_write_reg(0x01f2, 0x0010);
+ lw86x0_report_touch_info(touch_number);
+ lw86x0_ts_release();
+ //finger_up_status = 1;
+ }
+ else if(SKIP_ZERO_POINT<skip_zero_num)
+ {
+ skip_zero_num = SKIP_ZERO_POINT+1;
+ }
+ }
+ else if(touch_number==15)
+ {
+ //dbg("touch_number=%d\n",touch_number);
+ }
+ else
+ {
+ dbg("tn=%d\n",touch_number);
+ lw86x0_write_reg(0x01f2, 0x0011);
+ skip_zero_num = 0;
+ lw86x0_report_touch_info(touch_number);
+ }
+ }
+ else
+ {
+ //finger_up_status = 1;
+ }
+ lw86x0_write_reg(0x01f5, 0xffff);//clear interrupt
+ //mdelay(500);
+ //dbg("clear interrupt 1");
+ lw86x0_write_reg(0x01f5, 0xffff);//clear interrupt
+ //mdelay(500);
+ //dbg("clear interrupt 2");
+ //lw86x0_write_reg(0x01f5, 0xffff);//clear interrupt
+ //mdelay(500);
+ //dbg("clear interrupt 3");
+ //lw86x0_write_reg(0x01f5, 0xffff);//clear interrupt
+ //mdelay(500);
+ //dbg("clear interrupt 4");
+ //dbg("Write 0x1f5 = 0xffff");
+ //lw86x0_read_reg(0x01f5, &int_touch_status, 1);
+ //dbg("Re-Read 0x1f5 = %d",int_touch_status);
+
+ if(g_dbgmode==0)
+ {
+ //dbg("Enable Irq");
+ wmt_enable_gpirq(tsirq_gpio);
+ }
+ mutex_unlock(&ts_data_mutex);
+}
+
+static irqreturn_t lw86x0_ts_interrupt(int irq, void *dev_id)
+{
+ //dbg("enter lw86x0_ts_interrupt");
+ //if (!wmt_is_tsirq_enable(tsirq_gpio))
+ //{
+ // dbg("tsirq not enabled");
+ // return IRQ_NONE;
+ //}
+ if (wmt_is_tsint(tsirq_gpio))
+ {
+ wmt_clr_int(tsirq_gpio);
+ wmt_disable_gpirq(tsirq_gpio);
+ if(!l_early_suspend)
+ {
+ //dbg("tsirq enabled");
+ queue_work(l_tsdata.ts_workqueue, &l_tsdata.touch_event_work);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static void reset_chip(void)
+{
+ printk("\nReset LW IC\n\n");
+ lw86x0_write_reg(0x00e6, 0x3311);
+ lw86x0_write_reg(0x00e0, 0x0005);
+ lw86x0_write_reg(0x0214, 0x0020);
+ lw86x0_write_reg(0x033d, 0x8100);
+ mdelay(500);
+ wmt_rst_output(0);
+ wmt_set_irq_mode(tsirq_gpio, 0);
+ mdelay(100);
+ wmt_rst_output(1);
+ wmt_set_gpirq(tsirq_gpio, GIRQ_FALLING);
+ lw86x0_load_def_setting();
+ if(g_dbgmode==0)
+ {
+ wmt_enable_gpirq(tsirq_gpio);
+ }
+}
+
+
+static int check_chip_status(void)
+{
+ u16 read_value = 0;
+ u16 read_sram = 0;
+ int ret = lw86x0_read_reg(0x00e6, &read_value, 1);
+ if(ret != 0)
+ {
+ reset_chip();
+ return 0;
+ }
+ if(read_value != 0x3311)
+ {
+ reset_chip();
+ return 0;
+ }
+ else
+ {
+ lw86x0_read_reg(0x00e0, &read_value, 1);
+ if(read_value != 0x0005 && read_value != 0x000d)
+ {
+ dbg("0x00e0!=0x0005,0x000d\n");
+ reset_chip();
+ return 0;
+ }
+ lw86x0_read_reg(0x0180, &read_sram, 1);
+ if(read_sram != 0)
+ {
+ dbg("0x0180!=0\n");
+ reset_chip();
+ return 0;
+ }
+ lw86x0_read_reg(0x0181, &mcu_status, 1);
+ if(mcu_status_old == mcu_status)
+ {
+ dbg("0x0180 old!=new\n");
+ reset_chip();
+ mcu_status_old = mcu_status;
+ return 0;
+ }
+ else
+ {
+ mcu_status_old = mcu_status;
+ return 1;
+ }
+ }
+ return 1;
+}
+
+static void phone_status_listener(struct work_struct *work)
+{
+ if(stop_timer == 0)
+ {
+ check_chip_status();
+ }
+}
+
+static void lw86x0_ts_polling_phone_status(long unsigned int dev_addr)
+{
+ schedule_work(&phone_status_work);
+ mod_timer(&polling_phone_status_timer, jiffies + msecs_to_jiffies(2000));
+}
+
+/**
+** lw86x0 ts early suspend function
+**/
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ts_early_suspend(void)
+{
+ wmt_disable_gpirq(tsirq_gpio);
+}
+
+//#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void lw86x0_ts_early_suspend(struct early_suspend *handler)
+{
+ printk("lw86x0_ts_early_suspend\n");
+ ts_early_suspend();
+ l_early_suspend = 1;
+ lw86x0_write_reg(0x000c, 0xffff);
+ lw86x0_write_reg(0x033d, 0x0d60);
+ lw86x0_write_reg(0x00e2, 0x0300);
+ lw86x0_write_reg(0x000d, 0x4000);
+ lw86x0_write_reg(0x00e5, 0x4c01);
+ stop_timer = 1;
+}
+
+/**
+** lw86x0 late resume function
+**/
+static void ts_late_resume(void)
+{
+ printk("ts_late_resume\n");
+ //wmt_disable_gpirq(tsirq_gpio);
+ //lw86x0_hw_reset();
+ l_early_suspend = 0;
+
+ wmt_set_gpirq(tsirq_gpio, GIRQ_FALLING);
+ if(g_dbgmode==0)
+ {
+ printk("g_dbgmode==0\n");
+ wmt_enable_gpirq(tsirq_gpio);
+ }
+}
+
+
+static void lw86x0_ts_late_resume(struct early_suspend *handler)
+{
+ printk("==lw86x0_ts_resume=\n");
+ int ret = check_chip_status();
+ if(ret == 1)
+ {
+ lw86x0_write_reg(0x000d, 0xc000);
+ lw86x0_write_reg(0x00e2, 0x0100);
+ lw86x0_write_reg(0x00e5, 0x4c00);
+ lw86x0_write_reg(0x000c, 0xffff);
+ }
+ ts_late_resume();
+ l_early_suspend = 0;
+ stop_timer = 0;
+}
+#endif
+
+/**
+** lw86x0 ts suspend function
+**/
+
+static int lw86x0_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+/**
+** lw86x0 resume function
+**/
+
+static int lw86x0_resume(struct platform_device *pdev)
+{
+ lw86x0_hw_reset();
+ first_init_reg = 1;
+ lw86x0_load_def_setting();
+ return 0;
+}
+
+#ifdef SUPPORT_FW_UPGRADE
+//add by jackie
+#define BYTES_PER_PACKET 64
+//#define FILEPATH "/etc/firmware/TOUCH.BIN"
+int fileLen;
+u8 *pData;
+
+void read_data_from_fw_file(void)
+{
+ char fwname[32] = {0};
+ wmt_ts_get_firmwfilename(fwname);
+ if (wmt_ts_load_firmware(fwname, &pData, &fileLen)) {
+ dbg("Load firmware file failed~ \n");
+ return;
+ }
+}
+
+u8 check_fw_version(void)
+{
+ //Fw_Ver* pfwVerInFile = NULL;
+ //Fw_Ver fwVer;
+
+ u16 fw_check_sum;
+ u16 fw_file_check_sum = 0;
+
+ read_data_from_fw_file();
+ /*pfwVerInFile = (Fw_Ver* )&(pData[0x2000]);
+
+ printk("struct data:%c%c%d%d\n",pfwVerInFile->magic_num1,pfwVerInFile->magic_num2,pfwVerInFile->mj_ver,pfwVerInFile->mn_ver);//add by jackie
+ lw86x0_write_reg(0x00e6,0x3311);
+ lw86x0_flash_read((u8*)&fwVer,0x2000,4);
+ printk("lw86x0_flash:%c%c%d%d\n",fwVer.magic_num1,fwVer.magic_num2,fwVer.mj_ver,fwVer.mn_ver);//add by jackie
+ //printk("lw86x0_flash:%d%d\n",fwVer.magic_num1,fwVer.magic_num2);//add by jackie
+ if((fwVer.magic_num1!='L'||fwVer.magic_num2!='W')
+ ||((fwVer.magic_num1=='L'&&fwVer.magic_num2=='W')
+ &&(pfwVerInFile->magic_num1=='L'&&pfwVerInFile->magic_num2=='W')
+ &&(fwVer.mj_ver!=pfwVerInFile->mj_ver || fwVer.mn_ver!=pfwVerInFile->mn_ver))
+ )*/
+ lw86x0_write_reg(0x00e6, 0x3311);
+ lw86x0_write_reg(0x0020, 0x9000);
+ lw86x0_write_reg(0x0002, 0x8900);
+ lw86x0_write_reg(0x0115, 0x0100);
+ lw86x0_write_reg(0x0020, 0x1000);
+ msleep(200);
+ fw_check_sum = get_fw_check_sum();
+ fw_file_check_sum = get_fw_file_check_sum();
+ printk("**********fw_check_sum = %04x, fw_file_check_sum = %04x\n",fw_check_sum,fw_file_check_sum);
+ if(((fw_check_sum&0xff00)!=0x8000)||((fw_check_sum&0x00ff)!=fw_file_check_sum))
+ {
+ lw86x0_write_reg(0x0002, 0x8800);
+ printk("firmware crc check is not equal, update firmware......\n");
+ return 1;//return 1 means needing upgrade
+ }
+ else
+ {
+ printk("firmware is not updated......\n");
+ lw86x0_write_reg(0x0002, 0x8800);
+ return 0;
+ }
+}
+
+void fw_download(void)
+{
+ int pkt_num = (fileLen+BYTES_PER_PACKET-1)/BYTES_PER_PACKET;
+ int i;
+ int last_pkt_size = ((int)fileLen) % BYTES_PER_PACKET;
+ printk("pkt_num is:%d\n",pkt_num);//add
+ if(last_pkt_size==0)
+ {
+ last_pkt_size = BYTES_PER_PACKET;
+ }
+ lw86x0_flash_write_prepare();
+ for(i=0;i<pkt_num;i++)
+ {
+ lw86x0_flash_write(&pData[i*BYTES_PER_PACKET],i*BYTES_PER_PACKET,(i==pkt_num-1)?last_pkt_size:BYTES_PER_PACKET);
+ }
+ lw86x0_flash_write_finish(fileLen);
+ printk("firmware is updated......\n");//add
+}
+
+
+u8 get_fw_file_check_sum(void)
+{
+ //u16 dataLen;
+ //u8* pData = NULL;
+ u16 i;
+ u8 checksum = 0;
+ printk("**********dataLen = %04x\n",fileLen);
+ for(i=0;i<fileLen;i++)
+ {
+ checksum+=pData[i];
+ }
+ return checksum;
+}
+
+u16 get_fw_check_sum(void)
+{
+ u8 cnt = 10;
+ u16 check_sum = 0;
+ //u16 fw_length = 0;
+ while(cnt>0)
+ {
+ lw86x0_read_reg(0x0182, &check_sum, 1);
+ printk("**********check_sum = %04x\n",check_sum);
+ if((check_sum&0xff00)==0x8000)
+ {
+ break;
+ }
+ cnt--;
+ msleep(100);
+ }
+ return check_sum;
+}
+
+static void fw_upgrader(void)
+{
+ u16 fw_check_sum;
+ u16 fw_file_check_sum = 0;
+
+ if(check_fw_version()==0)
+ {
+ return;
+ }
+
+ lw86x0_write_reg(0x00e6, 0x3311);
+ fw_download();
+ lw86x0_write_reg(0x0020, 0x9000);
+ lw86x0_write_reg(0x0002, 0x8900);
+ lw86x0_write_reg(0x0115, 0x0100);
+ lw86x0_write_reg(0x0020, 0x1000);
+ msleep(200);
+ fw_check_sum = get_fw_check_sum();
+ fw_file_check_sum = get_fw_file_check_sum();
+ printk("**********fw_check_sum = %04x, fw_file_check_sum = %04x\n",fw_check_sum,fw_file_check_sum);
+ if(((fw_check_sum&0xff00)!=0x8000)||((fw_check_sum&0x00ff)!=fw_file_check_sum))
+ {
+ printk("*********redownload fw\n");
+ fw_download();
+ lw86x0_write_reg(0x00e6, 0x3311);
+ lw86x0_write_reg(0x0020, 0x9000);
+ lw86x0_write_reg(0x0002, 0x8900);
+ lw86x0_write_reg(0x0115, 0x0100);
+ lw86x0_write_reg(0x0020, 0x1000);
+ msleep(200);
+ fw_check_sum = get_fw_check_sum();
+ fw_file_check_sum = get_fw_file_check_sum();
+ printk("**********re-check fw_check_sum = %04x, fw_file_check_sum = %04x\n",fw_check_sum,fw_file_check_sum);
+ if(((fw_check_sum&0xff00)!=0x8000)||((fw_check_sum&0x00ff)!=fw_file_check_sum))
+ {
+ lw86x0_flash_write_prepare();
+ }
+ }
+ else
+ {
+ }
+ lw86x0_write_reg(0x0002, 0x8800);
+ kfree(pData);
+ lw86x0_hw_reset();
+
+}
+
+#endif
+
+
+static int wmt_wakeup_bl_notify(struct notifier_block *nb, unsigned long event,
+ void *dummy)
+{
+ //printk("get notify\n");
+ switch (event) {
+ case BL_CLOSE:
+ l_early_suspend = 1;
+ wmt_disable_gpirq(tsirq_gpio);
+ stop_timer = 1;
+ cancel_work_sync(&l_tsdata.touch_event_work);
+ cancel_work_sync(&phone_status_work);
+ printk("\nclose backlight\n\n");
+ break;
+ case BL_OPEN:
+ l_early_suspend = 0;
+ wmt_enable_gpirq(tsirq_gpio);
+ lw86x0_write_reg(0x01f5,0xffff);//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ stop_timer = 0;
+ printk("\nopen backlight\n\n");
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wmt_bl_notify = {
+ .notifier_call = wmt_wakeup_bl_notify,
+};
+
+static int lw86x0_ts_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ int i = 0;
+ u16 read_from_e6 = 0;
+ lw_i2c_client = ts_get_i2c_client();//get i2c_client
+
+ memset(&l_tsdata, 0 ,sizeof(l_tsdata));
+ INIT_WORK(&l_tsdata.touch_event_work, lw86x0_ts_touch_irq_work);
+ mutex_init(&ts_data_mutex);
+
+ l_tsdata.ts_workqueue = create_singlethread_workqueue("lw86x0-ts-queue");
+ if (!l_tsdata.ts_workqueue) {
+ err = -ESRCH;
+ goto exit_create_singlethread;
+ }
+
+ l_tsdata.input_dev = input_allocate_device();
+ if (!l_tsdata.input_dev) {
+ err = -ENOMEM;
+ dbg("failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ l_tsdata.input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ l_tsdata.input_dev->propbit[0] = BIT_MASK(INPUT_PROP_DIRECT);
+
+ if (wmt_ts_get_lcdexchg()) {
+ input_set_abs_params(l_tsdata.input_dev,
+ ABS_MT_POSITION_X, 0, wmt_ts_get_resolvY(), 0, 0);
+ input_set_abs_params(l_tsdata.input_dev,
+ ABS_MT_POSITION_Y, 0, wmt_ts_get_resolvX(), 0, 0);
+ } else {
+ input_set_abs_params(l_tsdata.input_dev,
+ ABS_MT_POSITION_X, 0, wmt_ts_get_resolvX(), 0, 0);
+ input_set_abs_params(l_tsdata.input_dev,
+ ABS_MT_POSITION_Y, 0, wmt_ts_get_resolvY(), 0, 0);
+ }
+ input_set_abs_params(l_tsdata.input_dev,
+ ABS_MT_TRACKING_ID, 0, 15, 0, 0);
+
+ l_tsdata.input_dev->name = LW86X0_NAME;
+ for (i = 0; i < TS_KEY_NUM; i++)
+ {
+ set_bit(l_tskey[i][1], l_tsdata.input_dev->keybit);
+ };
+ err = input_register_device(l_tsdata.input_dev);
+ if (err) {
+ errlog("lw86x0_ts_probe: failed to register input device.");
+ goto exit_input_register_device_failed;
+ }
+
+#ifdef SUPPORT_FW_UPGRADE
+ fw_upgrader();
+ mdelay(500);
+#endif
+
+ err = load_cfgfile();
+ if (err < 0)
+ goto exit_load_cfgfile_failed;
+ lw86x0_load_def_setting();
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ l_tsdata.early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB+ 1;
+ l_tsdata.early_suspend.suspend = lw86x0_ts_early_suspend;
+ l_tsdata.early_suspend.resume = lw86x0_ts_late_resume;
+ register_early_suspend(&l_tsdata.early_suspend);
+#endif
+
+ // init interrupt gpio
+ tsirq_gpio = wmt_ts_get_gpionum();
+ wmt_set_gpirq(tsirq_gpio, GIRQ_FALLING);//GIRQ_FALLING);
+ wmt_disable_gpirq(tsirq_gpio);
+
+ if(request_irq(wmt_get_tsirqnum(), lw86x0_ts_interrupt, IRQF_SHARED, "ts_lw86x0", l_tsdata.input_dev) < 0){
+ errlog("Could not allocate intrrupt for ts_lw86x0 !\n");
+ err = -1;
+ goto exit_register_irq;
+ }
+ lw86x0_ts_touch_irq_work(&l_tsdata.touch_event_work);
+ if(g_dbgmode==0)
+ {
+ wmt_enable_gpirq(tsirq_gpio);
+ }
+ msleep(5);
+ dbg("irqgpio=%d,irq=%d,resetgpio=%d\n", tsirq_gpio, wmt_get_tsirqnum(),wmt_ts_get_resetgpnum());
+
+ lw86x0_read_reg(0x00e6, &read_from_e6, 1);
+ if(read_from_e6 == 0x3311 || read_from_e6 == 0xa311)
+ {
+ INIT_WORK(&phone_status_work, phone_status_listener);
+ init_timer(&polling_phone_status_timer);
+ setup_timer(&polling_phone_status_timer, lw86x0_ts_polling_phone_status, (long unsigned int) pdev);
+ lw86x0_ts_polling_phone_status((long unsigned int) pdev);
+ }
+
+ register_bl_notifier(&wmt_bl_notify);
+
+ return 0;
+exit_register_irq:
+#ifdef CONFIG_HAS_EARLYSUSPEND//add by jackie
+ unregister_early_suspend(&l_tsdata.early_suspend);
+#endif
+ kfree(default_setting_table);
+exit_load_cfgfile_failed:
+
+exit_input_register_device_failed:
+ input_free_device(l_tsdata.input_dev);
+exit_input_dev_alloc_failed:
+ cancel_work_sync(&l_tsdata.touch_event_work);
+ destroy_workqueue(l_tsdata.ts_workqueue);
+exit_create_singlethread:
+ return err;
+}
+
+static int lw86x0_ts_remove(struct platform_device *pdev)
+{
+ kfree(default_setting_table);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&l_tsdata.early_suspend);
+#endif
+ free_irq(wmt_get_tsirqnum(), l_tsdata.input_dev);
+ input_unregister_device(l_tsdata.input_dev);
+ flush_workqueue(l_tsdata.ts_workqueue);
+ cancel_work_sync(&l_tsdata.touch_event_work);
+ destroy_workqueue(l_tsdata.ts_workqueue);
+ mutex_destroy(&ts_data_mutex);
+ del_timer(&polling_phone_status_timer);
+ unregister_bl_notifier(&wmt_bl_notify);
+ dbg("remove...\n");
+ return 0;
+}
+
+
+static int lw86x0_ts_init(void)
+{
+ dbg("lw86x0_ts_init\n");
+ lw86x0_hw_reset();
+ return 0;
+}
+
+static void lw86x0_ts_exit(void)
+{
+ dbg("lw86x0_ts_exit\n");
+}
+
+struct wmtts_device lw86x0_tsdev = {
+ .driver_name = "s_lw86x0_ts",
+ .ts_id = "lw86x0",
+ .init = lw86x0_ts_init,
+ .exit = lw86x0_ts_exit,
+ .probe = lw86x0_ts_probe,
+ .remove = lw86x0_ts_remove,
+ .suspend = lw86x0_suspend,
+ .resume = lw86x0_resume,
+ .penup = 1,
+};
+
diff --git a/drivers/input/touchscreen/lw86x0_ts/lw86x0_ts.h b/drivers/input/touchscreen/lw86x0_ts/lw86x0_ts.h
new file mode 100755
index 00000000..cc2d9e26
--- /dev/null
+++ b/drivers/input/touchscreen/lw86x0_ts/lw86x0_ts.h
@@ -0,0 +1,53 @@
+#ifndef _LW86X0_TS_H_
+#define _LW86X0_TS_H_
+
+//#include "wmt_custom_lw86x0.h"
+
+// define byte swap of a WORD
+#define swap16(a) ((((a)&0xff)<<8)|(((a)>>8)&0xff))
+
+//struct _reg_word for ioctl read or write register
+#define LW86X0_NAME "touch_lw86x0"
+
+#define SUPPORT_FW_UPGRADE
+#define TS_KEY_NUM 4
+#define COL_NUM_MAX 28
+#define ROW_NUM_MAX 16
+#define SUPPORT_POINT_NUM_MAX 10
+#define MULTI_DATA_MAX_SIZE 49
+
+typedef struct _reg_word
+{
+ u16 uOffset;
+ u16 uValue;
+ u16 multi_data[MULTI_DATA_MAX_SIZE];
+ int data_size;
+}reg_word;
+
+//struct _flash_op for ioctl write or read frimware
+#define FLASH_XFER_PKT_SIZE 256
+typedef struct _flash_op
+{
+ u16 startaddr; //=0 if the first pkt
+ u16 lastpkt; // =1 if last pkt; =0, otherwise
+ u16 pktlen; //data length in this pkt
+ char data[FLASH_XFER_PKT_SIZE];
+}flash_op;
+
+//struct _raw_data for ioctl read cdc/amb/diff data
+typedef struct _raw_data
+{
+ u8 row;
+ u8 col;
+ u16 data[COL_NUM_MAX*ROW_NUM_MAX];
+}rawdata;
+
+extern void wmt_ts_set_keylen(int keylen);
+extern void wmt_ts_set_baseaxis(int axis);
+extern void wmt_ts_set_keypos(int index, int min,int max);
+extern int lw86x0_write_reg(u16 addr, u16 value);
+extern int lw86x0_read_reg(u16 addr, u16 *pdata, int regcnt);
+extern void getversion(void);
+extern void lw86x0_stop_timer(int flags);
+
+#endif
diff --git a/drivers/input/touchscreen/lw86x0_ts/wmt_ts.c b/drivers/input/touchscreen/lw86x0_ts/wmt_ts.c
new file mode 100755
index 00000000..505dedfd
--- /dev/null
+++ b/drivers/input/touchscreen/lw86x0_ts/wmt_ts.c
@@ -0,0 +1,1165 @@
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>//add
+#include "wmt_ts.h"
+#include "lw86x0_ts.h"
+//#include "wmt_custom_lw86x0.h"
+
+
+struct i2c_client *l_client = NULL;
+
+
+
+/////////////////////////////////////////////////////////////////
+
+// commands for ui
+#define TS_IOC_MAGIC 't'
+#define LW86X0_READ_REG _IOWR(TS_IOC_MAGIC, 1, int*)
+#define LW86X0_WRITE_REG _IOW(TS_IOC_MAGIC, 2, int*)
+#define LW86X0_FLASH_DOWNLOAD _IOW(TS_IOC_MAGIC, 3, int *)
+#define LW86X0_FLASH_UPLOAD _IOWR(TS_IOC_MAGIC, 4, int *)
+#define LW86X0_CTRL_DEBUG_MODE _IOW(TS_IOC_MAGIC, 5, int *)
+#define LW86X0_CTRL_RD_DIFF _IOR(TS_IOC_MAGIC, 6, int *)
+#define LW86X0_CTRL_RD_CDC _IOR(TS_IOC_MAGIC, 7, int *)
+#define LW86X0_CTRL_RD_AMB _IOR(TS_IOC_MAGIC, 8, int *)
+#define LW86X0_CTRL_STOP_TIMER _IOR(TS_IOC_MAGIC, 15, int *)
+#define TS_IOC_MAXNR 15
+
+//
+#define TS_MAJOR 11
+#define TS_DRIVER_NAME "wmtts_touch"
+#define TS_NAME "wmtts"
+#define WMTTS_PROC_NAME "wmtts_config"
+
+#define LIGHT_ON_WAIT_TIME 5000 // 5s
+
+#define EXT_GPIO0 0
+#define EXT_GPIO1 1
+#define EXT_GPIO2 2
+#define EXT_GPIO3 3
+#define EXT_GPIO4 4
+#define EXT_GPIO5 5
+#define EXT_GPIO6 6
+#define EXT_GPIO7 7
+
+struct touch_tp_info {
+ char name[64];
+ unsigned int i2caddr;
+ int xaxis; // 0:x,1:x swap with y
+ int xdir; // 1:positive,-1:revert
+ int ydir; // 1:positive,-1:revert
+ int finger_num;
+};
+
+
+static struct touch_tp_info l_tp[] = {
+ {"LW86X0",(0x30>>1), 0, 1, 1},
+};
+
+static int irq_gpio;
+static int rst_gpio;
+static int keyled_gpio = -1;
+static int light_level;
+static int light_time = 5000; // unit: ms
+static int panelres_x;
+static int panelres_y;
+static int lcd_exchg = 0;
+static DECLARE_WAIT_QUEUE_HEAD(queue);
+static TS_EVENT g_evLast;
+static struct mutex cal_mutex;
+
+static struct class* l_dev_class = NULL;
+static struct device *l_clsdevice = NULL;
+extern struct wmtts_device lw86x0_tsdev;
+static struct wmtts_device* l_tsdev = &lw86x0_tsdev;
+static unsigned char ts_i2c_addr = 0;
+
+static struct proc_dir_entry* l_tsproc = NULL;
+static struct timer_list l_lighttimer; // for shaking
+static int l_tskey_btn = 0; // zero to disable touch key, positive to support touch key
+
+#if 0
+//add
+struct tp_infor{
+
+char name[64];
+int i2caddr;
+int xaxis;
+int xdir;
+int ydir;
+int finger_num;
+};
+
+//add by jackie
+//static int l_tpindex = -1;
+static struct tp_infor l_tpinfor[1];
+#endif
+/////////////////////////////////////////////////////
+// extrenal function
+/////////////////////////////////////////////////////
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+/////////////////////////////////////////////////////
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data );
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+
+char g_dbgmode = 0;
+int COL_NUM;
+int ROW_NUM;
+int SKIP_ZERO_POINT;
+
+int wmt_ts_get_configfilename(char* fname)
+{
+ sprintf(fname,"%s.cfg",l_tp[0].name);
+ return 0;
+}
+
+int wmt_ts_get_firmwfilename(char* fname)
+{
+ sprintf(fname,"%s_fw.bin",l_tp[0].name);
+ return 0;
+}
+
+int wmt_ts_get_xaxis(void)
+{
+ return l_tp[0].xaxis;
+}
+
+int wmt_ts_get_xdir(void)
+{
+ return l_tp[0].xdir;
+}
+
+int wmt_ts_get_ydir(void)
+{
+ return l_tp[0].ydir;
+}
+
+int wmt_ts_get_fingernum(void)
+{
+ return l_tp[0].finger_num;
+}
+
+ int wmt_ts_get_gpionum(void)
+{
+ return irq_gpio;
+}
+
+int wmt_ts_get_resetgpnum(void)
+{
+ return rst_gpio;
+}
+
+int wmt_ts_get_lcdexchg(void)
+{
+ return lcd_exchg;
+}
+
+int wmt_ts_get_resolvX(void)
+{
+ return panelres_x;
+}
+
+int wmt_ts_get_resolvY(void)
+{
+ return panelres_y;
+}
+
+int wmt_ts_enable_tskey(void)
+{
+ return l_tskey_btn;
+}
+
+int wmt_ts_enable_keyled(void)
+{
+ return (keyled_gpio>=0 ? 1:0);
+}
+
+
+static void ts_lighttimer_timeout(unsigned long timeout)
+{
+ // turn off the light
+ if (20 == keyled_gpio)
+ {
+ if (light_level>=0)
+ {
+ REG32_VAL(__GPIO_BASE+0x00F0) &= ~BIT4; // output low
+ dbg("turn off the light!\n");
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00F0) |= BIT4; // output high
+ dbg("turn off the light!\n");
+ }
+ }
+}
+
+
+static void wmt_ts_init_light(void)
+{
+ if (20 == keyled_gpio)
+ {
+ setup_timer(&l_lighttimer, ts_lighttimer_timeout, 0);
+ // init gpio20 and turn off light
+ REG32_VAL(__GPIO_BASE+0x00F0) &= ~BIT4; // output low
+ REG32_VAL(__GPIO_BASE+0x00B0) |= BIT4; // output enable
+ REG32_VAL(__GPIO_BASE+0x0070) |= BIT4; // enable gpio
+ REG32_VAL(__GPIO_BASE+0x04F0) |= BIT4; // pull up
+ REG32_VAL(__GPIO_BASE+0x04B0) |= BIT4; // enable pull up/down
+ }
+}
+
+void wmt_ts_turnoff_light(void)
+{
+ if (20 == keyled_gpio)
+ {
+ mod_timer(&l_lighttimer, jiffies + msecs_to_jiffies(light_time));
+ }
+}
+
+void wmt_ts_turnon_light(void)
+{
+ if (20 == keyled_gpio)
+ {
+ del_timer(&l_lighttimer);
+ if (light_level >= 0)
+ {
+ REG32_VAL(__GPIO_BASE+0x00F0) |= BIT4; // output high
+ dbg("turn on the light!\n");
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00F0) &= ~BIT4; // output low
+ dbg("turn on the light!\n");
+ }
+ }
+}
+
+static void wmt_ts_remove_light(void)
+{
+ if (20 == keyled_gpio)
+ {
+ if (light_level >= 0)
+ {
+ REG32_VAL(__GPIO_BASE+0x00F0) &= ~BIT4; // output low
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00F0) |= BIT4; // output high
+ }
+ del_timer(&l_lighttimer);
+ }
+}
+
+int wmt_is_tsirq_enable(int num)
+{
+ int val = 0;
+
+ if(num > 11)
+ return 0;
+
+ if(num<4)
+ val = REG32_VAL(__GPIO_BASE+0x0300) & (1<<(num*8+7));
+ else if(num >= 4 && num < 8)
+ val = REG32_VAL(__GPIO_BASE+0x0304) & (1<<((num-4)*8+7));
+ else
+ val = REG32_VAL(__GPIO_BASE+0x0308) & (1<<((num-8)*8+7));
+
+ return val?1:0;
+}
+
+int wmt_is_tsint(int num)
+{
+ if (num > 11)
+ {
+ return 0;
+ }
+ return (REG32_VAL(__GPIO_BASE+0x0360) & (1<<num)) ? 1: 0;
+}
+
+void wmt_clr_int(int num)
+{
+ if (num > 11)
+ {
+ return;
+ }
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num;
+}
+
+void wmt_tsreset_init(int num)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num);//&= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<num); // out high
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<num); //output enable
+ msleep(5);
+ //REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<num);
+}
+
+// enable:0-disable,1-enable
+void wmt_enable_rst_pull(int enable)
+{
+ if (enable)
+ {
+ REG32_VAL(__GPIO_BASE+0x0480) |= (1<<rst_gpio); //enable pull up/down
+ } else {
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<rst_gpio); //disable pull up/down
+ }
+}
+
+// up:0-pull down,1-pull up
+void wmt_set_rst_pull(int up)
+{
+ if (up)
+ {
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<rst_gpio); //pull up
+ } else {
+ REG32_VAL(__GPIO_BASE+0x04c0) &= ~(1<<rst_gpio); //pull down
+ }
+}
+
+// high:0-low level,1-high level
+void wmt_rst_output(int high)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ if (high)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<rst_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<rst_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<rst_gpio); //set output
+}
+
+void wmt_rst_input(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<rst_gpio); //set input
+}
+
+int wmt_set_irq_mode(unsigned int num, int mode)
+{
+ if(num >11)
+ return -1;
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num); //enable gpio
+ if(mode == 0)
+ {
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<num); //set output
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull down
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down
+ }
+ else if(mode == 1)
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ //msleep(5);
+ return 0;
+}
+
+
+int wmt_set_gpirq(unsigned int num, int type)
+{
+ int shift;
+ int offset;
+ unsigned long reg;
+
+ if(num >11)
+ return -1;
+ //if (num > 9)
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; // gpio10,11 as gpio
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull down
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down
+
+ //set gpio irq triger type
+ if(num < 4){//[0,3]
+ shift = num;
+ offset = 0x0300;
+ }else if(num >= 4 && num < 8){//[4,7]
+ shift = num-4;
+ offset = 0x0304;
+ }else{// [8,11]
+ shift = num-8;
+ offset = 0x0308;
+ }
+
+ reg = REG32_VAL(__GPIO_BASE + offset);
+
+ switch(type){
+ case GIRQ_LOW:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case GIRQ_HIGH:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ case GIRQ_FALLING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case GIRQ_RISING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ default://both edge
+ reg |= (1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+
+ }
+ //reg |= 1<<(shift*8+7);//enable interrupt
+ reg &= ~(1<<(shift*8+7)); //disable int
+ REG32_VAL(__GPIO_BASE + offset) = reg;
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num; //clear interrupt status
+ msleep(5);
+ return 0;
+}
+
+int wmt_enable_gpirq(unsigned int num)
+{
+ if(num > 11)
+ return -1;
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) |= 1<<(num*8+7); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) |= 1<<((num-4)*8+7); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) |= 1<<((num-8)*8+7); //enable interrupt
+
+ return 0;
+}
+
+int wmt_disable_gpirq(unsigned int num)
+{
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) &= ~(1<<(num*8+7)); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) &= ~(1<<((num-4)*8+7)); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) &= ~(1<<((num-8)*8+7)); //enable interrupt
+
+ return 0;
+}
+
+
+int wmt_get_tsirqnum(void)
+{
+ return IRQ_GPIO;
+}
+
+int wmt_ts_set_rawcoord(unsigned short x, unsigned short y)
+{
+ g_evLast.x = x;
+ g_evLast.y = y;
+ //dbg("raw(%d,%d)*\n", x, y);
+ return 0;
+}
+
+static void wmt_ts_platform_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device wmt_ts_plt_device = {
+ .name = TS_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = wmt_ts_platform_release,
+ },
+// .num_resources = ARRAY_SIZE(wm9715_ts_resources),
+// .resource = wm9715_ts_resources,
+};
+
+static int wmt_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dbg("ts suspend....\n");
+ if (wmt_ts_enable_keyled())
+ wmt_ts_remove_light();
+ if (l_tsdev->suspend !=NULL)
+ {
+ return l_tsdev->suspend(pdev, state);
+ }
+ return 0;
+}
+static int wmt_ts_resume(struct platform_device *pdev)
+{
+ klog("ts resume....\n");
+ if (wmt_ts_enable_keyled())
+ wmt_ts_init_light();
+ if (l_tsdev->resume != NULL)
+ {
+ return l_tsdev->resume(pdev);
+ }
+ return 0;
+}
+
+static int wmt_ts_probe(struct platform_device *pdev)
+{
+ l_tsproc= create_proc_entry(WMTTS_PROC_NAME, 0666, NULL/*&proc_root*/);
+ if (l_tsproc != NULL)
+ {
+ l_tsproc->read_proc = ts_readproc;
+ l_tsproc->write_proc = ts_writeproc;
+ }
+ if (l_tsdev->probe != NULL)
+ {
+ return l_tsdev->probe(pdev);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static int wmt_ts_remove(struct platform_device *pdev)
+{
+ if (l_tsproc != NULL)
+ {
+ remove_proc_entry(WMTTS_PROC_NAME, NULL);
+ l_tsproc = NULL;
+ }
+
+ if (l_tsdev->remove != NULL)
+ return l_tsdev->remove(pdev);
+ else
+ return 0;
+}
+
+static struct platform_driver wmt_ts_plt_driver = {
+ .driver = {
+ .name = TS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = wmt_ts_probe,
+ .remove = wmt_ts_remove,
+ .suspend = wmt_ts_suspend,
+ .resume = wmt_ts_resume,
+};
+
+static int wmt_ts_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ return ret;
+}
+
+static int wmt_ts_close(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static unsigned int wmt_ts_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ return 0;
+}
+
+
+void read_diff(rawdata* pdata)
+{
+ //u16 Buffer[COL_NUM_MAX*ROW_NUM_MAX*2];
+ u16 *Buffer;
+ u16 idx = 0;
+ int i,j;
+ u16 addr =0;
+ pdata->col = COL_NUM;
+ pdata->row = ROW_NUM;
+ Buffer = kzalloc(COL_NUM_MAX*ROW_NUM_MAX*2*2, GFP_KERNEL);
+ if (!Buffer) {
+ errlog("mem alloc fail.\n");
+ return;
+ }
+ for(i=0;i<pdata->row;i++)
+ {
+ addr = 0x42f2+i*60;
+ printk("read_diff: addr=0x%04x\n",addr);
+ for(j=0;j<pdata->col*2;j++)
+ {
+ if(lw86x0_read_reg(addr+j, &Buffer[idx],1)!=0)
+ {
+ lw86x0_read_reg(addr+j, &Buffer[idx],1);
+ }
+ printk("read_diff: Buffer[%d]=0x%04x\n",idx,Buffer[idx]);
+ idx++;
+ }
+ }
+ for(i=0; i<pdata->col * pdata->row; i++)
+ {
+ pdata->data[i] = ((Buffer[i*2]<<8)&0xff00)|(Buffer[i*2+1]&0x00ff);
+ printk("read_diff: pdata->data[%d]=0x%04x\n",i,pdata->data[i]);
+ }
+ kfree(Buffer);
+}
+
+void read_cdc(rawdata* pdata)
+{
+ int i,j;
+ u16 addr = 0x2fc8;
+ u16 idx = 0;
+
+ pdata->col = COL_NUM;
+ pdata->row = ROW_NUM;
+
+ for(i=0;i<pdata->col;i++)
+ {
+ for(j=0;j<pdata->row;j++)
+ {
+ printk("read_cdc: addr=0x%04x\n",addr+idx);
+ if(lw86x0_read_reg(addr+idx, &pdata->data[j*pdata->col+i],1)!=0)
+ {
+ lw86x0_read_reg(addr+idx, &pdata->data[j*pdata->col+i],1);
+ }
+ printk("read_cdc: pdata->data[%d]=0x%04x\n",j*pdata->col+i,pdata->data[j*pdata->col+i]);
+ idx++;
+ }
+ }
+}
+
+void read_amb(rawdata* pdata)
+{
+ int i,j;
+ u16 addr = 0x2E04;
+ u16 idx = 0;
+
+ pdata->col = COL_NUM;
+ pdata->row = ROW_NUM;
+
+ for(i=0;i<pdata->col;i++)
+ {
+ for(j=0;j<pdata->row;j++)
+ {
+ printk("read_amb: addr=0x%04x\n",addr+idx);
+ if(lw86x0_read_reg(addr+idx, &pdata->data[j*pdata->col+i],1)!=0)
+ {
+ lw86x0_read_reg(addr+idx, &pdata->data[j*pdata->col+i],1);
+ }
+ printk("read_amb: pdata->data[%d]=0x%04x\n",j*pdata->col+i,pdata->data[j*pdata->col+i]);
+ idx++;
+ }
+ }
+
+
+}
+
+void lw86x0_flash_write_prepare(void)
+{
+ lw86x0_stop_timer(1);
+ lw86x0_write_reg(0x00e2, 0x0300);//#write_en=1 tmr=1
+ udelay(1);
+ //mass erase
+ lw86x0_write_reg(0x00e2, 0x0305);//#xe=1 mas1=1
+ lw86x0_write_reg(0x00e2, 0x0315);//#erase=1
+ udelay(5);
+ lw86x0_write_reg(0x00e2, 0x0395);//#nvstr=1
+ mdelay(20);
+ lw86x0_write_reg(0x00e2, 0x0385);//#erase=0
+ udelay(100);
+ lw86x0_write_reg(0x00e2, 0x0305);//#nvstr=0
+ lw86x0_write_reg(0x00e2, 0x0300);//#xe=0 mas1=0
+}
+
+void lw86x0_flash_write(u8* pbData,u16 start_addr, u16 num)
+{
+ u16 yaddr = start_addr;
+ u16 xaddr = (start_addr)&0xFF80;//x addr is a 8bit address
+ u16 cnt = 0;
+ while(cnt<num)
+ {
+ while(1)
+ {
+ lw86x0_write_reg(0x00e3, xaddr);//#xaddr
+ lw86x0_write_reg(0x00e2, 0x0304);//#xe=1
+ lw86x0_write_reg(0x00e2, 0x0324);//#prog=1
+ udelay(5);
+ lw86x0_write_reg(0x00e2, 0x03a4);//#nvstr=1
+ udelay(10);
+ do
+ {
+ u16 data = pbData[cnt];
+ lw86x0_write_reg(0x00e3, yaddr);//#yaddr
+ lw86x0_write_reg(0x00e4, data);//#din
+ lw86x0_write_reg(0x00e2, 0xfbac);//#ye=0
+ lw86x0_write_reg(0x00e2, 0x03a4);//#ye=0
+ yaddr++;
+ cnt++;
+ if(cnt==num)
+ {
+ break;
+ }
+ }while(yaddr&0x007F);
+ xaddr+=0x0080;
+ udelay(20);
+ lw86x0_write_reg(0x00e2, 0x0384);//#prog=0
+ udelay(100);
+ lw86x0_write_reg(0x00e2, 0x0304);//#nvstr=0
+ lw86x0_write_reg(0x00e2, 0x0300);//#xe=0
+ if(cnt==num)
+ {
+ break;
+ }
+ }
+ }
+
+}
+
+void lw86x0_flash_write_finish(u16 total_len)
+{
+ //write length of FW to last 2 byte of flash
+ u8 *pLen = (u8 *)&total_len;
+ lw86x0_flash_write(&(pLen[1]),0x7FFE, 1);
+ lw86x0_flash_write(&(pLen[0]),0x7FFF, 1);
+
+ lw86x0_write_reg(0x00e2, 0x0300);//#tmr=1
+ lw86x0_write_reg(0x00e2, 0x0200);//#tmr=0
+ lw86x0_write_reg(0x00e2, 0x02c0);//#nvstr=1 se=1
+ lw86x0_write_reg(0x00e2, 0x02eb);//#prog=1 ifren=1 ye=1 mas=1
+ lw86x0_write_reg(0x00e2, 0x03eb);//#tmr=1
+ lw86x0_write_reg(0x00e2, 0x02eb);//#tmr=0
+ lw86x0_write_reg(0x00e2, 0x02c0);//#prog=0 ifren=0 ye=0 mas=0
+ lw86x0_write_reg(0x00e2, 0x0200);//#nvstr=0 se=0
+ lw86x0_write_reg(0x00e2, 0x0204);//#xe=1
+ lw86x0_write_reg(0x00e2, 0x0214);//#erase=1
+ udelay(5);
+ lw86x0_write_reg(0x00e2, 0x0294);//#nvstr=1
+ mdelay(20);
+ lw86x0_write_reg(0x00e2, 0x0284);//#erase=0
+ udelay(5);
+ lw86x0_write_reg(0x00e2, 0x0204);//#nvstr=0
+ lw86x0_write_reg(0x00e2, 0x0200);//#xe=0
+ lw86x0_write_reg(0x00e2, 0x0000);
+ lw86x0_write_reg(0x000c, 0xffff);
+ lw86x0_stop_timer(0);
+}
+
+void lw86x0_flash_read(u8* pbData, u16 start_addr, u16 num)
+{
+ lw86x0_stop_timer(1);
+ u16 cnt;
+ u16 rd_data = 0;
+ u8 *buf;
+ for(cnt=0; cnt<num;cnt++)
+ {
+ lw86x0_write_reg(0x00e4, 0x0000);//#read data
+ lw86x0_write_reg(0x00e2, 0x0300);//#tmr=1
+ lw86x0_write_reg(0x00e3, start_addr+cnt);
+ lw86x0_write_reg(0x00e2, 0x034c);//#se=1 ye=1 xe=1
+ lw86x0_write_reg(0x00e2, 0x0300);//#se=1 ye=1 xe=1
+ lw86x0_read_reg(0x00e4, &rd_data,1);
+ buf = (u8 *)&rd_data;
+ pbData[cnt] = buf[1];
+ }
+ lw86x0_stop_timer(0);
+}
+
+
+static long wmt_ts_ioctl(/*struct inode * node,*/ struct file *dev, unsigned int cmd, unsigned long arg)
+{
+ reg_word regword;
+ //rawdata rdata;
+ rawdata *rdata;
+ flash_op f_pkt;//flash packet
+
+ int ret = 0;
+ char ch;
+ rdata = kzalloc(sizeof(rawdata), GFP_KERNEL);
+ if (!rdata) {
+ errlog("mem alloc fail.\n");
+ return -ENOMEM;
+ }
+
+ if (_IOC_TYPE(cmd) != TS_IOC_MAGIC){
+ dbg("CMD ERROR!");
+ return -ENOTTY;
+ }
+
+ if (_IOC_NR(cmd) > TS_IOC_MAXNR){
+ dbg("NO SUCH IO CMD!\n");
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case LW86X0_WRITE_REG:
+ copy_from_user(&regword, (reg_word*)arg, sizeof(regword));
+ dbg("write reg[%d] word value 0x%x", regword.uOffset, regword.uValue );
+ ret = lw86x0_write_reg(regword.uOffset, regword.uValue);
+ if (ret != 0)
+ {
+ dbg("Faied to write reg. ret 0x%x\n",ret);
+ }
+ return 0;
+ case LW86X0_READ_REG:
+ copy_from_user(&regword, (reg_word*)arg, sizeof(regword));
+ ret = lw86x0_read_reg(regword.uOffset, &regword.uValue, 1);
+ if (ret != 0)
+ {
+ dbg("Faied to read reg. ret 0x%x\n",ret);
+ }
+ else
+ {
+
+ dbg("read reg[%d]=0x%04x",regword.uOffset, regword.uValue);
+ }
+ copy_to_user((unsigned int*)arg, &regword, sizeof(regword));
+ return 0;
+ case LW86X0_CTRL_DEBUG_MODE:
+ copy_from_user(&ch, (char*)arg, sizeof(char));
+ printk("LW86X0_CTRL_DEBUG_MODE,%c", ch);
+ if(ch=='1')
+ {
+ g_dbgmode = 1;
+ wmt_disable_gpirq(irq_gpio);
+ }
+ else
+ {
+ g_dbgmode = 0;
+ wmt_enable_gpirq(irq_gpio);
+ }
+ return 0;
+ /*
+ case LW86X0_CTRL_RD_DIFF:
+ copy_from_user(&rdata, (rawdata*)arg, sizeof(rdata));
+ printk("tpd-ioctrl: LW86X0_CTRL_RD_DIFF\n");
+ read_diff(&rdata);
+ copy_to_user((unsigned int*)arg, &rdata, sizeof(rdata));
+ return 0;
+ case LW86X0_CTRL_RD_CDC:
+ copy_from_user(&rdata, (rawdata*)arg, sizeof(rdata));
+ printk("tpd-ioctrl: LW86X0_CTRL_RD_CDC\n");
+ read_cdc(&rdata);
+ copy_to_user((unsigned int*)arg, &rdata, sizeof(rdata));
+ return 0;
+ case LW86X0_CTRL_RD_AMB:
+ copy_from_user(&rdata, (rawdata*)arg, sizeof(rdata));
+ printk("tpd-ioctrl: LW86X0_CTRL_RD_AMB\n");
+ read_amb(&rdata);
+ copy_to_user((unsigned int*)arg, &rdata, sizeof(rdata));
+ return 0;
+ */
+ case LW86X0_CTRL_RD_DIFF:
+ copy_from_user(rdata, (rawdata*)arg, sizeof(*rdata));
+ printk("tpd-ioctrl: LW86X0_CTRL_RD_DIFF\n");
+ read_diff(rdata);
+ copy_to_user((unsigned int*)arg, rdata, sizeof(*rdata));
+ return 0;
+ case LW86X0_CTRL_RD_CDC:
+ copy_from_user(rdata, (rawdata*)arg, sizeof(*rdata));
+ printk("tpd-ioctrl: LW86X0_CTRL_RD_CDC\n");
+ read_cdc(rdata);
+ copy_to_user((unsigned int*)arg, rdata, sizeof(*rdata));
+ return 0;
+ case LW86X0_CTRL_RD_AMB:
+ copy_from_user(rdata, (rawdata*)arg, sizeof(*rdata));
+ printk("tpd-ioctrl: LW86X0_CTRL_RD_AMB\n");
+ read_amb(rdata);
+ copy_to_user((unsigned int*)arg, rdata, sizeof(*rdata));
+ return 0;
+ case LW86X0_FLASH_DOWNLOAD:
+ copy_from_user(&f_pkt, (flash_op*)arg, sizeof(f_pkt));
+ if(f_pkt.startaddr==0)
+ {
+ lw86x0_flash_write_prepare();
+ }
+ lw86x0_flash_write(f_pkt.data,
+ f_pkt.startaddr,
+ f_pkt.pktlen);
+ printk("dnload: start addr = %04x\n",f_pkt.startaddr);
+ if(f_pkt.lastpkt==1)
+ {
+ u16 write_len = f_pkt.startaddr + f_pkt.pktlen;
+ lw86x0_flash_write_finish(write_len);
+ }
+ return 0;
+ case LW86X0_FLASH_UPLOAD:
+ copy_from_user(&f_pkt, (flash_op*)arg, sizeof(f_pkt));
+ lw86x0_flash_read(f_pkt.data, f_pkt.startaddr, f_pkt.pktlen);
+ printk("upload: start addr = %04x\n",f_pkt.startaddr);
+ printk("\n");
+ copy_to_user((int*)arg, &f_pkt, sizeof(f_pkt));
+ return 0;
+ case LW86X0_CTRL_STOP_TIMER:
+ copy_from_user(&ch, (char*)arg, sizeof(char));
+ if(ch == '1')
+ lw86x0_stop_timer(1);
+ else
+ lw86x0_stop_timer(0);
+ return 0;
+ }
+ kfree(rdata);
+ return -EINVAL;
+}
+
+static ssize_t wmt_ts_read(struct file *filp, char *buf, size_t count, loff_t *l)
+{
+ return 0;
+}
+
+
+static struct file_operations wmt_ts_fops = {
+ .read = wmt_ts_read,
+ .poll = wmt_ts_poll,
+ .unlocked_ioctl = wmt_ts_ioctl,
+ .open = wmt_ts_open,
+ .release = wmt_ts_close,
+};
+
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data )
+{
+ int calibrate = 0;
+ int val = 0;
+
+ if (sscanf(buffer, "calibrate=%d\n", &calibrate))
+ {
+ if (1 == calibrate)
+ {
+ if((l_tsdev->capacitance_calibrate != NULL) &&
+ (0 == l_tsdev->capacitance_calibrate()))
+ {
+ printk(KERN_ALERT "%s calibration successfully!\n", l_tsdev->ts_id);
+ } else {
+ printk(KERN_ALERT "%s calibration failed!\n", l_tsdev->ts_id);
+ }
+ }
+ } else if (sscanf(buffer, "out=%d\n", &val))
+ {
+ switch(val)
+ {
+ case 1: // reset1
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<irq_gpio); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<irq_gpio); //out high
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<irq_gpio); //set input
+ break;
+ case 0: // reset2
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<irq_gpio); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<irq_gpio); //out high
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<irq_gpio); //set input
+ break;
+ default:
+ break;
+ };
+ }
+ return count;
+}
+
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len = sprintf(page,
+ "echo calibrate=1 > /proc/wmtts_config to calibrate ts.\n");
+ return len;
+}
+
+unsigned char wmt_ts_get_i2caddr(void)
+{
+ return ts_i2c_addr;
+}
+
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 127;
+ char retval[128] = {0},*p=NULL,*s=NULL;
+ int Enable=0;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ errlog("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+
+ //check touch enable
+ p = retval;
+ sscanf(p,"%d:", &Enable);
+ if(Enable == 0){
+ errlog("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ //check touch IC name
+ p = strchr(p,':');p++;
+ if (strncmp(p, l_tp[0].name, strlen(l_tp[0].name))) {
+ errlog("Can't find %s!\n", l_tp[0].name);
+ return -ENODEV;
+ }
+
+ //get firmware file name
+ s = strchr(p,':');
+ memset(l_tp[0].name,0x00,sizeof(l_tp[0].name));
+ strncpy(l_tp[0].name, p, (s-p));
+ dbg("ts_fwname=%s\n", l_tp[0].name);
+
+ p = s + 1;
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%x",
+ &irq_gpio,&panelres_x,&panelres_y,&rst_gpio,
+ &(l_tp[0].xaxis),&(l_tp[0].xdir),&(l_tp[0].ydir),
+ &(l_tp[0].finger_num),&(l_tp[0].i2caddr));
+
+ dbg("%d;%d;%d;%d;%d;%d;%d;%d;%x;",irq_gpio,panelres_x,panelres_y,rst_gpio,
+ (l_tp[0].xaxis),(l_tp[0].xdir),(l_tp[0].ydir),
+ (l_tp[0].finger_num),(l_tp[0].i2caddr));
+
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ lcd_exchg = 1;
+ }
+
+ return 0;
+}
+//#if 0
+//add by jackie i2c_board_info
+struct i2c_board_info ts_i2c_board_info = {
+ .type = WMT_TS_I2C_NAME,
+ .flags = 0x00,
+ //.addr = 0x18, //WMT_TS_I2C_ADDR,//why error?
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+//add jackie static
+ int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+
+ ts_i2c_board_info.addr = l_tp[0].i2caddr;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(1);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+//add by jackie
+struct i2c_client* ts_get_i2c_client(void)
+{
+ return l_client;
+}
+//add
+static int __init wmt_ts_init(void)
+{
+ int ret = 0;
+
+ if(wmt_check_touch_env())
+ return -ENODEV;
+
+//add by jackie
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ mutex_init(&cal_mutex);
+
+ if (l_tsdev->init() < 0){
+ printk(KERN_ERR "Errors to init %s ts IC!!!\n", l_tsdev->ts_id);
+ return -1;
+ }
+ if (wmt_ts_enable_keyled())
+ wmt_ts_init_light();
+ // Create device node
+ if (register_chrdev (TS_MAJOR, TS_NAME, &wmt_ts_fops)) {
+ printk (KERN_ERR "wmt touch: unable to get major %d\n", TS_MAJOR);
+ return -EIO;
+ }
+
+ l_dev_class = class_create(THIS_MODULE, TS_NAME);
+ if (IS_ERR(l_dev_class)){
+ ret = PTR_ERR(l_dev_class);
+ printk(KERN_ERR "Can't class_create touch device !!\n");
+ return ret;
+ }
+ l_clsdevice = device_create(l_dev_class, NULL, MKDEV(TS_MAJOR, 0), NULL, TS_NAME);
+ if (IS_ERR(l_clsdevice)){
+ ret = PTR_ERR(l_clsdevice);
+ printk(KERN_ERR "Failed to create device %s !!!",TS_NAME);
+ return ret;
+ }
+
+ // register device and driver of platform
+ ret = platform_device_register(&wmt_ts_plt_device);
+ if(ret){
+ errlog("wmt ts plat device register failed!\n");
+ return ret;
+ }
+ ret = platform_driver_register(&wmt_ts_plt_driver);
+ if(ret){
+ errlog("can not register platform_driver_register\n");
+ platform_device_unregister(&wmt_ts_plt_device);
+ return ret;
+ }
+
+ klog("wmt ts driver init ok!\n");
+ return ret;
+}
+
+//add by jackie
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+//add end
+
+static void __exit wmt_ts_exit(void)
+{
+ dbg("%s\n",__FUNCTION__);
+
+ if (wmt_ts_enable_keyled())
+ wmt_ts_remove_light();
+ l_tsdev->exit();
+ mutex_destroy(&cal_mutex);
+ platform_driver_unregister(&wmt_ts_plt_driver);
+ platform_device_unregister(&wmt_ts_plt_device);
+ device_destroy(l_dev_class, MKDEV(TS_MAJOR, 0));
+ unregister_chrdev(TS_MAJOR, TS_NAME);
+ class_destroy(l_dev_class);
+ ts_i2c_unregister_device();
+}
+
+
+module_init(wmt_ts_init);
+module_exit(wmt_ts_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/lw86x0_ts/wmt_ts.h b/drivers/input/touchscreen/lw86x0_ts/wmt_ts.h
new file mode 100755
index 00000000..ff1218ac
--- /dev/null
+++ b/drivers/input/touchscreen/lw86x0_ts/wmt_ts.h
@@ -0,0 +1,98 @@
+
+#ifndef WMT_TSH_201010191758
+#define WMT_TSH_201010191758
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+
+//#define DEBUG_WMT_TS
+#ifdef DEBUG_WMT_TS
+#undef dbg
+#define dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args)
+
+//#define dbg(fmt, args...) if (wmt_ts_isrundbg()) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args)
+
+#else
+#define dbg(fmt, args...)
+#endif
+
+#undef errlog
+#undef klog
+#define errlog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+#define klog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+
+#define DONOTHING 0xff
+
+#define WMT_TS_I2C_NAME "lw86x0-ts"
+//////////////////////////////data type///////////////////////////
+typedef struct {
+ short pressure;
+ short x;
+ short y;
+ //short millisecs;
+} TS_EVENT;
+
+struct wmtts_device
+{
+ //data
+ char* driver_name;
+ char* ts_id;
+ //function
+ int (*init)(void);
+ int (*probe)(struct platform_device *platdev);
+ int (*remove)(struct platform_device *pdev);
+ void (*exit)(void);
+ int (*suspend)(struct platform_device *pdev, pm_message_t state);
+ int (*resume)(struct platform_device *pdev);
+ int (*capacitance_calibrate)(void);
+ int (*wait_penup)(struct wmtts_device*tsdev); // waiting untill penup
+ int penup; // 0--pendown;1--penup
+
+};
+
+//////////////////////////function interface/////////////////////////
+extern int wmt_ts_get_gpionum(void);
+extern int wmt_ts_get_resolvX(void);
+extern int wmt_ts_get_resolvY(void);
+extern int wmt_ts_set_rawcoord(unsigned short x, unsigned short y);
+extern int wmt_set_gpirq(unsigned int num, int type);
+extern int wmt_get_tsirqnum(void);
+extern int wmt_disable_gpirq(unsigned int num);
+extern int wmt_enable_gpirq(unsigned int num);
+extern int wmt_is_tsirq_enable(int num);
+extern void wmt_enable_rst_pull(int enable);
+extern void wmt_set_rst_pull(int up);
+extern void wmt_rst_output(int high);
+void wmt_rst_input(void);
+extern int wmt_is_tsint(int num);
+extern void wmt_clr_int(int num);
+extern void wmt_tsreset_init(int num);
+extern int wmt_ts_get_resetgpnum(void);
+extern int wmt_ts_get_lcdexchg(void);
+extern unsigned char wmt_ts_get_i2caddr(void);
+extern void wmt_ts_turnoff_light(void);
+extern void wmt_ts_turnon_light(void);
+extern int wmt_ts_enable_tskey(void);
+extern int wmt_ts_get_configfilename(char* fname);
+extern int wmt_ts_get_firmwfilename(char* fname);
+extern int wmt_ts_get_xaxis(void);
+extern int wmt_ts_get_xdir(void);
+extern int wmt_ts_get_ydir(void);
+extern int wmt_ts_get_fingernum(void);
+extern int wmt_ts_enable_keyled(void);
+extern int wmt_set_irq_mode(unsigned int num, int mode);
+extern int wmt_disable_gpirq(unsigned int num);
+extern int wmt_enable_gpirq(unsigned int num);
+extern int ts_i2c_register_device (void);
+extern struct i2c_client* ts_get_i2c_client(void);
+
+extern void lw86x0_flash_write_prepare(void);
+extern void lw86x0_flash_read(u8* pbData, u16 start_addr, u16 num);
+extern void lw86x0_flash_write(u8* pbData,u16 start_addr, u16 num);
+extern void lw86x0_flash_write_finish(u16 total_len);
+#endif
+
+
+
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
new file mode 100644
index 00000000..7d2b2136
--- /dev/null
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -0,0 +1,310 @@
+/*
+ * mainstone-wm97xx.c -- Mainstone Continuous Touch screen driver for
+ * Wolfson WM97xx AC97 Codecs.
+ *
+ * Copyright 2004, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ *
+ * 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.
+ *
+ * Notes:
+ * This is a wm97xx extended touch driver to capture touch
+ * data in a continuous manner on the Intel XScale architecture
+ *
+ * Features:
+ * - codecs supported:- WM9705, WM9712, WM9713
+ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wm97xx.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#include <mach/regs-ac97.h>
+
+#include <asm/mach-types.h>
+
+struct continuous {
+ u16 id; /* codec id */
+ u8 code; /* continuous code */
+ u8 reads; /* number of coord reads per read cycle */
+ u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+ {WM9705_ID2, 0, WM_READS(94), 94},
+ {WM9705_ID2, 1, WM_READS(188), 188},
+ {WM9705_ID2, 2, WM_READS(375), 375},
+ {WM9705_ID2, 3, WM_READS(750), 750},
+ {WM9712_ID2, 0, WM_READS(94), 94},
+ {WM9712_ID2, 1, WM_READS(188), 188},
+ {WM9712_ID2, 2, WM_READS(375), 375},
+ {WM9712_ID2, 3, WM_READS(750), 750},
+ {WM9713_ID2, 0, WM_READS(94), 94},
+ {WM9713_ID2, 1, WM_READS(120), 120},
+ {WM9713_ID2, 2, WM_READS(154), 154},
+ {WM9713_ID2, 3, WM_READS(188), 188},
+};
+
+/* continuous speed index */
+static int sp_idx;
+static u16 last, tries;
+static int irq;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 200;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pen down detection.
+ *
+ * This driver can either poll or use an interrupt to indicate a pen down
+ * event. If the irq request fails then it will fall back to polling mode.
+ */
+static int pen_int;
+module_param(pen_int, int, 0);
+MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+
+/* flush AC97 slot 5 FIFO on pxa machines */
+#ifdef CONFIG_PXA27x
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ schedule_timeout_uninterruptible(1);
+
+ while (MISR & (1 << 2))
+ MODR;
+}
+#else
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ unsigned int count;
+
+ schedule_timeout_uninterruptible(1);
+
+ for (count = 0; count < 16; count++)
+ MODR;
+}
+#endif
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+ int reads = 0;
+
+ /* When the AC97 queue has been drained we need to allow time
+ * to buffer up samples otherwise we end up spinning polling
+ * for samples. The controller can't have a suitably low
+ * threshold set to use the notifications it gives.
+ */
+ schedule_timeout_uninterruptible(1);
+
+ if (tries > 5) {
+ tries = 0;
+ return RC_PENUP;
+ }
+
+ x = MODR;
+ if (x == last) {
+ tries++;
+ return RC_AGAIN;
+ }
+ last = x;
+ do {
+ if (reads)
+ x = MODR;
+ y = MODR;
+ if (pressure)
+ p = MODR;
+
+ dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n",
+ x, y, p);
+
+ /* are samples valid */
+ if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+ (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+ (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
+ goto up;
+
+ /* coordinate is good */
+ tries = 0;
+ input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
+ input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+ input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
+ input_sync(wm->input_dev);
+ reads++;
+ } while (reads < cinfo[sp_idx].reads);
+up:
+ return RC_PENDOWN | RC_AGAIN;
+}
+
+static int wm97xx_acc_startup(struct wm97xx *wm)
+{
+ int idx = 0, ret = 0;
+
+ /* check we have a codec */
+ if (wm->ac97 == NULL)
+ return -ENODEV;
+
+ /* Go you big red fire engine */
+ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+ if (wm->id != cinfo[idx].id)
+ continue;
+ sp_idx = idx;
+ if (cont_rate <= cinfo[idx].speed)
+ break;
+ }
+ wm->acc_rate = cinfo[sp_idx].code;
+ wm->acc_slot = ac97_touch_slot;
+ dev_info(wm->dev,
+ "mainstone accelerated touchscreen driver, %d samples/sec\n",
+ cinfo[sp_idx].speed);
+
+ /* IRQ driven touchscreen is used on Palm hardware */
+ if (machine_is_palmt5() || machine_is_palmtx() || machine_is_palmld()) {
+ pen_int = 1;
+ irq = 27;
+ /* There is some obscure mutant of WM9712 interbred with WM9713
+ * used on Palm HW */
+ wm->variant = WM97xx_WM1613;
+ } else if (machine_is_mainstone() && pen_int)
+ irq = 4;
+
+ if (irq) {
+ ret = gpio_request(irq, "Touchscreen IRQ");
+ if (ret)
+ goto out;
+
+ ret = gpio_direction_input(irq);
+ if (ret) {
+ gpio_free(irq);
+ goto out;
+ }
+
+ wm->pen_irq = gpio_to_irq(irq);
+ irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);
+ } else /* pen irq not supported */
+ pen_int = 0;
+
+ /* codec specific irq config */
+ if (pen_int) {
+ switch (wm->id) {
+ case WM9705_ID2:
+ break;
+ case WM9712_ID2:
+ case WM9713_ID2:
+ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
+ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_STICKY,
+ WM97XX_GPIO_WAKE);
+ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_NOTSTICKY,
+ WM97XX_GPIO_NOWAKE);
+ break;
+ default:
+ dev_err(wm->dev,
+ "pen down irq not supported on this device\n");
+ pen_int = 0;
+ break;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static void wm97xx_acc_shutdown(struct wm97xx *wm)
+{
+ /* codec specific deconfig */
+ if (pen_int) {
+ if (irq)
+ gpio_free(irq);
+ wm->pen_irq = 0;
+ }
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+ if (enable)
+ enable_irq(wm->pen_irq);
+ else
+ disable_irq_nosync(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops mainstone_mach_ops = {
+ .acc_enabled = 1,
+ .acc_pen_up = wm97xx_acc_pen_up,
+ .acc_pen_down = wm97xx_acc_pen_down,
+ .acc_startup = wm97xx_acc_startup,
+ .acc_shutdown = wm97xx_acc_shutdown,
+ .irq_enable = wm97xx_irq_enable,
+ .irq_gpio = WM97XX_GPIO_2,
+};
+
+static int mainstone_wm97xx_probe(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+
+ return wm97xx_register_mach_ops(wm, &mainstone_mach_ops);
+}
+
+static int mainstone_wm97xx_remove(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+
+ wm97xx_unregister_mach_ops(wm);
+ return 0;
+}
+
+static struct platform_driver mainstone_wm97xx_driver = {
+ .probe = mainstone_wm97xx_probe,
+ .remove = mainstone_wm97xx_remove,
+ .driver = {
+ .name = "wm97xx-touch",
+ },
+};
+module_platform_driver(mainstone_wm97xx_driver);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
new file mode 100644
index 00000000..4eab50b8
--- /dev/null
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -0,0 +1,262 @@
+/*
+ * Driver for MAXI MAX11801 - A Resistive touch screen controller with
+ * i2c interface
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Author: Zhang Jiejing <jiejing.zhang@freescale.com>
+ *
+ * Based on mcs5000_ts.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This driver aims to support the series of MAXI touch chips max11801
+ * through max11803. The main difference between these 4 chips can be
+ * found in the table below:
+ * -----------------------------------------------------
+ * | CHIP | AUTO MODE SUPPORT(FIFO) | INTERFACE |
+ * |----------------------------------------------------|
+ * | max11800 | YES | SPI |
+ * | max11801 | YES | I2C |
+ * | max11802 | NO | SPI |
+ * | max11803 | NO | I2C |
+ * ------------------------------------------------------
+ *
+ * Currently, this driver only supports max11801.
+ *
+ * Data Sheet:
+ * http://www.maxim-ic.com/datasheet/index.mvp/id/5943
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+/* Register Address define */
+#define GENERNAL_STATUS_REG 0x00
+#define GENERNAL_CONF_REG 0x01
+#define MESURE_RES_CONF_REG 0x02
+#define MESURE_AVER_CONF_REG 0x03
+#define ADC_SAMPLE_TIME_CONF_REG 0x04
+#define PANEL_SETUPTIME_CONF_REG 0x05
+#define DELAY_CONVERSION_CONF_REG 0x06
+#define TOUCH_DETECT_PULLUP_CONF_REG 0x07
+#define AUTO_MODE_TIME_CONF_REG 0x08 /* only for max11800/max11801 */
+#define APERTURE_CONF_REG 0x09 /* only for max11800/max11801 */
+#define AUX_MESURE_CONF_REG 0x0a
+#define OP_MODE_CONF_REG 0x0b
+
+/* FIFO is found only in max11800 and max11801 */
+#define FIFO_RD_CMD (0x50 << 1)
+#define MAX11801_FIFO_INT (1 << 2)
+#define MAX11801_FIFO_OVERFLOW (1 << 3)
+
+#define XY_BUFSIZE 4
+#define XY_BUF_OFFSET 4
+
+#define MAX11801_MAX_X 0xfff
+#define MAX11801_MAX_Y 0xfff
+
+#define MEASURE_TAG_OFFSET 2
+#define MEASURE_TAG_MASK (3 << MEASURE_TAG_OFFSET)
+#define EVENT_TAG_OFFSET 0
+#define EVENT_TAG_MASK (3 << EVENT_TAG_OFFSET)
+#define MEASURE_X_TAG (0 << MEASURE_TAG_OFFSET)
+#define MEASURE_Y_TAG (1 << MEASURE_TAG_OFFSET)
+
+/* These are the state of touch event state machine */
+enum {
+ EVENT_INIT,
+ EVENT_MIDDLE,
+ EVENT_RELEASE,
+ EVENT_FIFO_END
+};
+
+struct max11801_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+};
+
+static u8 read_register(struct i2c_client *client, int addr)
+{
+ /* XXX: The chip ignores LSB of register address */
+ return i2c_smbus_read_byte_data(client, addr << 1);
+}
+
+static int max11801_write_reg(struct i2c_client *client, int addr, int data)
+{
+ /* XXX: The chip ignores LSB of register address */
+ return i2c_smbus_write_byte_data(client, addr << 1, data);
+}
+
+static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
+{
+ struct max11801_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ int status, i, ret;
+ u8 buf[XY_BUFSIZE];
+ int x = -1;
+ int y = -1;
+
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
+ XY_BUFSIZE, buf);
+
+ /*
+ * We should get 4 bytes buffer that contains X,Y
+ * and event tag
+ */
+ if (ret < XY_BUFSIZE)
+ goto out;
+
+ for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
+ if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
+ x = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
+ y = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ }
+
+ if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
+ goto out;
+
+ switch (buf[1] & EVENT_TAG_MASK) {
+ case EVENT_INIT:
+ /* fall through */
+ case EVENT_MIDDLE:
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_RELEASE:
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_FIFO_END:
+ break;
+ }
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static void __devinit max11801_ts_phy_init(struct max11801_data *data)
+{
+ struct i2c_client *client = data->client;
+
+ /* Average X,Y, take 16 samples, average eight media sample */
+ max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
+ /* X,Y panel setup time set to 20us */
+ max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
+ /* Rough pullup time (2uS), Fine pullup time (10us) */
+ max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
+ /* Auto mode init period = 5ms , scan period = 5ms*/
+ max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
+ /* Aperture X,Y set to +- 4LSB */
+ max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
+ /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+}
+
+static int __devinit max11801_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max11801_data *data;
+ struct input_dev *input_dev;
+ int error;
+
+ data = kzalloc(sizeof(struct max11801_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;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+
+ input_dev->name = "max11801_ts";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0);
+ input_set_drvdata(input_dev, data);
+
+ max11801_ts_phy_init(data);
+
+ error = request_threaded_irq(client->irq, NULL, max11801_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "max11801_ts", data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(data->input_dev);
+ if (error)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, data);
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+ return error;
+}
+
+static __devexit int max11801_ts_remove(struct i2c_client *client)
+{
+ struct max11801_data *data = i2c_get_clientdata(client);
+
+ free_irq(client->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id max11801_ts_id[] = {
+ {"max11801", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
+
+static struct i2c_driver max11801_ts_driver = {
+ .driver = {
+ .name = "max11801_ts",
+ .owner = THIS_MODULE,
+ },
+ .id_table = max11801_ts_id,
+ .probe = max11801_ts_probe,
+ .remove = __devexit_p(max11801_ts_remove),
+};
+
+module_i2c_driver(max11801_ts_driver);
+
+MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
new file mode 100644
index 00000000..48dc5b0d
--- /dev/null
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -0,0 +1,268 @@
+/*
+ * Driver for the Freescale Semiconductor MC13783 touchscreen.
+ *
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de/
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#define MC13783_TS_NAME "mc13783-ts"
+
+#define DEFAULT_SAMPLE_TOLERANCE 300
+
+static unsigned int sample_tolerance = DEFAULT_SAMPLE_TOLERANCE;
+module_param(sample_tolerance, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(sample_tolerance,
+ "If the minimal and maximal value read out for one axis (out "
+ "of three) differ by this value (default: "
+ __stringify(DEFAULT_SAMPLE_TOLERANCE) ") or more, the reading "
+ "is supposed to be wrong and is discarded. Set to 0 to "
+ "disable this check.");
+
+struct mc13783_ts_priv {
+ struct input_dev *idev;
+ struct mc13xxx *mc13xxx;
+ struct delayed_work work;
+ struct workqueue_struct *workq;
+ unsigned int sample[4];
+ struct mc13xxx_ts_platform_data *touch;
+};
+
+static irqreturn_t mc13783_ts_handler(int irq, void *data)
+{
+ struct mc13783_ts_priv *priv = data;
+
+ mc13xxx_irq_ack(priv->mc13xxx, irq);
+
+ /*
+ * Kick off reading coordinates. Note that if work happens already
+ * be queued for future execution (it rearms itself) it will not
+ * be rescheduled for immediate execution here. However the rearm
+ * delay is HZ / 50 which is acceptable.
+ */
+ queue_delayed_work(priv->workq, &priv->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+#define sort3(a0, a1, a2) ({ \
+ if (a0 > a1) \
+ swap(a0, a1); \
+ if (a1 > a2) \
+ swap(a1, a2); \
+ if (a0 > a1) \
+ swap(a0, a1); \
+ })
+
+static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)
+{
+ struct input_dev *idev = priv->idev;
+ int x0, x1, x2, y0, y1, y2;
+ int cr0, cr1;
+
+ /*
+ * the values are 10-bit wide only, but the two least significant
+ * bits are for future 12 bit use and reading yields 0
+ */
+ x0 = priv->sample[0] & 0xfff;
+ x1 = priv->sample[1] & 0xfff;
+ x2 = priv->sample[2] & 0xfff;
+ y0 = priv->sample[3] & 0xfff;
+ y1 = (priv->sample[0] >> 12) & 0xfff;
+ y2 = (priv->sample[1] >> 12) & 0xfff;
+ cr0 = (priv->sample[2] >> 12) & 0xfff;
+ cr1 = (priv->sample[3] >> 12) & 0xfff;
+
+ dev_dbg(&idev->dev,
+ "x: (% 4d,% 4d,% 4d) y: (% 4d, % 4d,% 4d) cr: (% 4d, % 4d)\n",
+ x0, x1, x2, y0, y1, y2, cr0, cr1);
+
+ sort3(x0, x1, x2);
+ sort3(y0, y1, y2);
+
+ cr0 = (cr0 + cr1) / 2;
+
+ if (!cr0 || !sample_tolerance ||
+ (x2 - x0 < sample_tolerance &&
+ y2 - y0 < sample_tolerance)) {
+ /* report the median coordinate and average pressure */
+ if (cr0) {
+ input_report_abs(idev, ABS_X, x1);
+ input_report_abs(idev, ABS_Y, y1);
+
+ dev_dbg(&idev->dev, "report (%d, %d, %d)\n",
+ x1, y1, 0x1000 - cr0);
+ queue_delayed_work(priv->workq, &priv->work, HZ / 50);
+ } else
+ dev_dbg(&idev->dev, "report release\n");
+
+ input_report_abs(idev, ABS_PRESSURE,
+ cr0 ? 0x1000 - cr0 : cr0);
+ input_report_key(idev, BTN_TOUCH, cr0);
+ input_sync(idev);
+ } else
+ dev_dbg(&idev->dev, "discard event\n");
+}
+
+static void mc13783_ts_work(struct work_struct *work)
+{
+ struct mc13783_ts_priv *priv =
+ container_of(work, struct mc13783_ts_priv, work.work);
+ unsigned int mode = MC13XXX_ADC_MODE_TS;
+ unsigned int channel = 12;
+
+ if (mc13xxx_adc_do_conversion(priv->mc13xxx,
+ mode, channel,
+ priv->touch->ato, priv->touch->atox,
+ priv->sample) == 0)
+ mc13783_ts_report_sample(priv);
+}
+
+static int mc13783_ts_open(struct input_dev *dev)
+{
+ struct mc13783_ts_priv *priv = input_get_drvdata(dev);
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS);
+
+ ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS,
+ mc13783_ts_handler, MC13783_TS_NAME, priv);
+ if (ret)
+ goto out;
+
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0);
+ if (ret)
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+ return ret;
+}
+
+static void mc13783_ts_close(struct input_dev *dev)
+{
+ struct mc13783_ts_priv *priv = input_get_drvdata(dev);
+
+ mc13xxx_lock(priv->mc13xxx);
+ mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+ MC13XXX_ADC0_TSMOD_MASK, 0);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+ mc13xxx_unlock(priv->mc13xxx);
+
+ cancel_delayed_work_sync(&priv->work);
+}
+
+static int __init mc13783_ts_probe(struct platform_device *pdev)
+{
+ struct mc13783_ts_priv *priv;
+ struct input_dev *idev;
+ int ret = -ENOMEM;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!priv || !idev)
+ goto err_free_mem;
+
+ INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
+ priv->idev = idev;
+ priv->touch = dev_get_platdata(&pdev->dev);
+ if (!priv->touch) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ ret = -ENODEV;
+ goto err_free_mem;
+ }
+
+ /*
+ * We need separate workqueue because mc13783_adc_do_conversion
+ * uses keventd and thus would deadlock.
+ */
+ priv->workq = create_singlethread_workqueue("mc13783_ts");
+ if (!priv->workq)
+ goto err_free_mem;
+
+ idev->name = MC13783_TS_NAME;
+ idev->dev.parent = &pdev->dev;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 0xfff, 0, 0);
+
+ idev->open = mc13783_ts_open;
+ idev->close = mc13783_ts_close;
+
+ input_set_drvdata(idev, priv);
+
+ ret = input_register_device(priv->idev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "register input device failed with %d\n", ret);
+ goto err_destroy_wq;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ return 0;
+
+err_destroy_wq:
+ destroy_workqueue(priv->workq);
+err_free_mem:
+ input_free_device(idev);
+ kfree(priv);
+ return ret;
+}
+
+static int __devexit mc13783_ts_remove(struct platform_device *pdev)
+{
+ struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ destroy_workqueue(priv->workq);
+ input_unregister_device(priv->idev);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_ts_driver = {
+ .remove = __devexit_p(mc13783_ts_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MC13783_TS_NAME,
+ },
+};
+
+static int __init mc13783_ts_init(void)
+{
+ return platform_driver_probe(&mc13783_ts_driver, &mc13783_ts_probe);
+}
+module_init(mc13783_ts_init);
+
+static void __exit mc13783_ts_exit(void)
+{
+ platform_driver_unregister(&mc13783_ts_driver);
+}
+module_exit(mc13783_ts_exit);
+
+MODULE_DESCRIPTION("MC13783 input touchscreen driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" MC13783_TS_NAME);
diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
new file mode 100644
index 00000000..b5285118
--- /dev/null
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -0,0 +1,310 @@
+/*
+ * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * Based on wm97xx-core.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c/mcs.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+
+/* Registers */
+#define MCS5000_TS_STATUS 0x00
+#define STATUS_OFFSET 0
+#define STATUS_NO (0 << STATUS_OFFSET)
+#define STATUS_INIT (1 << STATUS_OFFSET)
+#define STATUS_SENSING (2 << STATUS_OFFSET)
+#define STATUS_COORD (3 << STATUS_OFFSET)
+#define STATUS_GESTURE (4 << STATUS_OFFSET)
+#define ERROR_OFFSET 4
+#define ERROR_NO (0 << ERROR_OFFSET)
+#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET)
+#define ERROR_INT_RESET (2 << ERROR_OFFSET)
+#define ERROR_EXT_RESET (3 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET)
+
+#define MCS5000_TS_OP_MODE 0x01
+#define RESET_OFFSET 0
+#define RESET_NO (0 << RESET_OFFSET)
+#define RESET_EXT_SOFT (1 << RESET_OFFSET)
+#define OP_MODE_OFFSET 1
+#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET)
+#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET)
+#define GESTURE_OFFSET 4
+#define GESTURE_DISABLE (0 << GESTURE_OFFSET)
+#define GESTURE_ENABLE (1 << GESTURE_OFFSET)
+#define PROXIMITY_OFFSET 5
+#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET)
+#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET)
+#define SCAN_MODE_OFFSET 6
+#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET)
+#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET)
+#define REPORT_RATE_OFFSET 7
+#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET)
+#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET)
+
+#define MCS5000_TS_SENS_CTL 0x02
+#define MCS5000_TS_FILTER_CTL 0x03
+#define PRI_FILTER_OFFSET 0
+#define SEC_FILTER_OFFSET 4
+
+#define MCS5000_TS_X_SIZE_UPPER 0x08
+#define MCS5000_TS_X_SIZE_LOWER 0x09
+#define MCS5000_TS_Y_SIZE_UPPER 0x0A
+#define MCS5000_TS_Y_SIZE_LOWER 0x0B
+
+#define MCS5000_TS_INPUT_INFO 0x10
+#define INPUT_TYPE_OFFSET 0
+#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET)
+#define GESTURE_CODE_OFFSET 3
+#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET)
+
+#define MCS5000_TS_X_POS_UPPER 0x11
+#define MCS5000_TS_X_POS_LOWER 0x12
+#define MCS5000_TS_Y_POS_UPPER 0x13
+#define MCS5000_TS_Y_POS_LOWER 0x14
+#define MCS5000_TS_Z_POS 0x15
+#define MCS5000_TS_WIDTH 0x16
+#define MCS5000_TS_GESTURE_VAL 0x17
+#define MCS5000_TS_MODULE_REV 0x20
+#define MCS5000_TS_FIRMWARE_VER 0x21
+
+/* Touchscreen absolute values */
+#define MCS5000_MAX_XC 0x3ff
+#define MCS5000_MAX_YC 0x3ff
+
+enum mcs5000_ts_read_offset {
+ READ_INPUT_INFO,
+ READ_X_POS_UPPER,
+ READ_X_POS_LOWER,
+ READ_Y_POS_UPPER,
+ READ_Y_POS_LOWER,
+ READ_BLOCK_SIZE,
+};
+
+/* Each client has this additional data */
+struct mcs5000_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ const struct mcs_platform_data *platform_data;
+};
+
+static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)
+{
+ struct mcs5000_ts_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ u8 buffer[READ_BLOCK_SIZE];
+ int err;
+ int x;
+ int y;
+
+ err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO,
+ READ_BLOCK_SIZE, buffer);
+ if (err < 0) {
+ dev_err(&client->dev, "%s, err[%d]\n", __func__, err);
+ goto out;
+ }
+
+ switch (buffer[READ_INPUT_INFO]) {
+ case INPUT_TYPE_NONTOUCH:
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case INPUT_TYPE_SINGLE:
+ x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER];
+ y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER];
+
+ input_report_key(data->input_dev, BTN_TOUCH, 1);
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_sync(data->input_dev);
+ break;
+
+ case INPUT_TYPE_DUAL:
+ /* TODO */
+ break;
+
+ case INPUT_TYPE_PALM:
+ /* TODO */
+ break;
+
+ case INPUT_TYPE_PROXIMITY:
+ /* TODO */
+ break;
+
+ default:
+ dev_err(&client->dev, "Unknown ts input type %d\n",
+ buffer[READ_INPUT_INFO]);
+ break;
+ }
+
+ out:
+ return IRQ_HANDLED;
+}
+
+static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data)
+{
+ const struct mcs_platform_data *platform_data =
+ data->platform_data;
+ struct i2c_client *client = data->client;
+
+ /* Touch reset & sleep mode */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE,
+ RESET_EXT_SOFT | OP_MODE_SLEEP);
+
+ /* Touch size */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER,
+ platform_data->x_size >> 8);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER,
+ platform_data->x_size & 0xff);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER,
+ platform_data->y_size >> 8);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER,
+ platform_data->y_size & 0xff);
+
+ /* Touch active mode & 80 report rate */
+ i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE,
+ OP_MODE_ACTIVE | REPORT_RATE_80);
+}
+
+static int __devinit mcs5000_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mcs5000_ts_data *data;
+ struct input_dev *input_dev;
+ int ret;
+
+ if (!client->dev.platform_data)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(struct mcs5000_ts_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->platform_data = client->dev.platform_data;
+
+ input_dev->name = "MELPAS MCS-5000 Touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+
+ if (data->platform_data->cfg_pin)
+ data->platform_data->cfg_pin();
+
+ ret = request_threaded_irq(client->irq, NULL, mcs5000_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mcs5000_ts", data);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ ret = input_register_device(data->input_dev);
+ if (ret < 0)
+ goto err_free_irq;
+
+ mcs5000_ts_phys_init(data);
+ i2c_set_clientdata(client, data);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+ return ret;
+}
+
+static int __devexit mcs5000_ts_remove(struct i2c_client *client)
+{
+ struct mcs5000_ts_data *data = i2c_get_clientdata(client);
+
+ free_irq(client->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcs5000_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ /* Touch sleep mode */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
+
+ return 0;
+}
+
+static int mcs5000_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mcs5000_ts_data *data = i2c_get_clientdata(client);
+
+ mcs5000_ts_phys_init(data);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
+#endif
+
+static const struct i2c_device_id mcs5000_ts_id[] = {
+ { "mcs5000_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
+
+static struct i2c_driver mcs5000_ts_driver = {
+ .probe = mcs5000_ts_probe,
+ .remove = __devexit_p(mcs5000_ts_remove),
+ .driver = {
+ .name = "mcs5000_ts",
+#ifdef CONFIG_PM
+ .pm = &mcs5000_ts_pm,
+#endif
+ },
+ .id_table = mcs5000_ts_id,
+};
+
+module_i2c_driver(mcs5000_ts_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/metusb/Makefile b/drivers/input/touchscreen/metusb/Makefile
new file mode 100755
index 00000000..f806933e
--- /dev/null
+++ b/drivers/input/touchscreen/metusb/Makefile
@@ -0,0 +1,33 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=metusb
+
+obj-m := $(MY_MODULE_NAME).o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers modules.builtin
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers modules.builtin
+
+
+
diff --git a/drivers/input/touchscreen/metusb/metusb.c b/drivers/input/touchscreen/metusb/metusb.c
new file mode 100755
index 00000000..e86bf03c
--- /dev/null
+++ b/drivers/input/touchscreen/metusb/metusb.c
@@ -0,0 +1,856 @@
+
+//#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/hid.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+
+
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "Xiaoyijian"
+#define DRIVER_DESC "Metouch USB Touchscreen Driver"
+
+static int swap_xy = 0;
+static int swapx=0;
+static int swapy=0;
+
+static int v_shift=0;
+static int v_flag=0;
+
+static int h_shift=0;
+static int h_flag=0;
+
+
+#define TP_TIMEOUT 30
+#define TP_TIMEROUT_MAX 3
+
+/* device specifc data/functions */
+struct usbtouch_usb;
+struct usbtouch_device_info {
+ int min_xc, max_xc;
+ int min_yc, max_yc;
+ int min_press, max_press;
+ int rept_size;
+
+ /*
+ * Always service the USB devices irq not just when the input device is
+ * open. This is useful when devices have a watchdog which prevents us
+ * from periodically polling the device. Leave this unset unless your
+ * touchscreen device requires it, as it does consume more of the USB
+ * bandwidth.
+ */
+ bool irq_always;
+
+ void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
+
+ /*
+ * used to get the packet len. possible return values:
+ * > 0: packet len
+ * = 0: skip one byte
+ * < 0: -return value more bytes needed
+ */
+ 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);
+};
+
+/* a usbtouch device */
+struct usbtouch_usb {
+ unsigned char *data;
+ dma_addr_t data_dma;
+ unsigned char *buffer;
+ int buf_len;
+ struct urb *irq;
+ struct usb_interface *interface;
+ struct input_dev *input;
+
+ struct workqueue_struct *tp_queue;
+ struct delayed_work tp_work;
+ struct mutex tp_timeout_mutex;
+ int tp_timer_count;
+
+ struct usbtouch_device_info *type;
+ char name[128];
+ char phys[64];
+ void *priv;
+ int x, y;
+ int touch, press;
+};
+
+#define DEVTYPE_METOUCH 0
+
+#define USB_DEVICE_HID_CLASS(vend, prod) \
+ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS \
+ | USB_DEVICE_ID_MATCH_DEVICE, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = USB_INTERFACE_CLASS_HID, \
+ .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE
+
+/* Define these values to match your devices */
+#define METOUCH_VENDOR_ID 0x5A53
+#define METOUCH_PRODUCT_ID 0x0001
+#define METUSB_MINOR_BASE 0x0
+
+static int metouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt);
+static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,unsigned char *pkt, int len);
+static void usbtouch_irq(struct urb *urb);
+static int usbtouch_open(struct input_dev *input);
+static void usbtouch_close(struct input_dev *input);
+static int usbtouch_suspend(struct usb_interface *intf, pm_message_t message);
+static int usbtouch_resume(struct usb_interface *intf);
+static int usbtouch_reset_resume(struct usb_interface *intf);
+static void usbtouch_free_buffers(struct usb_device *udev,struct usbtouch_usb *usbtouch);
+static struct usb_endpoint_descriptor *usbtouch_get_input_endpoint(struct usb_host_interface *interface);
+static int usbtouch_probe(struct usb_interface *intf,const struct usb_device_id *id);
+static void usbtouch_disconnect(struct usb_interface *intf);
+static int __init usbtouch_init(void);
+static void __exit usbtouch_cleanup(void);
+static void GetUserCfg(void);
+static void AnlysCmd(char *cmd);
+static int myatoi(char *str);
+static void DeleteAllSpace(char *cmd);
+int mypow(int t);
+
+
+static struct usbtouch_device_info usbtouch_dev_info[] = {
+ [DEVTYPE_METOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .read_data = metouch_read_data,
+ },
+};
+
+static struct usb_device_id metusb_table [] = {
+ { USB_DEVICE(METOUCH_VENDOR_ID, METOUCH_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, metusb_table);
+
+/*****************************************************************************
+ * METOUCH Part
+ */
+/*
+AA 55 XL XH YL YH BTN CRC
+****************************************************************************/
+static int metouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[3] << 8) | pkt[2];
+ dev->y = (pkt[5] << 8) | pkt[4];
+ dev->touch = (pkt[6] & 0x03) ? 1 : 0;
+
+ if (h_flag == 0) dev->x = dev->x + h_shift;
+ if (v_flag == 0) dev->y = dev->y + v_shift;
+
+ if (h_flag>0)
+ {
+ if (dev->x > h_shift) dev->x = dev->x - h_shift;
+ else dev->x=0;
+ }
+
+ if (v_flag>0)
+ {
+ if (dev->y > v_shift) dev->y = dev->y - v_shift;
+ else dev->y=0;
+ }
+
+ if (dev->x > 4095) dev->x=4095;
+ if (dev->y > 4095) dev->y=4095;
+
+ if (dev->x <0) dev->x=0;
+ if (dev->y <0) dev->y=0;
+
+ if (swapx>0) dev->x = 4095 - dev->x;
+ if (swapy>0) dev->y = 4095 - dev->y;
+
+ return 1;
+}
+
+
+/*****************************************************************************
+ * Generic Part
+ */
+static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len)
+{
+ struct usbtouch_device_info *type = usbtouch->type;
+
+ if (!type->read_data(usbtouch, pkt))
+ return;
+
+ mutex_lock(&usbtouch->tp_timeout_mutex);
+ if( usbtouch->tp_timer_count < 0 ){
+ queue_delayed_work(usbtouch->tp_queue, &usbtouch->tp_work, msecs_to_jiffies(TP_TIMEOUT));
+ }
+ usbtouch->tp_timer_count = TP_TIMEROUT_MAX;
+ mutex_unlock(&usbtouch->tp_timeout_mutex);
+
+ //input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
+ input_report_abs(usbtouch->input, ABS_MT_TRACKING_ID, 0);
+ if (swap_xy) {
+ //input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
+ //input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
+ input_report_abs(usbtouch->input, ABS_MT_POSITION_X, usbtouch->y );
+ input_report_abs(usbtouch->input, ABS_MT_POSITION_Y, usbtouch->x );
+ } else {
+ //input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
+ //input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
+ input_report_abs(usbtouch->input, ABS_MT_POSITION_X, usbtouch->x );
+ input_report_abs(usbtouch->input, ABS_MT_POSITION_Y, usbtouch->y );
+ }
+ //if (type->max_press)
+ // input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
+ //input_sync(usbtouch->input);
+ input_mt_sync(usbtouch->input);
+ input_sync(usbtouch->input);
+
+
+
+}
+
+
+static void usbtouch_irq(struct urb *urb)
+{
+ struct usbtouch_usb *usbtouch = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ETIME:
+ /* this urb is timing out */
+ dbg("%s - urb timed out - was the device unplugged?",
+ __func__);
+ return;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EPIPE:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ 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",
+ __func__, retval);
+}
+
+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)) {
+ r = -EIO;
+ goto out_put;
+ }
+ }
+
+ 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(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 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)
+{
+ usb_free_coherent(udev, usbtouch->type->rept_size,
+ usbtouch->data, usbtouch->data_dma);
+ kfree(usbtouch->buffer);
+}
+
+static struct usb_endpoint_descriptor *usbtouch_get_input_endpoint(struct usb_host_interface *interface)
+{
+ int i;
+
+ for (i = 0; i < interface->desc.bNumEndpoints; i++)
+ if (usb_endpoint_dir_in(&interface->endpoint[i].desc))
+ return &interface->endpoint[i].desc;
+
+ return NULL;
+}
+
+
+static void tp_timeout_func(struct work_struct *work){
+ struct usbtouch_usb *usbtouch =
+ container_of(work, struct usbtouch_usb, tp_work.work);
+ int button_up = -1;
+
+ mutex_lock(&usbtouch->tp_timeout_mutex);
+ button_up = --usbtouch->tp_timer_count;
+ mutex_unlock(&usbtouch->tp_timeout_mutex);
+
+ if( button_up < 0){
+ input_mt_sync(usbtouch->input);
+ input_sync(usbtouch->input);
+ }else{
+ queue_delayed_work(usbtouch->tp_queue, &usbtouch->tp_work, msecs_to_jiffies(TP_TIMEOUT));
+ }
+
+}
+
+static int usbtouch_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usbtouch_usb *usbtouch;
+ struct input_dev *input_dev;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usbtouch_device_info *type;
+ int err = -ENOMEM;
+
+ GetUserCfg();
+
+ /* some devices are ignored */
+ if (id->driver_info == -1)
+ return -ENODEV;
+
+ endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting);
+ if (!endpoint)
+ return -ENXIO;
+
+ usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!usbtouch || !input_dev)
+ goto out_free;
+
+ type = &usbtouch_dev_info[id->driver_info];
+ usbtouch->type = type;
+ if (!type->process_pkt)
+ type->process_pkt = usbtouch_process_pkt;
+
+ usbtouch->data = usb_alloc_coherent(udev, type->rept_size,
+ GFP_KERNEL, &usbtouch->data_dma);
+ if (!usbtouch->data)
+ goto out_free;
+
+ if (type->get_pkt_len) {
+ usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);
+ if (!usbtouch->buffer)
+ goto out_free_buffers;
+ }
+
+ usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usbtouch->irq) {
+ dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
+ goto out_free_buffers;
+ }
+
+ usbtouch->interface = intf;
+ usbtouch->input = input_dev;
+
+ if (udev->manufacturer)
+ strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
+ strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
+ }
+
+ if (!strlen(usbtouch->name))
+ snprintf(usbtouch->name, sizeof(usbtouch->name),
+ "USB Touchscreen %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));
+ strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
+
+ input_dev->name = usbtouch->name;
+ input_dev->phys = usbtouch->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+
+ input_set_drvdata(input_dev, usbtouch);
+
+ input_dev->open = usbtouch_open;
+ input_dev->close = usbtouch_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+
+ set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ set_bit(ABS_MT_POSITION_X, input_dev->absbit);
+ set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, type->min_xc, type->max_xc, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, type->min_yc, type->max_yc, 0, 0);
+
+ mutex_init(&usbtouch->tp_timeout_mutex);
+ usbtouch->tp_timer_count = -1;
+ usbtouch->tp_queue= create_singlethread_workqueue("tp_queue");
+ INIT_DELAYED_WORK(&usbtouch->tp_work, tp_timeout_func);
+ //queue_delayed_work(tp_queue, &tp_work, msecs_to_jiffies(TP_TIMEOUT));
+
+ //input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ //input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
+ //input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
+
+ //if (type->max_press)
+ // input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
+ // type->max_press, 0, 0);
+
+
+ if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
+ usb_fill_int_urb(usbtouch->irq, udev,
+ usb_rcvintpipe(udev, endpoint->bEndpointAddress),
+ usbtouch->data, type->rept_size,
+ usbtouch_irq, usbtouch, endpoint->bInterval);
+ else
+ usb_fill_bulk_urb(usbtouch->irq, udev,
+ usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
+ usbtouch->data, type->rept_size,
+ usbtouch_irq, usbtouch);
+
+ usbtouch->irq->dev = udev;
+ usbtouch->irq->transfer_dma = usbtouch->data_dma;
+ usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* 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_do_exit;
+ }
+ }
+
+ err = input_register_device(usbtouch->input);
+ if (err) {
+ dbg("%s - input_register_device failed, err: %d", __func__, err);
+ goto out_do_exit;
+ }
+
+ 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;
+ }
+ }
+
+ return 0;
+
+out_unregister_input:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+out_do_exit:
+ if (type->exit)
+ type->exit(usbtouch);
+out_free_urb:
+ usb_free_urb(usbtouch->irq);
+out_free_buffers:
+ usbtouch_free_buffers(udev, usbtouch);
+out_free:
+ input_free_device(input_dev);
+ kfree(usbtouch);
+ return err;
+}
+
+static void usbtouch_disconnect(struct usb_interface *intf)
+{
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+
+ dbg("%s - called", __func__);
+
+ if (!usbtouch)
+ return;
+
+ dbg("%s - usbtouch is initialized, cleaning up", __func__);
+ mutex_destroy(&usbtouch->tp_timeout_mutex);
+ usb_set_intfdata(intf, NULL);
+ /* this will stop IO via close */
+ input_unregister_device(usbtouch->input);
+ usb_free_urb(usbtouch->irq);
+ if (usbtouch->type->exit)
+ usbtouch->type->exit(usbtouch);
+ usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
+ kfree(usbtouch);
+}
+
+
+static void GetUserCfg(void)
+{
+struct file *fd;
+char buffer[256];
+loff_t pos;
+
+mm_segment_t old_fs = get_fs();
+set_fs(KERNEL_DS);
+
+memset((char *)&buffer[0],0,256);
+
+fd= filp_open("/etc/metouch.cfg",O_RDONLY,0);
+
+if (IS_ERR(fd))
+ {
+ printk("Unable to open metouch config information.\n");
+ goto exithere;
+ }
+else
+ {
+ printk("open metouch config file ok.\n");
+
+ pos=0;
+ vfs_read(fd,buffer,256,&pos);
+
+ if (pos < 0)
+ {
+ printk("reach the end of file\n");
+ }
+ else
+ {
+ AnlysCmd(buffer);
+ }
+ filp_close(fd,NULL);
+ }
+exithere:;
+set_fs(old_fs);
+}
+
+static void AnlysCmd(char *cmd)
+{
+//static int swapx=0;
+//static int swapy=0;
+//static int v_shift=0;
+//static int h_shift=0;
+char *pstr=NULL;
+char upx[10];
+char upy[10];
+char vsh[10];
+char hsh[10];
+int i=0;
+
+//clear all invisible char
+DeleteAllSpace(cmd);
+
+//find UPSIDEX
+pstr=NULL;
+pstr=strstr((const char*)cmd,(const char*)"UPSIDEX=");
+if (pstr!=NULL)
+{
+strncpy(upx,(char *)(pstr+strlen("UPSIDEX=")),4);
+
+i=0;
+while (i<4)
+ {
+ if (upx[i]<0x20)
+ {
+ upx[i]=0;
+ swapx = myatoi(upx);
+ break;
+ }
+ i++;
+ }
+}
+
+//find UPSIDEY
+pstr=NULL;
+pstr=strstr((const char*)cmd,(const char*)"UPSIDEY=");
+if (pstr!=NULL)
+{
+strncpy(upy,(char *)(pstr+strlen("UPSIDEY=")),4);
+
+i=0;
+while (i<4)
+ {
+ if (upy[i]<0x20)
+ {
+ upy[i]=0;
+ swapy = myatoi(upy);
+ break;
+ }
+ i++;
+ }
+}
+
+//find V_SHIFT
+pstr=NULL;
+pstr=strstr((const char*)cmd,(const char*)"V_SHIFT=");
+if (pstr!=NULL)
+{
+strncpy(vsh,(char *)(pstr+strlen("V_SHIFT=")),4);
+i=0;
+while (i<4)
+ {
+ if (vsh[i]<0x20)
+ {
+ vsh[i]=0;
+ v_shift = myatoi(vsh);
+ break;
+ }
+ i++;
+ }
+}
+
+//find H_SHIFT
+pstr=NULL;
+pstr=strstr((const char*)cmd,(const char*)"H_SHIFT=");
+if (pstr!=NULL)
+{
+strncpy(hsh,(char *)(pstr+strlen("H_SHIFT=")),4);
+i=0;
+while (i<4)
+ {
+ if (hsh[i]<0x20)
+ {
+ hsh[i]=0;
+ h_shift = myatoi(hsh);
+ break;
+ }
+ i++;
+ }
+}
+//v_flag
+pstr=NULL;
+pstr=strstr((const char*)cmd,(const char*)"V_FLAG=");
+if (pstr!=NULL)
+{
+strncpy(hsh,(char *)(pstr+strlen("V_FLAG=")),4);
+
+i=0;
+while (i<4)
+ {
+ if (hsh[i]<0x20)
+ {
+ hsh[i]=0;
+ v_flag = myatoi(hsh);
+ break;
+ }
+ i++;
+ }
+}
+
+//H_flag
+pstr=NULL;
+pstr=strstr((const char*)cmd,(const char*)"H_FLAG=");
+if (pstr!=NULL)
+{
+strncpy(hsh,(char *)(pstr+strlen("H_FLAG=")),4);
+i=0;
+while (i<4)
+ {
+ if (hsh[i]<0x20)
+ {
+ hsh[i]=0;
+ h_flag = myatoi(hsh);
+ break;
+ }
+ i++;
+ }
+}
+
+printk("%d\n",v_flag);
+printk("%d\n",h_flag);
+printk("%d\n",swapx);
+printk("%d\n",swapy);
+printk("%d\n",v_shift);
+printk("%d\n",h_shift);
+}
+
+static void DeleteAllSpace(char *cmd)
+{
+char tmp[256];
+int i=0;
+int j=0;
+memset((char *)&tmp,0,256);
+for (i=0;i<250;i++)
+ {
+ //printk("%02x ",cmd[i]&0xff);
+
+ if ((cmd[i]!=0x09) && (cmd[i]!=0x20))
+ {
+ tmp[j]=cmd[i];
+ j++;
+ }
+ }
+//printk("%s\n",cmd);
+
+//printk("\n");
+tmp[j]=0;
+//for (i=0;i<250;i++)
+// {
+ //printk("%02x ",tmp[i]&0xff);
+// }
+//printk("%s\n",tmp);
+
+strncpy(cmd,tmp,250);
+}
+
+static int myatoi(char *str)
+{
+int i;
+int num=0;
+int len = strlen(str);
+
+
+for (i=0;i<len;i++)
+ {
+ // printk("%02x ",str[i]);
+ if (str[i]<0x30)
+ break;
+ else
+ {
+ // printk("s=%x d=%x s=%x ",str[i],str[i]-0x30,mypow(len-i-1));
+ num = num + mypow(i)*(str[len-i-1]-0x30);
+ // printk("num = %d\n",num);
+ }
+ }
+//printk("\n");
+return num;
+}
+
+int mypow(int t)
+{
+if (t==0) return 1;
+if (t==1) return 10;
+if (t==2) return 100;
+if (t==3) return 1000;
+if (t==4) return 10000;
+else
+return 0;
+}
+
+static struct usb_driver usbtouch_driver = {
+ .name = "metusb",
+ .probe = usbtouch_probe,
+ .disconnect = usbtouch_disconnect,
+ .suspend = usbtouch_suspend,
+ .resume = usbtouch_resume,
+ .reset_resume = usbtouch_reset_resume,
+ .id_table = metusb_table,
+ .supports_autosuspend = 1,
+};
+
+
+static int __init usbtouch_init(void)
+{
+ return usb_register(&usbtouch_driver);
+}
+
+static void __exit usbtouch_cleanup(void)
+{
+ usb_deregister(&usbtouch_driver);
+}
+
+module_init(usbtouch_init);
+module_exit(usbtouch_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("metusb");
diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c
new file mode 100644
index 00000000..c038db93
--- /dev/null
+++ b/drivers/input/touchscreen/migor_ts.c
@@ -0,0 +1,249 @@
+/*
+ * Touch Screen driver for Renesas MIGO-R Platform
+ *
+ * Copyright (c) 2008 Magnus Damm
+ * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>,
+ * Kenati Technologies Pvt Ltd.
+ *
+ * This file 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 file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+
+#define EVENT_PENDOWN 1
+#define EVENT_REPEAT 2
+#define EVENT_PENUP 3
+
+struct migor_ts_priv {
+ struct i2c_client *client;
+ struct input_dev *input;
+ int irq;
+};
+
+static const u_int8_t migor_ts_ena_seq[17] = { 0x33, 0x22, 0x11,
+ 0x01, 0x06, 0x07, };
+static const u_int8_t migor_ts_dis_seq[17] = { };
+
+static irqreturn_t migor_ts_isr(int irq, void *dev_id)
+{
+ struct migor_ts_priv *priv = dev_id;
+ unsigned short xpos, ypos;
+ unsigned char event;
+ u_int8_t buf[16];
+
+ /*
+ * The touch screen controller chip is hooked up to the CPU
+ * using I2C and a single interrupt line. The interrupt line
+ * is pulled low whenever someone taps the screen. To deassert
+ * the interrupt line we need to acknowledge the interrupt by
+ * communicating with the controller over the slow i2c bus.
+ *
+ * Since I2C bus controller may sleep we are using threaded
+ * IRQ here.
+ */
+
+ memset(buf, 0, sizeof(buf));
+
+ /* Set Index 0 */
+ buf[0] = 0;
+ if (i2c_master_send(priv->client, buf, 1) != 1) {
+ dev_err(&priv->client->dev, "Unable to write i2c index\n");
+ goto out;
+ }
+
+ /* Now do Page Read */
+ if (i2c_master_recv(priv->client, buf, sizeof(buf)) != sizeof(buf)) {
+ dev_err(&priv->client->dev, "Unable to read i2c page\n");
+ goto out;
+ }
+
+ ypos = ((buf[9] & 0x03) << 8 | buf[8]);
+ xpos = ((buf[11] & 0x03) << 8 | buf[10]);
+ event = buf[12];
+
+ switch (event) {
+ case EVENT_PENDOWN:
+ case EVENT_REPEAT:
+ input_report_key(priv->input, BTN_TOUCH, 1);
+ input_report_abs(priv->input, ABS_X, ypos); /*X-Y swap*/
+ input_report_abs(priv->input, ABS_Y, xpos);
+ input_sync(priv->input);
+ break;
+
+ case EVENT_PENUP:
+ input_report_key(priv->input, BTN_TOUCH, 0);
+ input_sync(priv->input);
+ break;
+ }
+
+ out:
+ return IRQ_HANDLED;
+}
+
+static int migor_ts_open(struct input_dev *dev)
+{
+ struct migor_ts_priv *priv = input_get_drvdata(dev);
+ struct i2c_client *client = priv->client;
+ int count;
+
+ /* enable controller */
+ count = i2c_master_send(client, migor_ts_ena_seq,
+ sizeof(migor_ts_ena_seq));
+ if (count != sizeof(migor_ts_ena_seq)) {
+ dev_err(&client->dev, "Unable to enable touchscreen.\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void migor_ts_close(struct input_dev *dev)
+{
+ struct migor_ts_priv *priv = input_get_drvdata(dev);
+ struct i2c_client *client = priv->client;
+
+ disable_irq(priv->irq);
+
+ /* disable controller */
+ i2c_master_send(client, migor_ts_dis_seq, sizeof(migor_ts_dis_seq));
+
+ enable_irq(priv->irq);
+}
+
+static int migor_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *idp)
+{
+ struct migor_ts_priv *priv;
+ struct input_dev *input;
+ int error;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!priv || !input) {
+ dev_err(&client->dev, "failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ priv->client = client;
+ priv->input = input;
+ priv->irq = client->irq;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ __set_bit(BTN_TOUCH, input->keybit);
+
+ input_set_abs_params(input, ABS_X, 95, 955, 0, 0);
+ input_set_abs_params(input, ABS_Y, 85, 935, 0, 0);
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+
+ input->open = migor_ts_open;
+ input->close = migor_ts_close;
+
+ input_set_drvdata(input, priv);
+
+ error = request_threaded_irq(priv->irq, NULL, migor_ts_isr,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, priv);
+ if (error) {
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input);
+ if (error)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, priv);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+
+ err_free_irq:
+ free_irq(priv->irq, priv);
+ err_free_mem:
+ input_free_device(input);
+ kfree(priv);
+ return error;
+}
+
+static int migor_ts_remove(struct i2c_client *client)
+{
+ struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+ free_irq(priv->irq, priv);
+ input_unregister_device(priv->input);
+ kfree(priv);
+
+ dev_set_drvdata(&client->dev, NULL);
+
+ return 0;
+}
+
+static int migor_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+static int migor_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume);
+
+static const struct i2c_device_id migor_ts_id[] = {
+ { "migor_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, migor_ts);
+
+static struct i2c_driver migor_ts_driver = {
+ .driver = {
+ .name = "migor_ts",
+ .pm = &migor_ts_pm,
+ },
+ .probe = migor_ts_probe,
+ .remove = migor_ts_remove,
+ .id_table = migor_ts_id,
+};
+
+module_i2c_driver(migor_ts_driver);
+
+MODULE_DESCRIPTION("MigoR Touchscreen driver");
+MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c
new file mode 100644
index 00000000..36e57dea
--- /dev/null
+++ b/drivers/input/touchscreen/mk712.c
@@ -0,0 +1,219 @@
+/*
+ * ICS MK712 touchscreen controller driver
+ *
+ * Copyright (c) 1999-2002 Transmeta Corporation
+ * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ */
+
+/*
+ * 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 driver supports the ICS MicroClock MK712 TouchScreen controller,
+ * found in Gateway AOL Connected Touchpad computers.
+ *
+ * Documentation for ICS MK712 can be found at:
+ * http://www.idt.com/products/getDoc.cfm?docID=18713923
+ */
+
+/*
+ * 1999-12-18: original version, Daniel Quinlan
+ * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
+ * to use queue_empty, Nathan Laredo
+ * 1999-12-20: improved random point rejection, Nathan Laredo
+ * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
+ * queue code, added module options, other fixes, Daniel Quinlan
+ * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
+ * Fixed multi open race, fixed memory checks, fixed resource
+ * allocation, fixed close/powerdown bug, switched to new init
+ * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
+ * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */
+module_param_named(io, mk712_io, uint, 0);
+MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
+
+static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */
+module_param_named(irq, mk712_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
+
+/* eight 8-bit registers */
+#define MK712_STATUS 0
+#define MK712_X 2
+#define MK712_Y 4
+#define MK712_CONTROL 6
+#define MK712_RATE 7
+
+/* status */
+#define MK712_STATUS_TOUCH 0x10
+#define MK712_CONVERSION_COMPLETE 0x80
+
+/* control */
+#define MK712_ENABLE_INT 0x01
+#define MK712_INT_ON_CONVERSION_COMPLETE 0x02
+#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04
+#define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10
+#define MK712_READ_ONE_POINT 0x20
+#define MK712_POWERUP 0x40
+
+static struct input_dev *mk712_dev;
+static DEFINE_SPINLOCK(mk712_lock);
+
+static irqreturn_t mk712_interrupt(int irq, void *dev_id)
+{
+ unsigned char status;
+ static int debounce = 1;
+ static unsigned short last_x;
+ static unsigned short last_y;
+
+ spin_lock(&mk712_lock);
+
+ status = inb(mk712_io + MK712_STATUS);
+
+ if (~status & MK712_CONVERSION_COMPLETE) {
+ debounce = 1;
+ goto end;
+ }
+
+ if (~status & MK712_STATUS_TOUCH) {
+ debounce = 1;
+ input_report_key(mk712_dev, BTN_TOUCH, 0);
+ goto end;
+ }
+
+ if (debounce) {
+ debounce = 0;
+ goto end;
+ }
+
+ input_report_key(mk712_dev, BTN_TOUCH, 1);
+ input_report_abs(mk712_dev, ABS_X, last_x);
+ input_report_abs(mk712_dev, ABS_Y, last_y);
+
+ end:
+ last_x = inw(mk712_io + MK712_X) & 0x0fff;
+ last_y = inw(mk712_io + MK712_Y) & 0x0fff;
+ input_sync(mk712_dev);
+ spin_unlock(&mk712_lock);
+ return IRQ_HANDLED;
+}
+
+static int mk712_open(struct input_dev *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mk712_lock, flags);
+
+ outb(0, mk712_io + MK712_CONTROL); /* Reset */
+
+ outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
+ MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
+ MK712_ENABLE_PERIODIC_CONVERSIONS |
+ MK712_POWERUP, mk712_io + MK712_CONTROL);
+
+ outb(10, mk712_io + MK712_RATE); /* 187 points per second */
+
+ spin_unlock_irqrestore(&mk712_lock, flags);
+
+ return 0;
+}
+
+static void mk712_close(struct input_dev *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mk712_lock, flags);
+
+ outb(0, mk712_io + MK712_CONTROL);
+
+ spin_unlock_irqrestore(&mk712_lock, flags);
+}
+
+static int __init mk712_init(void)
+{
+ int err;
+
+ if (!request_region(mk712_io, 8, "mk712")) {
+ printk(KERN_WARNING "mk712: unable to get IO region\n");
+ return -ENODEV;
+ }
+
+ outb(0, mk712_io + MK712_CONTROL);
+
+ if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */
+ (inw(mk712_io + MK712_Y) & 0xf000) ||
+ (inw(mk712_io + MK712_STATUS) & 0xf333)) {
+ printk(KERN_WARNING "mk712: device not present\n");
+ err = -ENODEV;
+ goto fail1;
+ }
+
+ mk712_dev = input_allocate_device();
+ if (!mk712_dev) {
+ printk(KERN_ERR "mk712: not enough memory\n");
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ mk712_dev->name = "ICS MicroClock MK712 TouchScreen";
+ mk712_dev->phys = "isa0260/input0";
+ mk712_dev->id.bustype = BUS_ISA;
+ mk712_dev->id.vendor = 0x0005;
+ mk712_dev->id.product = 0x0001;
+ mk712_dev->id.version = 0x0100;
+
+ mk712_dev->open = mk712_open;
+ mk712_dev->close = mk712_close;
+
+ mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0);
+ input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0);
+
+ if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) {
+ printk(KERN_WARNING "mk712: unable to get IRQ\n");
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ err = input_register_device(mk712_dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: free_irq(mk712_irq, mk712_dev);
+ fail1: input_free_device(mk712_dev);
+ release_region(mk712_io, 8);
+ return err;
+}
+
+static void __exit mk712_exit(void)
+{
+ input_unregister_device(mk712_dev);
+ free_irq(mk712_irq, mk712_dev);
+ release_region(mk712_io, 8);
+}
+
+module_init(mk712_init);
+module_exit(mk712_exit);
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
new file mode 100644
index 00000000..90772284
--- /dev/null
+++ b/drivers/input/touchscreen/mtouch.c
@@ -0,0 +1,220 @@
+/*
+ * MicroTouch (3M) serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2005/02/19 Dan Streetman <ddstreet@ieee.org>
+ * Copied elo.c and edited for MicroTouch protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "MicroTouch serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80
+#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40
+#define MTOUCH_FORMAT_TABLET_LENGTH 5
+#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01
+#define MTOUCH_RESPONSE_END_BYTE 0x0d
+
+/* todo: check specs for max length of all responses */
+#define MTOUCH_MAX_LENGTH 16
+
+#define MTOUCH_MIN_XC 0
+#define MTOUCH_MAX_XC 0x3fff
+#define MTOUCH_MIN_YC 0
+#define MTOUCH_MAX_YC 0x3fff
+
+#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1])
+#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3])
+#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct mtouch {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[MTOUCH_MAX_LENGTH];
+ char phys[32];
+};
+
+static void mtouch_process_format_tablet(struct mtouch *mtouch)
+{
+ struct input_dev *dev = mtouch->dev;
+
+ if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) {
+ input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data));
+ input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data));
+ input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data));
+ input_sync(dev);
+
+ mtouch->idx = 0;
+ }
+}
+
+static void mtouch_process_response(struct mtouch *mtouch)
+{
+ if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) {
+ /* FIXME - process response */
+ mtouch->idx = 0;
+ } else if (MTOUCH_MAX_LENGTH == mtouch->idx) {
+ printk(KERN_ERR "mtouch.c: too many response bytes\n");
+ mtouch->idx = 0;
+ }
+}
+
+static irqreturn_t mtouch_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct mtouch* mtouch = serio_get_drvdata(serio);
+
+ mtouch->data[mtouch->idx] = data;
+
+ if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0])
+ mtouch_process_format_tablet(mtouch);
+ else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0])
+ mtouch_process_response(mtouch);
+ else
+ printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * mtouch_disconnect() is the opposite of mtouch_connect()
+ */
+
+static void mtouch_disconnect(struct serio *serio)
+{
+ struct mtouch* mtouch = serio_get_drvdata(serio);
+
+ input_get_device(mtouch->dev);
+ input_unregister_device(mtouch->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(mtouch->dev);
+ kfree(mtouch);
+}
+
+/*
+ * mtouch_connect() is the routine that is called when someone adds a
+ * new serio device that supports MicroTouch (Format Tablet) protocol and registers it as
+ * an input device.
+ */
+
+static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct mtouch *mtouch;
+ struct input_dev *input_dev;
+ int err;
+
+ mtouch = kzalloc(sizeof(struct mtouch), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!mtouch || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ mtouch->serio = serio;
+ mtouch->dev = input_dev;
+ snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "MicroTouch Serial TouchScreen";
+ input_dev->phys = mtouch->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_MICROTOUCH;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
+ input_set_abs_params(mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, mtouch);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(mtouch->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(mtouch);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id mtouch_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MICROTOUCH,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, mtouch_serio_ids);
+
+static struct serio_driver mtouch_drv = {
+ .driver = {
+ .name = "mtouch",
+ },
+ .description = DRIVER_DESC,
+ .id_table = mtouch_serio_ids,
+ .interrupt = mtouch_interrupt,
+ .connect = mtouch_connect,
+ .disconnect = mtouch_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init mtouch_init(void)
+{
+ return serio_register_driver(&mtouch_drv);
+}
+
+static void __exit mtouch_exit(void)
+{
+ serio_unregister_driver(&mtouch_drv);
+}
+
+module_init(mtouch_init);
+module_exit(mtouch_exit);
diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c
new file mode 100644
index 00000000..f57aeb80
--- /dev/null
+++ b/drivers/input/touchscreen/pcap_ts.c
@@ -0,0 +1,260 @@
+/*
+ * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform.
+ *
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/ezx-pcap.h>
+
+struct pcap_ts {
+ struct pcap_chip *pcap;
+ struct input_dev *input;
+ struct delayed_work work;
+ u16 x, y;
+ u16 pressure;
+ u8 read_state;
+};
+
+#define SAMPLE_DELAY 20 /* msecs */
+
+#define X_AXIS_MIN 0
+#define X_AXIS_MAX 1023
+#define Y_AXIS_MAX X_AXIS_MAX
+#define Y_AXIS_MIN X_AXIS_MIN
+#define PRESSURE_MAX X_AXIS_MAX
+#define PRESSURE_MIN X_AXIS_MIN
+
+static void pcap_ts_read_xy(void *data, u16 res[2])
+{
+ struct pcap_ts *pcap_ts = data;
+
+ switch (pcap_ts->read_state) {
+ case PCAP_ADC_TS_M_PRESSURE:
+ /* pressure reading is unreliable */
+ if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX)
+ pcap_ts->pressure = res[0];
+ pcap_ts->read_state = PCAP_ADC_TS_M_XY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ break;
+ case PCAP_ADC_TS_M_XY:
+ pcap_ts->y = res[0];
+ pcap_ts->x = res[1];
+ if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX ||
+ pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) {
+ /* pen has been released */
+ input_report_abs(pcap_ts->input, ABS_PRESSURE, 0);
+ input_report_key(pcap_ts->input, BTN_TOUCH, 0);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ } else {
+ /* pen is touching the screen */
+ input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x);
+ input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y);
+ input_report_key(pcap_ts->input, BTN_TOUCH, 1);
+ input_report_abs(pcap_ts->input, ABS_PRESSURE,
+ pcap_ts->pressure);
+
+ /* switch back to pressure read mode */
+ pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+ schedule_delayed_work(&pcap_ts->work,
+ msecs_to_jiffies(SAMPLE_DELAY));
+ }
+ input_sync(pcap_ts->input);
+ break;
+ default:
+ dev_warn(&pcap_ts->input->dev,
+ "pcap_ts: Warning, unhandled read_state %d\n",
+ pcap_ts->read_state);
+ break;
+ }
+}
+
+static void pcap_ts_work(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
+ u8 ch[2];
+
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+ if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY)
+ return;
+
+ /* start adc conversion */
+ ch[0] = PCAP_ADC_CH_TS_X1;
+ ch[1] = PCAP_ADC_CH_TS_Y1;
+ pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch,
+ pcap_ts_read_xy, pcap_ts);
+}
+
+static irqreturn_t pcap_ts_event_touch(int pirq, void *data)
+{
+ struct pcap_ts *pcap_ts = data;
+
+ if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) {
+ pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+ schedule_delayed_work(&pcap_ts->work, 0);
+ }
+ return IRQ_HANDLED;
+}
+
+static int pcap_ts_open(struct input_dev *dev)
+{
+ struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+ schedule_delayed_work(&pcap_ts->work, 0);
+
+ return 0;
+}
+
+static void pcap_ts_close(struct input_dev *dev)
+{
+ struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&pcap_ts->work);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+}
+
+static int __devinit pcap_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *input_dev;
+ struct pcap_ts *pcap_ts;
+ int err = -ENOMEM;
+
+ pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL);
+ if (!pcap_ts)
+ return err;
+
+ pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, pcap_ts);
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work);
+
+ pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+ pcap_ts->input = input_dev;
+ input_set_drvdata(input_dev, pcap_ts);
+
+ input_dev->name = "pcap-touchscreen";
+ input_dev->phys = "pcap_ts/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0002;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = pcap_ts_open;
+ input_dev->close = pcap_ts_close;
+
+ 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, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN,
+ PRESSURE_MAX, 0, 0);
+
+ err = input_register_device(pcap_ts->input);
+ if (err)
+ goto fail_allocate;
+
+ err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS),
+ pcap_ts_event_touch, 0, "Touch Screen", pcap_ts);
+ if (err)
+ goto fail_register;
+
+ return 0;
+
+fail_register:
+ input_unregister_device(input_dev);
+ goto fail;
+fail_allocate:
+ input_free_device(input_dev);
+fail:
+ kfree(pcap_ts);
+
+ return err;
+}
+
+static int __devexit pcap_ts_remove(struct platform_device *pdev)
+{
+ struct pcap_ts *pcap_ts = platform_get_drvdata(pdev);
+
+ free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts);
+ cancel_delayed_work_sync(&pcap_ts->work);
+
+ input_unregister_device(pcap_ts->input);
+
+ kfree(pcap_ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcap_ts_suspend(struct device *dev)
+{
+ struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+ pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR);
+ return 0;
+}
+
+static int pcap_ts_resume(struct device *dev)
+{
+ struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+ pcap_set_ts_bits(pcap_ts->pcap,
+ pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+ return 0;
+}
+
+static const struct dev_pm_ops pcap_ts_pm_ops = {
+ .suspend = pcap_ts_suspend,
+ .resume = pcap_ts_resume,
+};
+#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops)
+#else
+#define PCAP_TS_PM_OPS NULL
+#endif
+
+static struct platform_driver pcap_ts_driver = {
+ .probe = pcap_ts_probe,
+ .remove = __devexit_p(pcap_ts_remove),
+ .driver = {
+ .name = "pcap-ts",
+ .owner = THIS_MODULE,
+ .pm = PCAP_TS_PM_OPS,
+ },
+};
+module_platform_driver(pcap_ts_driver);
+
+MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");
+MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcap_ts");
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
new file mode 100644
index 00000000..4c012fb2
--- /dev/null
+++ b/drivers/input/touchscreen/penmount.c
@@ -0,0 +1,335 @@
+/*
+ * Penmount serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2011 John Sung <penmount.touch@gmail.com>
+ *
+ * Based on ELO driver (drivers/input/touchscreen/elo.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "PenMount serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_AUTHOR("John Sung <penmount.touch@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define PM_MAX_LENGTH 6
+#define PM_MAX_MTSLOT 16
+#define PM_3000_MTSLOT 2
+#define PM_6250_MTSLOT 12
+
+/*
+ * Multi-touch slot
+ */
+
+struct mt_slot {
+ unsigned short x, y;
+ bool active; /* is the touch valid? */
+};
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct pm {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[PM_MAX_LENGTH];
+ char phys[32];
+ unsigned char packetsize;
+ unsigned char maxcontacts;
+ struct mt_slot slots[PM_MAX_MTSLOT];
+ void (*parse_packet)(struct pm *);
+};
+
+/*
+ * pm_mtevent() sends mt events and also emulates pointer movement
+ */
+
+static void pm_mtevent(struct pm *pm, struct input_dev *input)
+{
+ int i;
+
+ for (i = 0; i < pm->maxcontacts; ++i) {
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER,
+ pm->slots[i].active);
+ if (pm->slots[i].active) {
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, pm->slots[i].x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, pm->slots[i].y);
+ }
+ }
+
+ input_mt_report_pointer_emulation(input, true);
+ input_sync(input);
+}
+
+/*
+ * pm_checkpacket() checks if data packet is valid
+ */
+
+static bool pm_checkpacket(unsigned char *packet)
+{
+ int total = 0;
+ int i;
+
+ for (i = 0; i < 5; i++)
+ total += packet[i];
+
+ return packet[5] == (unsigned char)~(total & 0xff);
+}
+
+static void pm_parse_9000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0x80) && pm->packetsize == ++pm->idx) {
+ input_report_abs(dev, ABS_X, pm->data[1] * 128 + pm->data[2]);
+ input_report_abs(dev, ABS_Y, pm->data[3] * 128 + pm->data[4]);
+ input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+ input_sync(dev);
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_6000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xbf) == 0x30 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ input_report_abs(dev, ABS_X,
+ pm->data[2] * 256 + pm->data[1]);
+ input_report_abs(dev, ABS_Y,
+ pm->data[4] * 256 + pm->data[3]);
+ input_report_key(dev, BTN_TOUCH, pm->data[0] & 0x40);
+ input_sync(dev);
+ }
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_3000(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xce) == 0x40 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ int slotnum = pm->data[0] & 0x0f;
+ pm->slots[slotnum].active = pm->data[0] & 0x30;
+ pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+ pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+ pm_mtevent(pm, dev);
+ }
+ pm->idx = 0;
+ }
+}
+
+static void pm_parse_6250(struct pm *pm)
+{
+ struct input_dev *dev = pm->dev;
+
+ if ((pm->data[0] & 0xb0) == 0x30 && pm->packetsize == ++pm->idx) {
+ if (pm_checkpacket(pm->data)) {
+ int slotnum = pm->data[0] & 0x0f;
+ pm->slots[slotnum].active = pm->data[0] & 0x40;
+ pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+ pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+ pm_mtevent(pm, dev);
+ }
+ pm->idx = 0;
+ }
+}
+
+static irqreturn_t pm_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+
+ pm->data[pm->idx] = data;
+
+ pm->parse_packet(pm);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * pm_disconnect() is the opposite of pm_connect()
+ */
+
+static void pm_disconnect(struct serio *serio)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+
+ serio_close(serio);
+
+ input_unregister_device(pm->dev);
+ kfree(pm);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+/*
+ * pm_connect() is the routine that is called when someone adds a
+ * new serio device that supports PenMount protocol and registers it as
+ * an input device.
+ */
+
+static int pm_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct pm *pm;
+ struct input_dev *input_dev;
+ int max_x, max_y;
+ int err;
+
+ pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pm || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pm->serio = serio;
+ pm->dev = input_dev;
+ snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
+ pm->maxcontacts = 1;
+
+ input_dev->name = "PenMount Serial TouchScreen";
+ input_dev->phys = pm->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_PENMOUNT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ switch (serio->id.id) {
+ default:
+ case 0:
+ pm->packetsize = 5;
+ pm->parse_packet = pm_parse_9000;
+ input_dev->id.product = 0x9000;
+ max_x = max_y = 0x3ff;
+ break;
+
+ case 1:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_6000;
+ input_dev->id.product = 0x6000;
+ max_x = max_y = 0x3ff;
+ break;
+
+ case 2:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_3000;
+ input_dev->id.product = 0x3000;
+ max_x = max_y = 0x7ff;
+ pm->maxcontacts = PM_3000_MTSLOT;
+ break;
+
+ case 3:
+ pm->packetsize = 6;
+ pm->parse_packet = pm_parse_6250;
+ input_dev->id.product = 0x6250;
+ max_x = max_y = 0x3ff;
+ pm->maxcontacts = PM_6250_MTSLOT;
+ break;
+ }
+
+ input_set_abs_params(pm->dev, ABS_X, 0, max_x, 0, 0);
+ input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
+
+ if (pm->maxcontacts > 1) {
+ input_mt_init_slots(pm->dev, pm->maxcontacts);
+ input_set_abs_params(pm->dev,
+ ABS_MT_POSITION_X, 0, max_x, 0, 0);
+ input_set_abs_params(pm->dev,
+ ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+ }
+
+ serio_set_drvdata(serio, pm);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pm->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pm);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id pm_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PENMOUNT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pm_serio_ids);
+
+static struct serio_driver pm_drv = {
+ .driver = {
+ .name = "serio-penmount",
+ },
+ .description = DRIVER_DESC,
+ .id_table = pm_serio_ids,
+ .interrupt = pm_interrupt,
+ .connect = pm_connect,
+ .disconnect = pm_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init pm_init(void)
+{
+ return serio_register_driver(&pm_drv);
+}
+
+static void __exit pm_exit(void)
+{
+ serio_unregister_driver(&pm_drv);
+}
+
+module_init(pm_init);
+module_exit(pm_exit);
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
new file mode 100644
index 00000000..72f6ba3a
--- /dev/null
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -0,0 +1,229 @@
+/*
+ * Driver for Pixcir I2C touchscreen controllers.
+ *
+ * Copyright (C) 2010-2011 Pixcir, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/pixcir_ts.h>
+
+struct pixcir_i2c_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ const struct pixcir_ts_platform_data *chip;
+ bool exiting;
+};
+
+static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
+{
+ struct pixcir_i2c_ts_data *tsdata = data;
+ u8 rdbuf[10], wrbuf[1] = { 0 };
+ u8 touch;
+ int ret;
+
+ ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
+ if (ret != sizeof(wrbuf)) {
+ dev_err(&tsdata->client->dev,
+ "%s: i2c_master_send failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
+
+ ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf));
+ if (ret != sizeof(rdbuf)) {
+ dev_err(&tsdata->client->dev,
+ "%s: i2c_master_recv failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
+
+ touch = rdbuf[0];
+ if (touch) {
+ u16 posx1 = (rdbuf[3] << 8) | rdbuf[2];
+ u16 posy1 = (rdbuf[5] << 8) | rdbuf[4];
+ u16 posx2 = (rdbuf[7] << 8) | rdbuf[6];
+ u16 posy2 = (rdbuf[9] << 8) | rdbuf[8];
+
+ input_report_key(tsdata->input, BTN_TOUCH, 1);
+ input_report_abs(tsdata->input, ABS_X, posx1);
+ input_report_abs(tsdata->input, ABS_Y, posy1);
+
+ input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1);
+ input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1);
+ input_mt_sync(tsdata->input);
+
+ if (touch == 2) {
+ input_report_abs(tsdata->input,
+ ABS_MT_POSITION_X, posx2);
+ input_report_abs(tsdata->input,
+ ABS_MT_POSITION_Y, posy2);
+ input_mt_sync(tsdata->input);
+ }
+ } else {
+ input_report_key(tsdata->input, BTN_TOUCH, 0);
+ }
+
+ input_sync(tsdata->input);
+}
+
+static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
+{
+ struct pixcir_i2c_ts_data *tsdata = dev_id;
+
+ while (!tsdata->exiting) {
+ pixcir_ts_poscheck(tsdata);
+
+ if (tsdata->chip->attb_read_val())
+ break;
+
+ msleep(20);
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pixcir_i2c_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int pixcir_i2c_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
+ pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
+
+static int __devinit pixcir_i2c_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct pixcir_ts_platform_data *pdata = client->dev.platform_data;
+ struct pixcir_i2c_ts_data *tsdata;
+ struct input_dev *input;
+ int error;
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data not defined\n");
+ return -EINVAL;
+ }
+
+ tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!tsdata || !input) {
+ dev_err(&client->dev, "Failed to allocate driver data!\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsdata->client = client;
+ tsdata->input = input;
+ tsdata->chip = pdata;
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input, ABS_X, 0, pdata->x_max, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, pdata->y_max, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
+
+ input_set_drvdata(input, tsdata);
+
+ error = request_threaded_irq(client->irq, NULL, pixcir_ts_isr,
+ IRQF_TRIGGER_FALLING,
+ client->name, tsdata);
+ if (error) {
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input);
+ if (error)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, tsdata);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, tsdata);
+err_free_mem:
+ input_free_device(input);
+ kfree(tsdata);
+ return error;
+}
+
+static int __devexit pixcir_i2c_ts_remove(struct i2c_client *client)
+{
+ struct pixcir_i2c_ts_data *tsdata = i2c_get_clientdata(client);
+
+ device_init_wakeup(&client->dev, 0);
+
+ tsdata->exiting = true;
+ mb();
+ free_irq(client->irq, tsdata);
+
+ input_unregister_device(tsdata->input);
+ kfree(tsdata);
+
+ return 0;
+}
+
+static const struct i2c_device_id pixcir_i2c_ts_id[] = {
+ { "pixcir_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
+
+static struct i2c_driver pixcir_i2c_ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pixcir_ts",
+ .pm = &pixcir_dev_pm_ops,
+ },
+ .probe = pixcir_i2c_ts_probe,
+ .remove = __devexit_p(pixcir_i2c_ts_remove),
+ .id_table = pixcir_i2c_ts_id,
+};
+
+module_i2c_driver(pixcir_i2c_ts_driver);
+
+MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>");
+MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
new file mode 100644
index 00000000..bf1a0640
--- /dev/null
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -0,0 +1,441 @@
+/*
+ * Samsung S3C24XX touchscreen driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the term of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright 2008 Ben Dooks <ben-linux@fluff.org>
+ * Copyright 2009 Simtec Electronics <linux@simtec.co.uk>
+ *
+ * Additional work by Herbert Pötzl <herbert@13thfloor.at> and
+ * Harald Welte <laforge@openmoko.org>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <plat/adc.h>
+#include <plat/regs-adc.h>
+#include <plat/ts.h>
+
+#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
+
+#define INT_DOWN (0)
+#define INT_UP (1 << 8)
+
+#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_AUTO_PST | \
+ S3C2410_ADCTSC_XY_PST(0))
+
+#define FEAT_PEN_IRQ (1 << 0) /* HAS ADCCLRINTPNDNUP */
+
+/* Per-touchscreen data. */
+
+/**
+ * struct s3c2410ts - driver touchscreen state.
+ * @client: The ADC client we registered with the core driver.
+ * @dev: The device we are bound to.
+ * @input: The input device we registered with the input subsystem.
+ * @clock: The clock for the adc.
+ * @io: Pointer to the IO base.
+ * @xp: The accumulated X position data.
+ * @yp: The accumulated Y position data.
+ * @irq_tc: The interrupt number for pen up/down interrupt
+ * @count: The number of samples collected.
+ * @shift: The log2 of the maximum count to read in one go.
+ * @features: The features supported by the TSADC MOdule.
+ */
+struct s3c2410ts {
+ struct s3c_adc_client *client;
+ struct device *dev;
+ struct input_dev *input;
+ struct clk *clock;
+ void __iomem *io;
+ unsigned long xp;
+ unsigned long yp;
+ int irq_tc;
+ int count;
+ int shift;
+ int features;
+};
+
+static struct s3c2410ts ts;
+
+/**
+ * get_down - return the down state of the pen
+ * @data0: The data read from ADCDAT0 register.
+ * @data1: The data read from ADCDAT1 register.
+ *
+ * Return non-zero if both readings show that the pen is down.
+ */
+static inline bool get_down(unsigned long data0, unsigned long data1)
+{
+ /* returns true if both data values show stylus down */
+ return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
+ !(data1 & S3C2410_ADCDAT0_UPDOWN));
+}
+
+static void touch_timer_fire(unsigned long data)
+{
+ unsigned long data0;
+ unsigned long data1;
+ bool down;
+
+ data0 = readl(ts.io + S3C2410_ADCDAT0);
+ data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+ down = get_down(data0, data1);
+
+ if (down) {
+ if (ts.count == (1 << ts.shift)) {
+ ts.xp >>= ts.shift;
+ ts.yp >>= ts.shift;
+
+ dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
+ __func__, ts.xp, ts.yp, ts.count);
+
+ input_report_abs(ts.input, ABS_X, ts.xp);
+ input_report_abs(ts.input, ABS_Y, ts.yp);
+
+ input_report_key(ts.input, BTN_TOUCH, 1);
+ input_sync(ts.input);
+
+ ts.xp = 0;
+ ts.yp = 0;
+ ts.count = 0;
+ }
+
+ s3c_adc_start(ts.client, 0, 1 << ts.shift);
+ } else {
+ ts.xp = 0;
+ ts.yp = 0;
+ ts.count = 0;
+
+ input_report_key(ts.input, BTN_TOUCH, 0);
+ input_sync(ts.input);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+ }
+}
+
+static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
+
+/**
+ * stylus_irq - touchscreen stylus event interrupt
+ * @irq: The interrupt number
+ * @dev_id: The device ID.
+ *
+ * Called when the IRQ_TC is fired for a pen up or down event.
+ */
+static irqreturn_t stylus_irq(int irq, void *dev_id)
+{
+ unsigned long data0;
+ unsigned long data1;
+ bool down;
+
+ data0 = readl(ts.io + S3C2410_ADCDAT0);
+ data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+ down = get_down(data0, data1);
+
+ /* TODO we should never get an interrupt with down set while
+ * the timer is running, but maybe we ought to verify that the
+ * timer isn't running anyways. */
+
+ if (down)
+ s3c_adc_start(ts.client, 0, 1 << ts.shift);
+ else
+ dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);
+
+ if (ts.features & FEAT_PEN_IRQ) {
+ /* Clear pen down/up interrupt */
+ writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * s3c24xx_ts_conversion - ADC conversion callback
+ * @client: The client that was registered with the ADC core.
+ * @data0: The reading from ADCDAT0.
+ * @data1: The reading from ADCDAT1.
+ * @left: The number of samples left.
+ *
+ * Called when a conversion has finished.
+ */
+static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
+ unsigned data0, unsigned data1,
+ unsigned *left)
+{
+ dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);
+
+ ts.xp += data0;
+ ts.yp += data1;
+
+ ts.count++;
+
+ /* From tests, it seems that it is unlikely to get a pen-up
+ * event during the conversion process which means we can
+ * ignore any pen-up events with less than the requisite
+ * count done.
+ *
+ * In several thousand conversions, no pen-ups where detected
+ * before count completed.
+ */
+}
+
+/**
+ * s3c24xx_ts_select - ADC selection callback.
+ * @client: The client that was registered with the ADC core.
+ * @select: The reason for select.
+ *
+ * Called when the ADC core selects (or deslects) us as a client.
+ */
+static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
+{
+ if (select) {
+ writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+ ts.io + S3C2410_ADCTSC);
+ } else {
+ mod_timer(&touch_timer, jiffies+1);
+ writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
+ }
+}
+
+/**
+ * s3c2410ts_probe - device core probe entry point
+ * @pdev: The device we are being bound to.
+ *
+ * Initialise, find and allocate any resources we need to run and then
+ * register with the ADC and input systems.
+ */
+static int __devinit s3c2410ts_probe(struct platform_device *pdev)
+{
+ struct s3c2410_ts_mach_info *info;
+ struct device *dev = &pdev->dev;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int ret = -EINVAL;
+
+ /* Initialise input stuff */
+ memset(&ts, 0, sizeof(struct s3c2410ts));
+
+ ts.dev = dev;
+
+ info = pdev->dev.platform_data;
+ if (!info) {
+ dev_err(dev, "no platform data, cannot attach\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "initialising touchscreen\n");
+
+ ts.clock = clk_get(dev, "adc");
+ if (IS_ERR(ts.clock)) {
+ dev_err(dev, "cannot get adc clock source\n");
+ return -ENOENT;
+ }
+
+ clk_enable(ts.clock);
+ dev_dbg(dev, "got and enabled clocks\n");
+
+ ts.irq_tc = ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "no resource for interrupt\n");
+ goto err_clk;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no resource for registers\n");
+ ret = -ENOENT;
+ goto err_clk;
+ }
+
+ ts.io = ioremap(res->start, resource_size(res));
+ if (ts.io == NULL) {
+ dev_err(dev, "cannot map registers\n");
+ ret = -ENOMEM;
+ goto err_clk;
+ }
+
+ /* inititalise the gpio */
+ if (info->cfg_gpio)
+ info->cfg_gpio(to_platform_device(ts.dev));
+
+ ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
+ s3c24xx_ts_conversion, 1);
+ if (IS_ERR(ts.client)) {
+ dev_err(dev, "failed to register adc client\n");
+ ret = PTR_ERR(ts.client);
+ goto err_iomap;
+ }
+
+ /* Initialise registers */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(dev, "Unable to allocate the input device !!\n");
+ ret = -ENOMEM;
+ goto err_iomap;
+ }
+
+ ts.input = input_dev;
+ ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
+ input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
+
+ ts.input->name = "S3C24XX TouchScreen";
+ ts.input->id.bustype = BUS_HOST;
+ ts.input->id.vendor = 0xDEAD;
+ ts.input->id.product = 0xBEEF;
+ ts.input->id.version = 0x0102;
+
+ ts.shift = info->oversampling_shift;
+ ts.features = platform_get_device_id(pdev)->driver_data;
+
+ ret = request_irq(ts.irq_tc, stylus_irq, 0,
+ "s3c2410_ts_pen", ts.input);
+ if (ret) {
+ dev_err(dev, "cannot get TC interrupt\n");
+ goto err_inputdev;
+ }
+
+ dev_info(dev, "driver attached, registering input device\n");
+
+ /* All went ok, so register to the input system */
+ ret = input_register_device(ts.input);
+ if (ret < 0) {
+ dev_err(dev, "failed to register input device\n");
+ ret = -EIO;
+ goto err_tcirq;
+ }
+
+ return 0;
+
+ err_tcirq:
+ free_irq(ts.irq_tc, ts.input);
+ err_inputdev:
+ input_free_device(ts.input);
+ err_iomap:
+ iounmap(ts.io);
+ err_clk:
+ del_timer_sync(&touch_timer);
+ clk_put(ts.clock);
+ return ret;
+}
+
+/**
+ * s3c2410ts_remove - device core removal entry point
+ * @pdev: The device we are being removed from.
+ *
+ * Free up our state ready to be removed.
+ */
+static int __devexit s3c2410ts_remove(struct platform_device *pdev)
+{
+ free_irq(ts.irq_tc, ts.input);
+ del_timer_sync(&touch_timer);
+
+ clk_disable(ts.clock);
+ clk_put(ts.clock);
+
+ input_unregister_device(ts.input);
+ iounmap(ts.io);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410ts_suspend(struct device *dev)
+{
+ writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC);
+ disable_irq(ts.irq_tc);
+ clk_disable(ts.clock);
+
+ return 0;
+}
+
+static int s3c2410ts_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s3c2410_ts_mach_info *info = pdev->dev.platform_data;
+
+ clk_enable(ts.clock);
+ enable_irq(ts.irq_tc);
+
+ /* Initialise registers */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+ return 0;
+}
+
+static struct dev_pm_ops s3c_ts_pmops = {
+ .suspend = s3c2410ts_suspend,
+ .resume = s3c2410ts_resume,
+};
+#endif
+
+static struct platform_device_id s3cts_driver_ids[] = {
+ { "s3c2410-ts", 0 },
+ { "s3c2440-ts", 0 },
+ { "s3c64xx-ts", FEAT_PEN_IRQ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
+
+static struct platform_driver s3c_ts_driver = {
+ .driver = {
+ .name = "samsung-ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &s3c_ts_pmops,
+#endif
+ },
+ .id_table = s3cts_driver_ids,
+ .probe = s3c2410ts_probe,
+ .remove = __devexit_p(s3c2410ts_remove),
+};
+module_platform_driver(s3c_ts_driver);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "
+ "Ben Dooks <ben@simtec.co.uk>, "
+ "Simtec Electronics <linux@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C24XX Touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/semisens/Makefile b/drivers/input/touchscreen/semisens/Makefile
new file mode 100755
index 00000000..4539aa8d
--- /dev/null
+++ b/drivers/input/touchscreen/semisens/Makefile
@@ -0,0 +1,33 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_semisens
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := sn310m-touch.o touch.o
+#mach-sn310m-sample.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers modules.builtin
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers modules.builtin
diff --git a/drivers/input/touchscreen/semisens/sn310m-touch-pdata.h b/drivers/input/touchscreen/semisens/sn310m-touch-pdata.h
new file mode 100755
index 00000000..fcf1e3c6
--- /dev/null
+++ b/drivers/input/touchscreen/semisens/sn310m-touch-pdata.h
@@ -0,0 +1,201 @@
+/****************************************************************
+ *
+ * sn310m-touch-pdata.c
+ *
+ * Copyright (c) 2013 SEMISENS Co.,Ltd
+ * http://www.semisens.com
+ *
+ ****************************************************************/
+
+#ifndef __TOUCH_PDATA_H
+#define __TOUCH_PDATA_H
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ #include <linux/earlysuspend.h>
+#endif
+
+#include <linux/interrupt.h>
+
+#ifndef errlog
+#define errlog(fmt, args...) printk(KERN_ERR "[%s:%d]: " fmt, __FUNCTION__, __LINE__ ,## args)
+#endif
+
+
+//#define DEBUG_TOUCH
+
+#undef dbg
+#ifdef DEBUG_TOUCH
+#define dbg(fmt, args...) printk(KERN_ERR "[%s:%d]: " fmt, __FUNCTION__, __LINE__ ,## args)
+#else
+#define dbg(fmt, args...)
+#endif
+
+#undef MSM_GPIO_TO_INT
+#define MSM_GPIO_TO_INT(a) (a)
+
+
+
+
+#define I2C_TOUCH_NAME "SN310M"
+#define I2C_SEND_MAX_SIZE 512 // I2C Send/Receive data max size
+
+//--------------------------------------------
+// Button struct (1 = press, 0 = release)
+//--------------------------------------------
+typedef struct button__t {
+ unsigned char bt0_press :1; // lsb
+ unsigned char bt1_press :1;
+ unsigned char bt2_press :1;
+ unsigned char bt3_press :1;
+ unsigned char bt4_press :1;
+ unsigned char bt5_press :1;
+ unsigned char bt6_press :1;
+ unsigned char bt7_press :1; // msb
+} __attribute__ ((packed)) button_t;
+
+typedef union button__u {
+ unsigned char ubyte;
+ button_t bits;
+} __attribute__ ((packed)) button_u;
+
+//--------------------------------------------
+// Touch Event type define
+//--------------------------------------------
+#define TS_EVENT_UNKNOWN 0x00
+#define TS_EVENT_PRESS 0x01
+#define TS_EVENT_MOVE 0x02
+#define TS_EVENT_RELEASE 0x03
+
+typedef struct finger__t {
+ unsigned int status; // true : ts data updated, false : no update data
+ unsigned int event; // ts event type
+ unsigned int id; // ts received id
+ unsigned int x; // ts data x
+ unsigned int y; // ts data y
+ unsigned int area; // ts finger area
+ unsigned int pressure; // ts finger pressure
+} __attribute__ ((packed)) finger_t;
+
+struct touch {
+ int irq;
+ struct i2c_client *client;
+ struct touch_pdata *pdata;
+ struct input_dev *input;
+ char phys[32];
+
+ finger_t *finger; // finger data
+ struct mutex mutex;
+
+ // sysfs control flags
+ unsigned char disabled; // interrupt status
+ unsigned char fw_version;
+
+ unsigned char *fw_buf;
+ unsigned int fw_size;
+ int fw_status;
+
+ // irq func used
+ struct workqueue_struct *work_queue;
+ struct work_struct work;
+
+ // noise filter work
+ struct delayed_work filter_dwork;
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend power;
+#endif
+};
+
+struct i2c_client;
+struct input_dev;
+struct device;
+
+#define IRQ_MODE_THREAD 0
+#define IRQ_MODE_NORMAL 1
+#define IRQ_MODE_POLLING 2
+
+//--------------------------------------------
+// IRQ type & trigger action
+//--------------------------------------------
+//
+// IRQF_TRIGGER_RISING, IRQF_TRIGGER_FALLING, IRQF_TRIGGER_HIGH, IRQF_TRIGGER_LOW
+// IRQF_DISABLED, IRQF_SHARED, IRQF_IRQPOLL, IRQF_ONESHOT, IRQF_NO_THREAD
+//
+//--------------------------------------------
+struct touch_pdata {
+ char *name; // input drv name
+
+ int irq_gpio; // irq gpio define
+ int reset_gpio; // reset gpio define
+ int reset_level; // reset level setting (1 = High reset, 0 = Low reset)
+
+ int irq_mode; // IRQ_MODE_THREAD, IRQ_MODE_NORMAL, IRQ_MODE_POLLING
+ int irq_flags; // irq flags setup : Therad irq mode(IRQF_TRIGGER_HIGH | IRQF_ONESHOT)
+
+ int abs_max_x, abs_max_y;
+ int abs_min_x, abs_min_y;
+
+ int area_max, area_min;
+ int press_max, press_min;
+ int id_max, id_min;
+
+ int vendor, product, version;
+
+ int max_fingers;
+
+ int *keycode, keycnt;
+ int lcd_exchg;
+
+ //--------------------------------------------
+ // Control function
+ //--------------------------------------------
+ void (*gpio_init) (void); // gpio early-init function
+
+ irqreturn_t (*irq_func) (int irq, void *handle);
+ void (*touch_work) (struct touch *ts);
+
+ void (*report) (struct touch *ts);
+ void (*key_report) (struct touch *ts, unsigned char button_data);
+
+ int (*early_probe) (struct touch *ts);
+ int (*probe) (struct touch *ts);
+ void (*enable) (struct touch *ts);
+ void (*disable) (struct touch *ts);
+ int (*input_open) (struct input_dev *input);
+ void (*input_close) (struct input_dev *input);
+
+ void (*event_clear) (struct touch *ts);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ void (*resume) (struct early_suspend *h);
+ void (*suspend) (struct early_suspend *h);
+#endif
+
+ /* by limst, added to control power for touch IC */
+ int (*power) (int on);
+
+ //--------------------------------------------
+ // I2C control function
+ //--------------------------------------------
+ int (*i2c_write) (struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len);
+ int (*i2c_read) (struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len);
+
+ //--------------------------------------------
+ // Firmware update control function
+ //--------------------------------------------
+ char *fw_filename;
+ int fw_filesize;
+
+ int (*i2c_boot_write) (struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len);
+ int (*i2c_boot_read) (struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len);
+ int (*fw_control) (struct touch *ts, unsigned int fw_status);
+ int (*flash_firmware) (struct device *dev, const char *fw_name);
+
+ //--------------------------------------------
+ // Calibration control function
+ //--------------------------------------------
+ int (*calibration) (struct touch *ts);
+};
+
+#endif // __TOUCH_PDATA_H
+
diff --git a/drivers/input/touchscreen/semisens/sn310m-touch.c b/drivers/input/touchscreen/semisens/sn310m-touch.c
new file mode 100755
index 00000000..81275baf
--- /dev/null
+++ b/drivers/input/touchscreen/semisens/sn310m-touch.c
@@ -0,0 +1,332 @@
+/****************************************************************
+ *
+ * sn310m-touch.c : I2C Touchscreen driver (platform data struct)
+ *
+ * Copyright (c) 2013 SEMISENS Co.,Ltd
+ * http://www.semisens.com
+ *
+ ****************************************************************/
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <asm/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+
+//----------------------------------------------
+#include "sn310m-touch-pdata.h"
+#include "sn310m-touch.h"
+#include "touch.h"
+
+
+//----------------------------------------------
+unsigned char sn310m_id_tracking(struct touch *ts, unsigned char find_id);
+
+//----------------------------------------------
+// Touch i2c control function
+//----------------------------------------------
+int sn310m_i2c_read(struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len)
+{
+ struct i2c_msg msg[2];
+ int ret = 0;
+ unsigned char i = 0;
+ unsigned char cmd_tmp[10] = {0, };
+
+ if((len == 0) || (data == NULL)) {
+ dev_err(&client->dev, "I2C read error: Null pointer or length == 0\n");
+ return -1;
+ }
+
+ memset(cmd_tmp, 0x00, sizeof(cmd_tmp));
+
+ if(cmd_len) {
+ for(i = 0; i < cmd_len; i++) {
+ cmd_tmp[i] = cmd[cmd_len -1 -i];
+ }
+ }
+
+ memset(msg, 0x00, sizeof(msg));
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags & I2C_M_TEN;
+ msg[0].len = cmd_len;
+ msg[0].buf = cmd_tmp;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags & I2C_M_TEN;
+ msg[1].flags |= I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = data;
+
+ if((ret = i2c_transfer(client->adapter, msg, 2)) != 2) {
+ dev_err(&client->dev, "I2C read error: (%d) reg: 0x%X len: %d\n", ret, cmd_tmp[0], len);
+ return -EIO;
+ }
+
+ return len;
+}
+
+int sn310m_i2c_write(struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len)
+{
+ int ret = 0;
+ unsigned char block_data[10] = {0, };
+ unsigned char i = 0;
+ unsigned char cmd_tmp[10] = {0, };
+
+ if((cmd_len + len) >= sizeof(block_data)) {
+ dev_err(&client->dev, "I2C write error: wdata overflow reg: 0x%X len: %d\n", cmd[0], cmd_len + len);
+ return -1;
+ }
+
+ memset(block_data, 0x00, sizeof(block_data));
+ memset(cmd_tmp, 0x00, sizeof(cmd_tmp));
+
+ if(cmd_len) {
+ for(i = 0; i < cmd_len; i++) {
+ cmd_tmp[i] = cmd[cmd_len -1 -i];
+ }
+ }
+
+ if(cmd_len)
+ memcpy(&block_data[0], &cmd_tmp[0], cmd_len);
+
+ if(len)
+ memcpy(&block_data[cmd_len], &data[0], len);
+
+ if((ret = i2c_master_send(client, block_data, (cmd_len + len))) < 0) {
+ dev_err(&client->dev, "I2C write error: (%d) reg: 0x%X len: %d\n", ret, cmd[0], len);
+ return ret;
+ }
+
+ return len;
+}
+
+//----------------------------------------------
+// Touch initialize & finalize function
+//----------------------------------------------
+int sn310m_input_open(struct input_dev *input)
+{
+ struct touch *ts = input_get_drvdata(input);
+
+ dbg("%s\n", __func__);
+
+ ts->pdata->enable(ts);
+
+ return 0;
+}
+
+void sn310m_enable(struct touch *ts)
+{
+ unsigned short cmd = REG_TS_STATUS;
+ unsigned int rdata = 0;
+ dbg("sn310m_enable++\n");
+ if(ts->disabled) {
+ while(!gpio_get_value(ts->pdata->irq_gpio))
+ ts->pdata->i2c_read(ts->client, (unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&rdata, sizeof(rdata));
+ wmt_gpio_set_irq_type(ts->pdata->irq_gpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ts->pdata->irq_gpio);
+ dbg("enable_irq (%d)\n",ts->irq);
+ ts->disabled = false;
+ }
+ dbg("sn310m_enable--\n");
+}
+
+void sn310m_disable(struct touch *ts)
+{
+ dbg("sn310m_disable++\n");
+ if(!ts->disabled) {
+ //disable_irq(ts->irq);//wmt_gpio_mask_irq(ts->pdata->irq_gpio);//
+ wmt_gpio_mask_irq(ts->pdata->irq_gpio);
+ dbg("disable_irq(ts->irq);\n");
+ ts->disabled = true;
+ if(ts->pdata->event_clear){
+ ts->pdata->event_clear(ts);
+ }
+ }
+ dbg("sn310m_disable--");
+}
+
+int sn310m_early_probe(struct touch *ts)
+{
+ // nothing to do...
+
+ return 0;
+}
+
+int sn310m_probe(struct touch *ts)
+{
+ unsigned short cmd = REG_FIRMWARE_VERSION;
+ unsigned short rdata = 0;
+
+ if(ts->pdata->i2c_read(ts->client, (unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&rdata, sizeof(rdata)) < 0) {
+ errlog("fail to get touch ic firmware version.\n");
+ return -1;
+ }
+
+ ts->fw_version = rdata;
+
+ dbg("touch ic firmware version : %d \n", rdata);
+
+ return 0;
+}
+
+//----------------------------------------------
+// calibration function
+//----------------------------------------------
+int sn310m_calibration(struct touch *ts)
+{
+ // nothing to do...
+
+ return 0;
+}
+
+#define SN310M_NATIVE_INTERFACE
+#if defined(SN310M_NATIVE_INTERFACE)
+#include <linux/syscalls.h>
+extern int g_MiscInitialize;
+#endif
+
+//----------------------------------------------
+// Touch data processing function
+//----------------------------------------------
+void sn310m_work(struct touch *ts)
+{
+ unsigned char find_slot = 0;
+ unsigned short cmd = 0;
+ status_reg_u status;
+ data_reg_t data;
+ button_u button;
+ unsigned int ids = 0;
+ int i = 0;
+
+ mutex_lock(&ts->mutex);
+
+ cmd = REG_TS_STATUS;
+ ts->pdata->i2c_read(ts->client, (unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&status.uint, sizeof(status_reg_u));
+
+ if(status.bits.ts_cnt <= ts->pdata->max_fingers) {
+ unsigned char cnt = 0;
+
+ if(ts->pdata->keycode && (status.bits.ts_cnt == 0)) {
+ button.bits.bt0_press = (status.bits.button & 0x01) ? 1 : 0;
+ button.bits.bt1_press = (status.bits.button & 0x02) ? 1 : 0;
+ button.bits.bt2_press = (status.bits.button & 0x04) ? 1 : 0;
+ button.bits.bt3_press = (status.bits.button & 0x08) ? 1 : 0;
+
+ ts->pdata->key_report(ts, button.ubyte);
+ }
+
+ for(cnt = 0; cnt < status.bits.ts_cnt; cnt++) {
+ unsigned int id;
+ unsigned int x;
+ unsigned int y;
+ unsigned int area;
+ unsigned int pressure;
+
+ cmd = REG_TS_DATA(cnt);
+ ts->pdata->i2c_read(ts->client, (unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&data.packet0, sizeof(data_reg_t));
+
+ id = data.packet0 >> 12;
+ x = data.packet0 & 0xfff;
+ y = data.packet1 & 0xfff;
+ area = data.packet2 & 0xfff;
+ pressure = ((data.packet1 >> 8) & 0x00f0) + (data.packet2 >> 12);
+
+ dbg("DEBUG(%s) : cmd=%d, id=%d, x=%d, y=%d, area=%d, pressure=%d \n", __func__, cmd, id, x, y, area, pressure);
+ dbg("DEBUG(%s) : pkt0=%x pkt1=%x pkt2=%x \n", __func__, data.packet0, data.packet1, data.packet2);
+
+ if((x >= ts->pdata->abs_max_x) || (y >= ts->pdata->abs_max_y)) {
+ if(ts->pdata->event_clear)
+ ts->pdata->event_clear(ts);
+
+ dbg("ERROR(%s) : x(%d) or y(%d) value overflow!\n", __func__, x, y);
+ continue;
+ }
+
+ if(ts->pdata->id_max) {
+ if((id >= ts->pdata->id_max) || (id < ts->pdata->id_min)) {
+ if(ts->pdata->event_clear)
+ ts->pdata->event_clear(ts);
+
+ dbg("ERROR(%s) : id(%d) value overflow!\n", __func__, id);
+ continue;
+ }
+ if((find_slot = sn310m_id_tracking(ts, id)) == 0xFF) {
+ dbg("ERROR(%s) : Empty slot not found\n", __func__);
+ continue;
+ }
+ }
+ else {
+ if(id == 0)
+ continue;
+
+ find_slot = cnt;
+ }
+
+ if(ts->finger[find_slot].event == TS_EVENT_UNKNOWN)
+ ts->finger[find_slot].event = TS_EVENT_PRESS;
+ else if((ts->finger[find_slot].event == TS_EVENT_PRESS) || (ts->finger[find_slot].event == TS_EVENT_MOVE))
+ ts->finger[find_slot].event = TS_EVENT_MOVE;
+
+ if (ts->pdata->lcd_exchg) {
+ int tmp;
+ tmp = x;
+ x = y;
+ y = ts->pdata->abs_max_x - tmp;
+ }
+
+ ts->finger[find_slot].status = true;
+ ts->finger[find_slot].id = id;
+ ts->finger[find_slot].x = x;
+ ts->finger[find_slot].y = y;
+ ts->finger[find_slot].area = (ts->pdata->area_max < area) ? ts->pdata->area_max : area;
+ ts->finger[find_slot].pressure = (ts->pdata->press_max < pressure) ? ts->pdata->press_max : pressure;
+ ids |= 1 << find_slot;
+ }
+ }
+
+ for(i = 0; i < ts->pdata->max_fingers; i++) {
+ if(!(ids & (1 << i))) {
+ if(ts->finger[i].event != TS_EVENT_UNKNOWN) {
+ ts->finger[i].status = true;
+ ts->finger[i].event = TS_EVENT_RELEASE;
+ }
+ }
+ }
+
+ ts->pdata->report(ts);
+ mutex_unlock(&ts->mutex);
+ wmt_gpio_unmask_irq(ts->pdata->irq_gpio);
+}
+
+unsigned char sn310m_id_tracking(struct touch *ts, unsigned char find_id)
+{
+ unsigned char find_slot = 0xFF;
+ int i = 0;
+
+ for(i = 0; i < ts->pdata->max_fingers; i++) {
+ if(ts->finger[i].id == find_id)
+ find_slot = i;
+
+ if((ts->finger[i].event == TS_EVENT_UNKNOWN) && (find_slot == 0xFF))
+ find_slot = i;
+ }
+ return find_slot;
+}
+
+//----------------------------------------------
+// Firmware update Control function
+//----------------------------------------------
+int sn310m_flash_firmware(struct device *dev, const char *fw_name)
+{
+ // nothing to do...
+ return 0;
+}
diff --git a/drivers/input/touchscreen/semisens/sn310m-touch.h b/drivers/input/touchscreen/semisens/sn310m-touch.h
new file mode 100755
index 00000000..0469bf19
--- /dev/null
+++ b/drivers/input/touchscreen/semisens/sn310m-touch.h
@@ -0,0 +1,97 @@
+/****************************************************************
+ *
+ * sn310m-touch.c : i2c Touchscreen driver (platform data struct)
+ *
+ * Copyright (c) 2013 SEMISENS Co.,Ltd
+ * http://www.semisens.com
+ *
+ ****************************************************************/
+#ifndef __SN310M_TOUCH_H
+#define __SN310M_TOUCH_H
+
+//----------------------------------------------
+// register address for firmware update
+//----------------------------------------------
+#define REG_CMD_ISP_MODE 0x02F1 // 0x0200 : prepare eFlash, 0x0100 : finish eFalsh
+#define REG_CMD_FLASH_BUS 0x04F1 // 0x0000 : set eFlash bus functions
+#define REG_CMD_FLASH_ENABLE 0x08F1 // 0xFFFF : enable eFlash functions
+#define REG_CMD_FLASH_AUTH 0x00F4 // 0x0100 : get eFlash approach authority
+#define REG_CMD_FLASH_CON_EN 0x02F4 // 0x0000 : enable eFlash controller
+#define REG_CMD_FLASH_COMMAND 0x04F4 // 0x0200 : erase eFlash, 0x0000 : write eFlash
+#define REG_CMD_FLASH_BUSY 0x08F4 // [15] bit is busy flag for eflash eperating.
+
+//----------------------------------------------
+// register setting value for firmware update
+//----------------------------------------------
+#define REG_SET_PREPARE_FLASH_ACCESS 0x0200
+#define REG_SET_FINISH_FLASH_ACCESS 0x0100
+#define REG_SET_ENABLE_FLASH_ERASE 0x0200
+#define REG_SET_ENABLE_FLASH_WRITE 0x0000
+
+#define SN310M_MAX_FW_SIZE (10*1024) // 10 Kbytes
+#define REG_FIRMWARE_VERSION (0x3EE0)
+
+//----------------------------------------------
+// Touch status & data register address
+//----------------------------------------------
+#define REG_TS_STATUS 0x00E0
+
+typedef struct status_reg__t {
+ unsigned int ts_cnt :4; // lsb
+ unsigned int reserved1 :4;
+ unsigned int button :5;
+ unsigned int reserved2 :3; // msb
+} __attribute__ ((packed)) status_reg_t;
+
+typedef union status_reg__u {
+ unsigned short uint;
+ status_reg_t bits;
+} __attribute__ ((packed)) status_reg_u;
+
+#define REG_TS_DATA_BASE 0x02E0
+#define REG_TS_DATA(x) (((x * 6) << 8) + REG_TS_DATA_BASE)
+
+typedef struct data_reg__t {
+ unsigned short packet0;
+ unsigned short packet1;
+ unsigned short packet2;
+} __attribute__ ((packed)) data_reg_t;
+
+typedef union data_reg__u {
+ unsigned int uint;
+ data_reg_t bits;
+} __attribute__ ((packed)) data_reg_u;
+
+
+//----------------------------------------------
+// i2c Control function
+//----------------------------------------------
+extern int sn310m_i2c_read(struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len);
+extern int sn310m_i2c_write(struct i2c_client *client, unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int len);
+
+//----------------------------------------------
+// Touch initialize & finalize function
+//----------------------------------------------
+extern int sn310m_input_open(struct input_dev *input);
+extern void sn310m_enable(struct touch *ts);
+extern void sn310m_disable(struct touch *ts);
+extern int sn310m_early_probe(struct touch *ts);
+extern int sn310m_probe(struct touch *ts);
+
+//----------------------------------------------
+// Calibration function
+//----------------------------------------------
+extern int sn310m_calibration(struct touch *ts);
+
+//----------------------------------------------
+// Touch data processing function
+//----------------------------------------------
+extern void sn310m_work(struct touch *ts);
+
+//----------------------------------------------
+// Firmware update Control function
+//----------------------------------------------
+extern int sn310m_flash_firmware(struct device *dev, const char *fw_name);
+
+#endif // __SN310M_TOUCH_H
+
diff --git a/drivers/input/touchscreen/semisens/touch.c b/drivers/input/touchscreen/semisens/touch.c
new file mode 100755
index 00000000..39d6ce15
--- /dev/null
+++ b/drivers/input/touchscreen/semisens/touch.c
@@ -0,0 +1,1121 @@
+/****************************************************************
+ *
+ * touch.c : I2C Touchscreen driver
+ *
+ * Copyright (c) 2013 SEMISENS Co.,Ltd
+ * http://www.semisens.com
+ *
+ ****************************************************************/
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <asm/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+//----------------------------------------------
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ #include <linux/wakelock.h>
+ #include <linux/earlysuspend.h>
+ #include <linux/suspend.h>
+#endif
+
+//----------------------------------------------
+#include <linux/input/mt.h>
+#include "sn310m-touch-pdata.h"
+#include "sn310m-touch.h"
+
+//----------------------------------------------
+#include "touch.h"
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+
+
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+
+#define SN310M_NATIVE_INTERFACE /* This is to debug semisens TSC */
+
+#if defined(SN310M_NATIVE_INTERFACE)
+#include <linux/miscdevice.h>
+#include <linux/syscalls.h>
+struct touch* g_ts;
+int g_MiscInitialize = 0;
+static int P_SN310M_Dist_Probe(struct touch* ts);
+static int P_SN310M_Dist_Open(struct inode *inode, struct file *file);
+static long P_SN310M_Dist_Ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static void P_SN310M_Dist_Remove(void);
+#endif
+
+// function prototype define
+//----------------------------------------------
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ static void touch_suspend(struct early_suspend *h);
+ static void touch_resume(struct early_suspend *h);
+#endif
+
+irqreturn_t touch_irq(int irq, void *handle);
+#if 0 /* unused */
+ static void touch_work(struct touch *ts);
+#endif
+static void touch_work_q(struct work_struct *work);
+static void touch_key_report(struct touch *ts, unsigned char button_data);
+static void touch_report_protocol_a(struct touch *ts);
+static void touch_report_protocol_b(struct touch *ts);
+static void touch_event_clear(struct touch *ts);
+#if 0 /* unused */
+ static void touch_enable(struct touch *ts);
+ static void touch_disable(struct touch *ts);
+#endif
+static void touch_input_close(struct input_dev *input);
+static int touch_input_open(struct input_dev *input);
+static int touch_check_functionality (struct touch_pdata *pdata);
+void touch_hw_reset(struct touch *ts);
+int touch_info_display(struct touch *ts);
+int touch_probe(struct i2c_client *client, const struct i2c_device_id *client_id);
+int touch_remove(struct i2c_client *client);
+
+
+// Kinsey:
+#define WMT_TS_I2C_NAME "wmt-ts"
+static struct i2c_client *l_client;
+
+
+
+
+//----------------------------------------------
+irqreturn_t touch_irq(int irq, void *handle)
+{
+ struct touch *ts = handle;
+ if (gpio_irqstatus(ts->pdata->irq_gpio)){
+ wmt_gpio_ack_irq(ts->pdata->irq_gpio);
+ if (is_gpio_irqenable(ts->pdata->irq_gpio)){
+ wmt_gpio_mask_irq(ts->pdata->irq_gpio);
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ if(!ts->earlysus)
+ queue_work(ts->work_queue, &ts->work);
+ #else
+ queue_work(ts->work_queue, &ts->work);
+ #endif
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+
+}
+
+//----------------------------------------------
+static void touch_work_q(struct work_struct *work)
+{
+ struct touch *ts = container_of(work, struct touch, work);
+ ts->pdata->touch_work(ts);
+}
+
+//----------------------------------------------
+static void touch_key_report(struct touch *ts, unsigned char button_data)
+{
+ static button_u button_old;
+ button_u button_new;
+
+ button_new.ubyte = button_data;
+ if(button_old.ubyte != button_new.ubyte) {
+ if((button_old.bits.bt0_press != button_new.bits.bt0_press) && (ts->pdata->keycnt > 0)) {
+ if(button_new.bits.bt0_press) input_report_key(ts->input, ts->pdata->keycode[0], true);
+ else input_report_key(ts->input, ts->pdata->keycode[0], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[0](0x%04X) %s\n", ts->pdata->keycode[0], button_new.bits.bt0_press ? "press":"release");
+ #endif
+ }
+ if((button_old.bits.bt1_press != button_new.bits.bt1_press) && (ts->pdata->keycnt > 1)) {
+ if(button_new.bits.bt1_press) input_report_key(ts->input, ts->pdata->keycode[1], true);
+ else input_report_key(ts->input, ts->pdata->keycode[1], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[1](0x%04X) %s\n", ts->pdata->keycode[1], button_new.bits.bt1_press ? "press":"release");
+ #endif
+ }
+ if((button_old.bits.bt2_press != button_new.bits.bt2_press) && (ts->pdata->keycnt > 2)) {
+ if(button_new.bits.bt2_press) input_report_key(ts->input, ts->pdata->keycode[2], true);
+ else input_report_key(ts->input, ts->pdata->keycode[2], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[2](0x%04X) %s\n", ts->pdata->keycode[2], button_new.bits.bt2_press ? "press":"release");
+ #endif
+ }
+ if((button_old.bits.bt3_press != button_new.bits.bt3_press) && (ts->pdata->keycnt > 3)) {
+ if(button_new.bits.bt3_press) input_report_key(ts->input, ts->pdata->keycode[3], true);
+ else input_report_key(ts->input, ts->pdata->keycode[3], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[3](0x%04X) %s\n", ts->pdata->keycode[3], button_new.bits.bt3_press ? "press":"release");
+ #endif
+ }
+ if((button_old.bits.bt4_press != button_new.bits.bt4_press) && (ts->pdata->keycnt > 4)) {
+ if(button_new.bits.bt4_press) input_report_key(ts->input, ts->pdata->keycode[4], true);
+ else input_report_key(ts->input, ts->pdata->keycode[4], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[4](0x%04X) %s\n", ts->pdata->keycode[4], button_new.bits.bt4_press ? "press":"release");
+ #endif
+ }
+ if((button_old.bits.bt5_press != button_new.bits.bt5_press) && (ts->pdata->keycnt > 5)) {
+ if(button_new.bits.bt5_press) input_report_key(ts->input, ts->pdata->keycode[5], true);
+ else input_report_key(ts->input, ts->pdata->keycode[5], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[5](0x%04X) %s\n", ts->pdata->keycode[5], button_new.bits.bt5_press ? "press":"release");
+ #endif
+ }
+ if((button_old.bits.bt6_press != button_new.bits.bt6_press) && (ts->pdata->keycnt > 6)) {
+ if(button_new.bits.bt6_press) input_report_key(ts->input, ts->pdata->keycode[6], true);
+ else input_report_key(ts->input, ts->pdata->keycode[6], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[6](0x%04X) %s\n", ts->pdata->keycode[6], button_new.bits.bt6_press ? "press":"release");
+ #endif
+ }
+ if((button_old.bits.bt7_press != button_new.bits.bt7_press) && (ts->pdata->keycnt > 7)) {
+ if(button_new.bits.bt7_press) input_report_key(ts->input, ts->pdata->keycode[7], true);
+ else input_report_key(ts->input, ts->pdata->keycode[7], false);
+ #if defined(DEBUG_TOUCH_KEY)
+ dbg("keycode[7](0x%04X) %s\n", ts->pdata->keycode[7], button_new.bits.bt7_press ? "press":"release");
+ #endif
+ }
+ button_old.ubyte = button_new.ubyte;
+ }
+}
+
+//----------------------------------------------
+static void touch_report_protocol_a(struct touch *ts)
+{
+ int id;
+
+ for(id = 0; id < ts->pdata->max_fingers; id++) {
+
+ if(ts->finger[id].event == TS_EVENT_UNKNOWN) continue;
+
+ if(ts->finger[id].event != TS_EVENT_RELEASE) {
+ if(ts->pdata->id_max) input_report_abs(ts->input, ABS_MT_TRACKING_ID, ts->finger[id].id);
+ if(ts->pdata->area_max) input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, ts->finger[id].area ? ts->finger[id].area : 10);
+ if(ts->pdata->press_max) input_report_abs(ts->input, ABS_MT_PRESSURE, ts->finger[id].pressure);
+
+ input_report_abs(ts->input, ABS_MT_POSITION_X, ts->finger[id].x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y, ts->finger[id].y);
+ dbg("%s : id = %d, x = %d, y = %d\n", __func__, ts->finger[id].id, ts->finger[id].x, ts->finger[id].y);
+ }
+ else {
+ ts->finger[id].event = TS_EVENT_UNKNOWN;
+ dbg("%s : release id = %d\n", __func__, ts->finger[id].id);
+ }
+
+ input_mt_sync(ts->input);
+ }
+
+ input_sync(ts->input);
+}
+
+//----------------------------------------------
+static void touch_report_protocol_b(struct touch *ts)
+{
+ int id;
+#if defined(DEBUG_TOUCH)
+ char *event_str[] = {"unknown", "press", "move", "release"};
+#endif
+
+ for(id = 0; id < ts->pdata->max_fingers; id++) {
+ if((ts->finger[id].event == TS_EVENT_UNKNOWN) || (ts->finger[id].status == false))
+ continue;
+
+ input_mt_slot(ts->input, id);
+ ts->finger[id].status = false;
+
+ if(ts->finger[id].event != TS_EVENT_RELEASE) {
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
+
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, ts->finger[id].id);
+ input_report_abs(ts->input, ABS_MT_POSITION_X, ts->finger[id].x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y, ts->finger[id].y);
+
+ if(ts->pdata->area_max) input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, ts->finger[id].area ? ts->finger[id].area : 10);
+ if(ts->pdata->press_max) input_report_abs(ts->input, ABS_MT_PRESSURE, ts->finger[id].pressure);
+
+#if defined(DEBUG_TOUCH)
+ dbg("%s : event = %s, slot = %d, id = %d, x = %d, y = %d\n", __func__, event_str[ts->finger[id].event], id, ts->finger[id].id, ts->finger[id].x, ts->finger[id].y);
+#endif
+ }
+ else {
+#if defined(DEBUG_TOUCH)
+ dbg("%s : event = %s, slot = %d, id = %d\n", __func__, event_str[ts->finger[id].event], id, ts->finger[id].id);
+#endif
+ ts->finger[id].event = TS_EVENT_UNKNOWN;
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, false);
+ }
+ }
+ input_sync(ts->input);
+}
+
+//----------------------------------------------
+static void touch_event_clear(struct touch *ts)
+{
+ unsigned char id;
+
+ for(id = 0; id < ts->pdata->max_fingers; id++) {
+ if(ts->finger[id].event == TS_EVENT_MOVE) {
+ ts->finger[id].status = true;
+ ts->finger[id].event = TS_EVENT_RELEASE;
+ }
+ }
+ ts->pdata->report(ts);
+ if(ts->pdata->keycode)
+ ts->pdata->key_report(ts, 0x00);
+}
+
+//----------------------------------------------
+static int touch_input_open(struct input_dev *input)
+{
+ struct touch *ts = input_get_drvdata(input);
+
+ ts->pdata->enable(ts);
+
+ dbg("%s\n", __func__);
+
+ return 0;
+}
+
+//----------------------------------------------
+static void touch_input_close(struct input_dev *input)
+{
+ struct touch *ts = input_get_drvdata(input);
+
+ ts->pdata->disable(ts);
+
+ dbg("%s\n", __func__);
+}
+
+//----------------------------------------------
+static int touch_check_functionality(struct touch_pdata *pdata)
+{
+ if(!pdata) {
+ errlog("Error : Platform data is NULL pointer!\n"); return -1;
+ }
+
+ pdata->i2c_read = sn310m_i2c_read;
+ pdata->i2c_write = sn310m_i2c_write;
+
+ pdata->i2c_boot_read = sn310m_i2c_read;
+ pdata->i2c_boot_write = sn310m_i2c_write;
+
+ pdata->enable = sn310m_enable;
+ pdata->disable = sn310m_disable;
+ pdata->probe = sn310m_probe;
+
+ if(!pdata->report) {
+ if(pdata->id_max) pdata->report = touch_report_protocol_b;
+ else pdata->report = touch_report_protocol_a;
+ }
+ if(!pdata->key_report) pdata->key_report = touch_key_report;
+
+ pdata->touch_work = sn310m_work;
+
+ if(!pdata->irq_func) pdata->irq_func = touch_irq;
+
+ if(!pdata->event_clear) pdata->event_clear = touch_event_clear;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if(!pdata->resume) pdata->resume = touch_resume;
+ if(!pdata->suspend) pdata->suspend = touch_suspend;
+#endif
+
+ //pdata->irq_gpio = 7;
+
+ return 0;
+}
+
+//----------------------------------------------
+void touch_hw_reset(struct touch *ts)
+{
+ if(ts->pdata->reset_gpio) {
+ if(gpio_request(ts->pdata->reset_gpio, "touch reset")) {
+ errlog("--------------------------------------------------------\n");
+ errlog("%s : request port error!\n", "touch reset");
+ errlog("--------------------------------------------------------\n");
+ }
+ else {
+ if(ts->pdata->power) {
+ /* power sequence: reset low -> power on -> reset high */
+ gpio_direction_output(ts->pdata->reset_gpio, 0);
+ gpio_set_value(ts->pdata->reset_gpio, 0);
+ mdelay(15);
+ ts->pdata->power(1);
+ mdelay(50);
+ gpio_set_value(ts->pdata->reset_gpio, 1);
+ mdelay(15);
+ }
+ else {
+ /* if there is no power control for touch, then just do reset (high -> low -> high) */
+ gpio_direction_output(ts->pdata->reset_gpio, 1);
+ gpio_set_value(ts->pdata->reset_gpio, 1);
+ mdelay(15);
+ gpio_set_value(ts->pdata->reset_gpio, 0);
+ mdelay(20);
+ gpio_set_value(ts->pdata->reset_gpio, 1);
+ mdelay(15);
+ }
+ }
+ }
+}
+
+//----------------------------------------------
+int touch_info_display(struct touch *ts)
+{
+ errlog("--------------------------------------------------------\n");
+ errlog(" TOUCH SCREEN INFORMATION\n");
+ errlog("--------------------------------------------------------\n");
+ if(ts->pdata->irq_gpio) {
+ errlog("TOUCH INPUT Name = %s\n", ts->pdata->name);
+
+ switch(ts->pdata->irq_mode) {
+ default :
+ case IRQ_MODE_THREAD: errlog("TOUCH IRQ Mode = %s\n", "IRQ_MODE_THREAD"); break;
+ case IRQ_MODE_NORMAL: errlog("TOUCH IRQ Mode = %s\n", "IRQ_MODE_NORMAL"); break;
+ case IRQ_MODE_POLLING: errlog("TOUCH IRQ Mode = %s\n", "IRQ_MODE_POLLING"); break;
+ }
+ errlog("TOUCH F/W Version = %d.%02d\n", ts->fw_version / 100, ts->fw_version % 100);
+ errlog("TOUCH FINGRES MAX = %d\n", ts->pdata->max_fingers);
+ errlog("TOUCH ABS X MAX = %d, TOUCH ABS X MIN = %d\n", ts->pdata->abs_max_x, ts->pdata->abs_min_x);
+ errlog("TOUCH ABS Y MAX = %d, TOUCH ABS Y MIN = %d\n", ts->pdata->abs_max_y, ts->pdata->abs_min_y);
+
+ if(ts->pdata->area_max)
+ errlog("TOUCH MAJOR MAX = %d, TOUCH MAJOR MIN = %d\n", ts->pdata->area_max, ts->pdata->area_min);
+
+ if(ts->pdata->press_max)
+ errlog("TOUCH PRESS MAX = %d, TOUCH PRESS MIN = %d\n", ts->pdata->press_max, ts->pdata->press_min);
+
+ if(ts->pdata->id_max) {
+ errlog("TOUCH ID MAX = %d, TOUCH ID MIN = %d\n", ts->pdata->id_max, ts->pdata->id_min);
+ errlog("Mulit-Touch Protocol-B Used.\n");
+ }
+ else
+ errlog("Mulit-Touch Protocol-A Used.\n");
+
+ if(ts->pdata->gpio_init)
+ errlog("GPIO early-init function implemented\n");
+
+ if(ts->pdata->reset_gpio)
+ errlog("H/W Reset function implemented\n");
+
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ errlog("Early-suspend function implemented\n");
+ #endif
+ if(ts->pdata->fw_control)
+ errlog("Firmware update function(sysfs control) implemented\n");
+
+ /* flashing sample is not implemented yet */
+ if(ts->pdata->flash_firmware)
+ errlog("Firmware update function(udev control) implemented\n");
+
+ if(ts->pdata->calibration)
+ errlog("Calibration function implemented\n");
+ }
+ else {
+ errlog("TOUCH INPUT Name = %s\n", ts->pdata->name);
+ errlog("Dummy Touchscreen driver!\n");
+ }
+ errlog("--------------------------------------------------------\n");
+ return 0;
+}
+
+//----------------------------------------------
+int touch_probe(struct i2c_client *client, const struct i2c_device_id *client_id)
+{
+ return -1;
+}
+
+//----------------------------------------------
+//
+// Power Management function
+//
+//----------------------------------------------
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void touch_suspend(struct early_suspend *h)
+{
+ struct touch *ts = container_of(h, struct touch, power);
+
+ dbg("%s++\n", __func__);
+
+ /* TSC enters deep sleep mode */
+ dbg("[%s] touch reset goes low!\n", __func__);
+ gpio_direction_output(ts->pdata->reset_gpio, 0);
+ gpio_set_value(ts->pdata->reset_gpio, 0);
+
+ ts->pdata->disable(ts);
+}
+
+//----------------------------------------------
+static void touch_resume(struct early_suspend *h)
+{
+ struct touch *ts = container_of(h, struct touch, power);
+
+ dbg("%s++\n", __func__);
+
+ /* TSC enters active mode */
+ dbg("[%s] touch reset goes high!\n", __func__);
+ gpio_direction_output(ts->pdata->reset_gpio, 1);
+ gpio_set_value(ts->pdata->reset_gpio, 1);
+
+ ts->pdata->enable(ts);
+}
+#endif
+
+//----------------------------------------------
+int touch_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct touch *ts = dev_get_drvdata(dev);
+
+ dbg("touch_remove++");
+
+ if(ts->irq) free_irq(ts->irq, ts);
+
+ if(ts->pdata->reset_gpio) gpio_free(ts->pdata->reset_gpio);
+
+ if(ts->pdata->irq_gpio) gpio_free(ts->pdata->irq_gpio);
+
+ input_unregister_device(ts->input);
+
+ dev_set_drvdata(dev, NULL);
+
+#if defined(SN310M_NATIVE_INTERFACE)
+ P_SN310M_Dist_Remove();
+#endif
+
+ kfree(ts->finger); ts->finger = NULL;
+ kfree(ts); ts = NULL;
+
+ return 0;
+}
+
+#if defined(SN310M_NATIVE_INTERFACE)
+#define SN310M_DIST_MINOR 250
+
+typedef struct {
+ unsigned int addr;
+ short *buf;
+ unsigned int size;
+} packet_t;
+
+static const struct file_operations SN310M_Dist_Fops =
+{
+ .owner = THIS_MODULE,
+ .open = P_SN310M_Dist_Open,
+ .unlocked_ioctl = P_SN310M_Dist_Ioctl,
+};
+
+
+static struct miscdevice SN310M_Dist_MiscDev =
+{
+ .minor = SN310M_DIST_MINOR,
+ .name = "sn310m_dist",
+ .fops = &SN310M_Dist_Fops,
+ .mode = 0x666,
+};
+
+
+static long P_SN310M_Dist_Ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ packet_t* packet = (packet_t*)arg;
+ int i;
+
+ mutex_lock(&g_ts->mutex);
+ switch(cmd) {
+ case 0: // write data
+ if(packet->size) {
+ unsigned short addr = (packet->addr >> 8) | (packet->addr & 0x00ff) << 8;
+ g_ts->pdata->i2c_write(g_ts->client, (unsigned char *)&addr, sizeof(addr), (unsigned char *)packet->buf, packet->size*2);
+ dbg("Request I2C Write\n");
+ }
+ break;
+
+ case 1: // read data
+ if(packet->size) {
+ unsigned short addr = (packet->addr >> 8) | (packet->addr & 0x00ff) << 8;
+ short buffer[500] = {0, };
+
+ g_ts->pdata->i2c_read(g_ts->client, (unsigned char *)&addr, sizeof(addr), (unsigned char *)buffer, packet->size*2);
+ for(i = 0; (i < packet->size) && (i < 500); i++) {
+ packet->buf[i] = buffer[i];
+ }
+ dbg("Request I2C Read\n");
+ }
+ break;
+
+ default:
+ mutex_unlock(&g_ts->mutex);
+ return -ENOIOCTLCMD;
+ }
+
+ mutex_unlock(&g_ts->mutex);
+ return 0;
+}
+
+static int P_SN310M_Dist_Open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int P_SN310M_Dist_Probe(struct touch* ts)
+{
+ int result = 0;
+
+ g_ts = ts;
+ result = misc_register(&SN310M_Dist_MiscDev);
+ if(result == 0) {
+ dbg("succeeded to register sn310m_misc_device \n");
+ }
+ else {
+ errlog("failed to register sn310m_misc_device \n");
+ }
+
+ return result;
+}
+
+static void P_SN310M_Dist_Remove(void)
+{
+ misc_deregister(&SN310M_Dist_MiscDev);
+ g_ts = NULL;
+}
+#endif
+static const struct i2c_device_id sample_ts_id[] = {
+ { I2C_TOUCH_NAME, 0 },
+ {},
+};
+
+
+
+#define TS_DRIVER_NAME "wmt-touch"
+
+static void wmt_ts_platform_release(struct device *device)
+{
+ dbg("wmt_ts_platform_release\n");
+ return;
+}
+
+static struct platform_device wmt_ts_plt_device = {
+ .name = TS_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = wmt_ts_platform_release,
+ },
+};
+
+static int sn310m_keycode[] = {
+ KEY_HOME, KEY_MENU, KEY_BACK, KEY_SEARCH
+};
+
+struct touch_pdata sn310m_touch_pdata = {
+
+ .name = "sn310m", // input drv name
+ .irq_gpio = 7,//SAMPLE_GPIO_0, // irq gpio define
+ .reset_gpio = 4,//SAMPLE_GPIO_1, // reset gpio define
+ .reset_level = 0, // reset level setting (1 = High reset, 0 = Low reset)
+
+ .irq_mode = IRQ_MODE_NORMAL, // IRQ_MODE_THREAD, IRQ_MODE_NORMAL, IRQ_MODE_POLLING
+ .irq_flags = IRQF_SHARED ,//IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+
+ .abs_max_x = 600,
+ .abs_max_y = 1024,
+
+ .area_max = 10,
+ .press_max = 255,
+
+ .id_max = 10 + 1,
+ .id_min = 0,
+
+ .vendor = 0x16B4,
+ .product = 0x0310,
+ .version = 0x0001,
+
+ .max_fingers = 5,
+
+ .keycnt = 4,
+ .keycode = sn310m_keycode,
+ .lcd_exchg = 0,
+
+ //--------------------------------------------
+ // Control function
+ //--------------------------------------------
+ .touch_work = sn310m_work,
+ .enable = sn310m_enable,
+ .disable = sn310m_disable,
+ .early_probe = sn310m_early_probe,
+ .probe = sn310m_probe,
+
+ //--------------------------------------------
+ // I2C control function
+ //--------------------------------------------
+ .i2c_write = sn310m_i2c_write,
+ .i2c_read = sn310m_i2c_read,
+
+ //--------------------------------------------
+ // Calibration function
+ //--------------------------------------------
+ .calibration = sn310m_calibration,
+
+ //--------------------------------------------
+ // Firmware update control function
+ //--------------------------------------------
+ .fw_filename = "sn310m_fw.bin",
+ .fw_filesize = (10 * 1024), // 10K bytes
+ .input_open = sn310m_input_open,
+ .flash_firmware = sn310m_flash_firmware,
+};
+
+
+int temp;
+static int wmt_ts_probe(struct platform_device *pdev)
+{
+ int rc = -1;
+ struct i2c_client *client = l_client;
+ struct device *dev = &client->dev;
+ struct touch *ts;
+
+
+ dbg("wmt_ts_probe\n");
+
+ if(!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "i2c byte data not supported\n");
+ return -EIO;
+ }
+
+
+ client->dev.platform_data = &sn310m_touch_pdata;
+
+ if(touch_check_functionality(client->dev.platform_data) < 0) {
+ dev_err(&client->dev, "Platform data is not available!\n");
+ return -EINVAL;
+ }
+
+ if(!(ts = kzalloc(sizeof(struct touch), GFP_KERNEL))) {
+ errlog("touch struct malloc error!\n");
+ return -ENOMEM;
+ }
+ ts->client = client;
+ ts->pdata = client->dev.platform_data;
+
+
+ /* by limst, setting gpio for IRQ */
+ if(ts->pdata->irq_gpio) {
+ int ret;
+
+ ts->irq = IRQ_GPIO;//MSM_GPIO_TO_INT(ts->pdata->irq_gpio);
+ dbg("IRQ_GPIO(%d) IRQ(%d) REG\n", ts->pdata->irq_gpio, ts->irq);
+
+ ret = gpio_request(ts->pdata->irq_gpio, "touch_int");
+ if(ret < 0)
+ errlog("FAIL: touch_int gpio_request\n");
+ else
+ dbg("OK: touch_int gpio_request value(%d)\n", gpio_get_value(ts->pdata->irq_gpio));
+
+ wmt_gpio_setpull(ts->pdata->irq_gpio,WMT_GPIO_PULL_UP);
+ gpio_direction_input(ts->pdata->irq_gpio);
+ wmt_gpio_set_irq_type(ts->pdata->irq_gpio, IRQ_TYPE_EDGE_FALLING);
+ }
+
+ i2c_set_clientdata(client, ts);
+
+ if(ts->pdata->max_fingers) {
+ if(!(ts->finger = kzalloc(sizeof(finger_t) * ts->pdata->max_fingers, GFP_KERNEL))) {
+ kfree(ts);
+ errlog("touch data struct malloc error!\n");
+ return -ENOMEM;
+ }
+ }
+
+ if(ts->pdata->gpio_init) ts->pdata->gpio_init();
+
+ if(ts->pdata->early_probe) {
+ if((rc = ts->pdata->early_probe(ts)) < 0)
+ goto err_free_mem;
+ }
+
+
+ dev_set_drvdata(dev, ts);
+
+ if(!(ts->input = input_allocate_device()))
+ goto err_free_mem;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", ts->pdata->name);
+
+ if(!ts->pdata->input_open) ts->input->open = touch_input_open;
+ else ts->input->open = ts->pdata->input_open;
+ if(!ts->pdata->input_close) ts->input->close = touch_input_close;
+ else ts->input->close = ts->pdata->input_close;
+
+ /*
+ * by limst, for the test purpose,
+ * input device's name is forcedly set to the name of android idc file
+ */
+ ts->input->name = "qwerty";//idc's filename //"touch_dev";
+ //ts->input->name = ts->pdata->name;
+ ts->input->phys = ts->phys;
+ ts->input->dev.parent = dev;
+ ts->input->id.bustype = BUS_I2C;
+
+ ts->input->id.vendor = ts->pdata->vendor;
+ ts->input->id.product = ts->pdata->product;
+ ts->input->id.version = ts->pdata->version;
+
+ set_bit(EV_SYN, ts->input->evbit);
+ set_bit(EV_ABS, ts->input->evbit);
+
+ /* Register Touch Key Event */
+ if(ts->pdata->keycode) {
+ int key;
+
+ set_bit(EV_KEY, ts->input->evbit);
+
+ for(key = 0; key < ts->pdata->keycnt; key++) {
+ if(ts->pdata->keycode[key] <= 0) continue;
+ set_bit(ts->pdata->keycode[key] & KEY_MAX, ts->input->keybit);
+ }
+ }
+
+ input_set_drvdata(ts->input, ts);
+
+ if (sn310m_touch_pdata.lcd_exchg) {
+ input_set_abs_params(ts->input, ABS_MT_POSITION_X, ts->pdata->abs_min_y, ts->pdata->abs_max_y, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, ts->pdata->abs_min_x, ts->pdata->abs_max_x, 0, 0);
+ } else {
+ input_set_abs_params(ts->input, ABS_MT_POSITION_X, ts->pdata->abs_min_x, ts->pdata->abs_max_x, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, ts->pdata->abs_min_y, ts->pdata->abs_max_y, 0, 0);
+ }
+
+ if(ts->pdata->area_max)
+ input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, ts->pdata->area_min, ts->pdata->area_max, 0, 0);
+
+ if(ts->pdata->press_max)
+ input_set_abs_params(ts->input, ABS_MT_PRESSURE, ts->pdata->press_min, ts->pdata->press_max, 0, 0);
+
+ if(ts->pdata->id_max) {
+ input_set_abs_params(ts->input, ABS_MT_TRACKING_ID, ts->pdata->id_min, ts->pdata->id_max, 0, 0);
+ input_mt_init_slots(ts->input, ts->pdata->max_fingers);
+ }
+
+
+ mutex_init(&ts->mutex);
+ if(ts->irq) {
+ switch(ts->pdata->irq_mode) {
+ default :
+ case IRQ_MODE_THREAD:
+ INIT_WORK(&ts->work, touch_work_q);
+ if((ts->work_queue = create_singlethread_workqueue("work_queue")) == NULL)
+ goto err_free_input_mem;
+
+ if((rc = request_threaded_irq(ts->irq, NULL, ts->pdata->irq_func,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ts->pdata->name, ts))) {
+ dev_err(dev, "threaded irq %d request fail!\n", ts->irq);
+ goto err_free_input_mem;
+ }
+ break;
+ case IRQ_MODE_NORMAL:
+ INIT_WORK(&ts->work, touch_work_q);
+ if((ts->work_queue = create_singlethread_workqueue("work_queue")) == NULL)
+ goto err_free_input_mem;
+
+ if((rc = request_irq(ts->irq, ts->pdata->irq_func, ts->pdata->irq_flags, ts->pdata->name, ts))) {
+ errlog("irq %d request fail!\n", ts->irq);
+ goto err_free_input_mem;
+ }
+ dbg("irq %d request ok!\n", ts->irq);
+ break;
+ case IRQ_MODE_POLLING:
+ errlog("Error IRQ_MODE POLLING!! but defined irq_gpio\n");
+ break;
+ } /* end of switch */
+ }
+ ts->disabled = true;
+
+ if((rc = input_register_device(ts->input))) {
+ dev_err(dev, "(%s) input register fail!\n", ts->input->name);
+ goto err_free_input_mem;
+ }
+
+ /* by limst, added to turn on the power and reset of Touch IC */
+ touch_hw_reset(ts);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ if(ts->pdata->suspend) ts->power.suspend = ts->pdata->suspend;
+ if(ts->pdata->resume) ts->power.resume = ts->pdata->resume;
+
+ ts->power.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1;
+
+ register_early_suspend(&ts->power);
+#endif
+
+ if(ts->pdata->probe) {
+ ts->pdata->probe(ts);
+ }
+
+ touch_info_display(ts);
+
+#if defined(SN310M_NATIVE_INTERFACE)
+ if(P_SN310M_Dist_Probe(ts) < 0) {
+ errlog("P_SN310M_Dist_Probe(), fail\n");
+ }
+#endif
+
+ return 0;
+
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ err_free_input_mem:
+ input_free_device(ts->input);
+ ts->input = NULL;
+ err_free_mem:
+ kfree(ts->finger);
+ ts->finger = NULL;
+ kfree(ts);
+ ts = NULL;
+ return rc;
+}
+
+static int wmt_ts_remove(struct platform_device *pdev)
+{
+ struct i2c_client *client = l_client;
+ struct device *dev = &client->dev;
+ struct touch *ts = dev_get_drvdata(dev);
+
+ dbg("wmt_ts_remove\n");
+
+ if(ts->irq) free_irq(ts->irq, ts);
+
+ if(ts->pdata->reset_gpio) gpio_free(ts->pdata->reset_gpio);
+
+ if(ts->pdata->irq_gpio) gpio_free(ts->pdata->irq_gpio);
+
+ input_unregister_device(ts->input);
+
+ dev_set_drvdata(dev, NULL);
+
+ #if defined(SN310M_NATIVE_INTERFACE)
+ P_SN310M_Dist_Remove();
+ #endif
+
+ kfree(ts->finger); ts->finger = NULL;
+ kfree(ts); ts = NULL;
+
+ return 0;
+}
+
+static int wmt_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct i2c_client *client = l_client;
+ struct device *dev = &client->dev;
+ struct touch *ts = dev_get_drvdata(dev);
+
+ dbg("%s++\n", __func__);
+
+ /* TSC enters deep sleep mode */
+ dbg("[%s] touch reset goes low!\n", __func__);
+ gpio_direction_output(ts->pdata->reset_gpio, 0);
+ gpio_set_value(ts->pdata->reset_gpio, 0);
+
+
+ ts->pdata->disable(ts);
+
+ return 0;
+}
+static int wmt_ts_resume(struct platform_device *pdev)
+{
+ struct i2c_client *client = l_client;
+ struct device *dev = &client->dev;
+ struct touch *ts = dev_get_drvdata(dev);
+
+ dbg("%s++\n", __func__);
+
+ /* TSC enters active mode */
+ dbg("[%s] touch reset goes high!\n", __func__);
+ gpio_direction_output(ts->pdata->reset_gpio, 1);
+ gpio_set_value(ts->pdata->reset_gpio, 1);
+
+ ts->pdata->enable(ts);
+ //touch_hw_reset(ts);
+
+ return 0;
+}
+
+
+static struct platform_driver wmt_ts_plt_driver = {
+ .driver = {
+ .name = TS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = wmt_ts_probe,
+ .remove = wmt_ts_remove,
+ .suspend = wmt_ts_suspend,
+ .resume = wmt_ts_resume,
+};
+
+
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = WMT_TS_I2C_NAME,
+ .flags = 0x00,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+
+ ts_i2c_board_info.addr =(unsigned short) 0x3c;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(1);/*in bus 1*/
+
+ if (NULL == adapter) {
+ errlog("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ errlog("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+static struct tp_info l_tpinfo;
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 127;
+ char retval[200] = {0};
+ char *p=NULL;
+ char *s=NULL;
+ int Enable=0;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ errlog("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ memset(&l_tpinfo,0,sizeof(l_tpinfo));
+
+ p = retval;
+ sscanf(p,"%d:", &Enable);
+ p = strchr(p,':');
+ p++;
+ s = strchr(p,':');
+ strncpy(l_tpinfo.name,p, (s-p));
+ p = s+1;
+ //dbg("ts_name=%s\n", l_tpinfo.name);
+
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &l_tpinfo.irq_gpio,&l_tpinfo.panelres_x,&l_tpinfo.panelres_y,&l_tpinfo.rst_gpio,
+ &(l_tpinfo.xaxis),&(l_tpinfo.xdir),&(l_tpinfo.ydir),
+ &(l_tpinfo.max_finger_num),&l_tpinfo.i2caddr,&l_tpinfo.low_Impendence_mode,&l_tpinfo.download_option);
+
+ if (ret < 8){
+ errlog("Wrong format ts u-boot param(%d)!\nwmt.io.touch=%s\n",ret,retval);
+ return -ENODEV;
+ }
+
+ //check touch enable
+ if(Enable == 0){
+ errlog("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+ if (strstr(l_tpinfo.name, sn310m_touch_pdata.name) == NULL){
+ errlog("Can't find %s in the wmt.io.touch\n", sn310m_touch_pdata.name);
+ return -ENODEV;
+ }
+
+ errlog("p.x = %d, p.y = %d, gpio=%d, resetgpio=%d,xaxis=%d,xdir=%d,ydri=%d,maxfingernum=%d,,i2c_addr=0x%X,low_Impendence_mode=%d,s_download_option=%d\n",
+ l_tpinfo.panelres_x, l_tpinfo.panelres_y, l_tpinfo.irq_gpio, l_tpinfo.rst_gpio,
+ l_tpinfo.xaxis,l_tpinfo.xdir,l_tpinfo.ydir,
+ l_tpinfo.max_finger_num,l_tpinfo.i2caddr,l_tpinfo.low_Impendence_mode,l_tpinfo.download_option);
+
+ sn310m_touch_pdata.irq_gpio = l_tpinfo.irq_gpio;
+ sn310m_touch_pdata.reset_gpio = l_tpinfo.rst_gpio;
+ sn310m_touch_pdata.abs_max_x = l_tpinfo.panelres_x;
+ sn310m_touch_pdata.abs_max_y = l_tpinfo.panelres_y;
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ sn310m_touch_pdata.lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+
+static int __init sample_touch_init(void)
+{
+ int ret = 0;
+
+ if(wmt_check_touch_env())
+ return -ENODEV;
+
+
+ if (ts_i2c_register_device()<0){
+ errlog("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ ret = platform_device_register(&wmt_ts_plt_device);
+ if(ret){
+ errlog("wmt ts plat device register failed!\n");
+ return ret;
+ }
+ ret = platform_driver_register(&wmt_ts_plt_driver);
+ if(ret){
+ errlog("can not register platform_driver_register\n");
+ platform_device_unregister(&wmt_ts_plt_device);
+ return ret;
+ }
+ return 0;
+}
+
+static void sample_touch_exit(void)
+{
+ platform_driver_unregister(&wmt_ts_plt_driver);
+ platform_device_unregister(&wmt_ts_plt_device);
+ ts_i2c_unregister_device();
+
+ return;
+}
+
+
+module_init(sample_touch_init);
+module_exit(sample_touch_exit);
+
+#ifndef MODULE
+__initcall(sample_touch_init);
+#endif
+
+
+
+MODULE_AUTHOR("SEMISENS Co., Ltd.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Touchscreen Driver for SN310M");
diff --git a/drivers/input/touchscreen/semisens/touch.h b/drivers/input/touchscreen/semisens/touch.h
new file mode 100755
index 00000000..750112ea
--- /dev/null
+++ b/drivers/input/touchscreen/semisens/touch.h
@@ -0,0 +1,54 @@
+/****************************************************************
+ *
+ * touch.c : I2C Touchscreen driver
+ *
+ * Copyright (c) 2013 SEMISENS Co.,Ltd
+ * http://www.semisens.com
+ *
+ ****************************************************************/
+#ifndef _TOUCH_H_
+#define _TOUCH_H_
+
+//----------------------------------------------
+// extern function define
+//----------------------------------------------
+extern void touch_hw_reset(struct touch *ts);
+extern int touch_info_display(struct touch *ts);
+#if 0 /* depends on kernel version */
+extern int touch_probe(struct i2c_client *client);
+extern int touch_remove(struct device *dev);
+#else
+extern int touch_probe(struct i2c_client *client, const struct i2c_device_id *client_id);
+extern int touch_remove(struct i2c_client *client);
+#endif
+
+struct tp_info
+{
+ char name[64];
+ unsigned int xaxis; //0: x, 1: x swap with y
+ unsigned int xdir; // 1: positive,-1: revert
+ unsigned int ydir; // 1: positive,-1: revert
+ unsigned int max_finger_num;
+ unsigned int download_option; // 0: disable 1:force download 2:force cancel download
+ unsigned int low_Impendence_mode; // 0: High Impendence Mode 1: Low Impendence Mode
+ unsigned int irq_gpio;
+ unsigned int rst_gpio;
+ unsigned int panelres_x;
+ unsigned int panelres_y;
+ unsigned int i2caddr;
+ unsigned int lcd_exchg;
+#if 0
+ struct input_dev *inputdev;
+ struct work_struct int_work;
+ struct i2c_client *i2cclient;
+ struct workqueue_struct *wq;
+#if SUPPORT_TS_KEY
+ int key_num;
+#endif
+#endif
+
+};
+
+
+
+#endif /* _TOUCH_H_ */
diff --git a/drivers/input/touchscreen/sis_usbhid_ts/Kconfig b/drivers/input/touchscreen/sis_usbhid_ts/Kconfig
new file mode 100755
index 00000000..21574ec3
--- /dev/null
+++ b/drivers/input/touchscreen/sis_usbhid_ts/Kconfig
@@ -0,0 +1,16 @@
+#
+# SIS USB capacity touch screen driver configuration
+#
+config TOUCHSCREEN_SIS
+ tristate "SIS USB Capacitive Touchscreen Input Driver Support"
+ depends on ARCH_WMT
+ default y
+ help
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_sis
+
diff --git a/drivers/input/touchscreen/sis_usbhid_ts/Makefile b/drivers/input/touchscreen/sis_usbhid_ts/Makefile
new file mode 100755
index 00000000..045ea698
--- /dev/null
+++ b/drivers/input/touchscreen/sis_usbhid_ts/Makefile
@@ -0,0 +1,32 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_sis
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := hid-sis.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers modules.builtin
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers modules.builtin
diff --git a/drivers/input/touchscreen/sis_usbhid_ts/hid-sis.c b/drivers/input/touchscreen/sis_usbhid_ts/hid-sis.c
new file mode 100755
index 00000000..0b9cee3d
--- /dev/null
+++ b/drivers/input/touchscreen/sis_usbhid_ts/hid-sis.c
@@ -0,0 +1,1104 @@
+/*
+ * HID driver for sis 9237/9257 test touchscreens
+ *
+ * Copyright (c) 2008 Rafi Rubin
+ * Copyright (c) 2009 Stephane Chatty
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/hid-debug.h>
+//for i2c-bridge
+#include <linux/usb.h>
+#include "../../../hid/usbhid/usbhid.h"
+#include <asm/uaccess.h>
+#include <linux/types.h>
+
+#ifdef CONFIG_HID_SIS_UPDATE_FW
+//for ioctl
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+
+#define INTERNAL_DEVICE_NAME "sis_zeus_hid_touch_device"
+#define BRIDGE_DEVICE_NAME "sis_zeus_hid_bridge_touch_device"
+#define SIS817_DEVICE_NAME "sis_aegis_hid_touch_device"
+#define SISF817_DEVICE_NAME "sis_aegis_hid_bridge_touch_device"
+
+static int sis_char_devs_count = 1; /* device count */
+static int sis_char_major = 0;
+static struct cdev sis_char_cdev;
+static struct class *sis_char_class = NULL;
+//20110111 Tammy system call for tool
+static struct hid_device *hid_dev_backup = NULL; //backup address
+static struct urb *backup_urb = NULL;
+#endif //CONFIG_HID_SIS_UPDATE_FW
+
+///////////////// SIS START /////////////////
+#define USB_VENDOR_ID_SIS_TOUCH 0x1039
+#define USB_VENDOR_ID_SIS2_TOUCH 0x0457
+#define USB_PRODUCT_ID_SIS_TOUCH 0x0810
+#define USB_PRODUCT_ID_SIS2_TOUCH 0x0151
+#define USB_PRODUCT_ID_NEW_SIS2_TOUCH 0x0816
+#define USB_PRODUCT_ID_SIS9200_TOUCH 0x9200
+#define USB_PRODUCT_ID_SIS817_TOUCH 0x0817
+#define USB_PRODUCT_ID_SISF817_TOUCH 0xF817
+
+//waltop id-table
+#define USB_VENUS_ID_WALTOP 0x0503
+#define USB_VENUS_ID_WALTOP2 0x1040
+///////////////// SIS END /////////////////
+//#define CONFIG_HID_SIS_UPDATE_FW
+//#define CONFIG_DEBUG_HID_SIS_INIT
+//#define CONFIG_DEBUG_HID_SIS_SENDPOINT
+
+#define MAX_X 4095
+#define MAX_Y 4095
+//#define MAX_PRESSURE 2047
+#define MAX_SCANTIME 65535
+#define MAX_CONTACTID 31
+
+#define MAX_POINT 10
+#define HID_DG_SCANTIME 0x000d0056 //new usage not defined in hid.h
+#define REPORTID_10 0x10
+#define REPORTID_TYPE1 0x30
+
+#define CTRL 0
+#define DIR_IN 0x1
+
+struct Point {
+ u16 x, y, id, pressure, width, height;
+};
+
+struct sis_data {
+ int id, total, ReportID, scantime;
+ struct Point pt[MAX_POINT];
+};
+
+
+static int pkg_num=0;
+static int idx=-1;
+
+/*
+ * this driver is aimed at two firmware versions in circulation:
+ * - dual pen/fingedrivers/hid/hid-sis.c:83:r single touch
+ * - finger multitouch, pen not working
+ */
+
+static int sis_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ // No special mappings needed for the pen and single touch
+ if (field->physical == HID_GD_POINTER)
+ return -1;
+
+ else if (field->physical && (field->physical != HID_GD_POINTER))
+ return 0;
+
+#ifdef CONFIG_DEBUG_HID_SIS_INIT
+ printk (KERN_INFO "sis_input_mapping : usage->hid = %x\n", usage->hid);
+#endif //CONFIG_DEBUG_HID_SIS_INIT
+
+ switch (usage->hid & HID_USAGE_PAGE) {
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X);
+ input_set_abs_params(hi->input, ABS_X,
+ field->logical_minimum, field->logical_maximum, 0, 0);
+ return 1;
+
+ case HID_GD_Y:
+ hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y);
+ input_set_abs_params(hi->input, ABS_Y,
+ field->logical_minimum, field->logical_maximum, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ /* we do not want to map these for now */
+ case HID_DG_CONFIDENCE:
+ case HID_DG_INPUTMODE:
+ case HID_DG_DEVICEINDEX:
+ case HID_DG_CONTACTCOUNT:
+ case HID_DG_CONTACTMAX:
+ case HID_DG_INRANGE:
+
+ //new usage for SiS817 Device(for later use)
+ case HID_DG_WIDTH:
+ //hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MINOR);
+ //input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);
+ //return 1;
+ case HID_DG_HEIGHT:
+ //hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MAJOR);
+ //input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ //return 1;
+ case HID_DG_TIPPRESSURE:
+ //hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE);
+ //input_set_abs_params(hi->input, ABS_MT_PRESSURE, 0, 2047, 0, 0);
+ //return 1;
+ case HID_DG_SCANTIME:
+ return -1;
+
+ case HID_DG_TIPSWITCH:
+ hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE);
+ input_set_abs_params(hi->input, ABS_MT_PRESSURE, 0, 1, 0, 0);
+ return 1;
+
+ case HID_DG_CONTACTID:
+ hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TRACKING_ID);
+ input_set_abs_params(hi->input, ABS_MT_TRACKING_ID, 0, 127, 0, 0);
+ return 1;
+ }
+ return 0;
+
+ /*case HID_UP_BUTTON:
+ return 0;*/
+
+ case 0xff000000:
+ /* ignore HID features */
+ return -1;
+
+ }
+ /* ignore buttons */
+ return 0;
+}
+
+//sis_input_mapped : unmapped usage that no use in sis_event
+static int sis_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+#ifdef CONFIG_DEBUG_HID_SIS_INIT
+ printk (KERN_INFO "sis_input_mapping : usage->hid = %x\n", usage->hid);
+#endif //CONFIG_DEBUG_HID_SIS_INIT
+
+ if (usage->type == EV_KEY || usage->type == EV_ABS)
+ clear_bit(usage->code, *bit);
+
+ return 0;
+}
+
+static void sis_event_emission(struct sis_data *nd, struct input_dev *input)
+{
+ int i;
+ bool all_touch_up = true;
+ for(i=0; i< nd->total; i++)
+ {
+
+#ifdef CONFIG_DEBUG_HID_SIS_SENDPOINT
+ printk(KERN_INFO "MT_event: finger(s)=%d, id=%d, x=%d, y=%d\n", nd->total, nd->pt[i].id, nd->pt[i].x, nd->pt[i].y);
+ printk(KERN_INFO "MT_event: pressure=%d, width=%d, height=%d, scantime=%d\n", nd->pt[i].pressure, nd->pt[i].width, nd->pt[i].height, nd->scantime);
+#endif //CONFIG_DEBUG_HID_SIS_SENDPOINT
+
+ //checking correction of data
+ if(nd->pt[i].x > MAX_X || nd->pt[i].y > MAX_Y || nd->pt[i].id > MAX_CONTACTID /*|| nd->scantime > MAX_SCANTIME*/)
+ {
+ printk(KERN_INFO "point data error : abort sending point this time");
+ break;
+ }
+
+ if(nd->pt[i].pressure)
+ {
+ //input_report_abs(input, ABS_MT_TOUCH_MAJOR, max(nd->pt[i].height,nd->pt[i].width));
+ //input_report_abs(input, ABS_MT_TOUCH_MINOR, min(nd->pt[i].height,nd->pt[i].width));
+
+ input_report_abs(input, ABS_MT_PRESSURE, nd->pt[i].pressure);
+ input_report_abs(input, ABS_MT_POSITION_X, MAX_Y - nd->pt[i].y);
+ input_report_abs(input, ABS_MT_POSITION_Y, nd->pt[i].x);
+ input_report_abs(input, ABS_MT_TRACKING_ID, nd->pt[i].id);
+ input_mt_sync(input);
+ all_touch_up = false;
+ }
+
+ if(i == (nd->total - 1) && all_touch_up == true)
+ input_mt_sync(input);
+ }
+ //input_sync(input);
+ //input_sync will be send by hid default flow
+}
+
+static void sis_event_clear(struct sis_data *nd, int max)
+{
+ int i;
+ for(i=0; i<max; i++)
+ {
+ nd->pt[i].id = 0;
+ nd->pt[i].x = 0;
+ nd->pt[i].y = 0;
+ nd->pt[i].pressure = 0;
+ nd->pt[i].width = 0;
+ nd->pt[i].height = 0;
+ }
+ nd->scantime = 0;
+ idx = -1;
+ pkg_num = 0;
+}
+
+static int sis_raw_event (struct hid_device *hid, struct hid_report *report,
+ u8 *raw_data, int size)
+{
+ struct sis_data *nd = hid_get_drvdata(hid);
+ nd->ReportID = raw_data[0];
+
+#ifdef CONFIG_DEBUG_HID_SIS_SENDPOINT
+ printk(KERN_INFO "raw_event : ReportID = %d\n", nd->ReportID);
+#endif //CONFIG_DEBUG_HID_SIS_SENDPOINT
+
+ hid_set_drvdata(hid, nd);
+ return 0;
+}
+
+static void sis_event_lastdata(struct hid_device *hid, struct sis_data *nd, struct input_dev *input)
+{
+ int pkg_n=0;
+
+//817 method : original format
+ if ( (hid->product == USB_PRODUCT_ID_SIS817_TOUCH || hid->product == USB_PRODUCT_ID_SISF817_TOUCH) && nd->ReportID == REPORTID_10)
+ {
+#ifdef CONFIG_DEBUG_HID_SIS_SENDPOINT
+ printk (KERN_INFO "sis_event_lastdata : 817 original format\n");
+#endif //CONFIG_DEBUG_HID_SIS_SENDPOINT
+
+ sis_event_emission(nd, input);
+ sis_event_clear(nd, MAX_POINT);
+ }
+ //817 method : Extend Class Format
+ else if ( (hid->product == USB_PRODUCT_ID_SIS817_TOUCH || hid->product == USB_PRODUCT_ID_SISF817_TOUCH) && nd->ReportID != REPORTID_10)
+ {
+#ifdef CONFIG_DEBUG_HID_SIS_SENDPOINT
+ printk (KERN_INFO "sis_event_lastdata : 817 extend format\n");
+#endif //CONFIG_DEBUG_HID_SIS_SENDPOINT
+
+ if(nd->total >= 6)
+ {
+ idx = 4;
+ pkg_num = nd->total;
+ }
+ else if(nd->total >= 1)
+ {
+ sis_event_emission(nd, input);
+ sis_event_clear(nd, MAX_POINT);
+ }
+ else
+ {
+ if(pkg_num >0)
+ {
+ nd->total = pkg_num;
+ sis_event_emission(nd, input);
+ pkg_n = 0;
+ sis_event_clear(nd, MAX_POINT);
+ }
+ else
+ {
+ sis_event_clear(nd, MAX_POINT);
+ }
+ }
+ }
+ else //816 method
+ {
+#ifdef CONFIG_DEBUG_HID_SIS_SENDPOINT
+ printk (KERN_INFO "sis_event_lastdata : 816 format\n");
+#endif //CONFIG_DEBUG_HID_SIS_SENDPOINT
+
+ if(nd->total >= 3)
+ {
+ idx = 1;
+ pkg_num = nd->total;
+ }
+ else if(nd->total >= 1)
+ {
+ sis_event_emission(nd, input);
+ sis_event_clear(nd, MAX_POINT);
+ }
+ else
+ {
+ if(pkg_num >0)
+ {
+ if((pkg_num%2)>0)
+ pkg_n = pkg_num+1;
+ else
+ pkg_n = pkg_num;
+
+ if(pkg_n == (idx + 1) )
+ {
+ nd->total = pkg_num;
+ sis_event_emission(nd, input);
+ pkg_n = 0;
+ sis_event_clear(nd, MAX_POINT);
+ }
+ }
+ else
+ {
+ sis_event_clear(nd, MAX_POINT);
+ }
+ }
+ }
+}
+/*
+ * this function is called upon all reports
+ * so that we can filter contact point information,
+ * decide whether we are in multi or single touch mode
+ * and call input_mt_sync after each point if necessary
+ */
+static int sis_event (struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ struct sis_data *nd = hid_get_drvdata(hid);
+ //printk (KERN_INFO "sis_event");
+
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ struct input_dev *input = field->hidinput->input;
+#ifdef CONFIG_DEBUG_HID_SIS_SENDPOINT
+ printk (KERN_INFO "sis_event : usage->hid = %x, value = %d\n", usage->hid, value);
+#endif //CONFIG_DEBUG_HID_SIS_SENDPOINT
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ break;
+
+ case HID_DG_TIPSWITCH:
+ idx++;
+ nd->pt[idx].pressure = !!value;
+ break;
+
+ case HID_DG_CONTACTID:
+ nd->pt[idx].id = value;
+ break;
+
+ case HID_GD_X:
+ nd->pt[idx].x = value;
+ break;
+
+ case HID_GD_Y:
+ nd->pt[idx].y = value;
+ break;
+
+ //new usage for SiS817 Extend Class Device
+ case HID_DG_SCANTIME:
+ nd->scantime = value;
+ if ( (nd->ReportID & 0xf0) > REPORTID_TYPE1 )
+ sis_event_lastdata(hid, nd, input);
+ break;
+
+ case HID_DG_WIDTH:
+ nd->pt[idx].width = value;
+ break;
+
+ case HID_DG_HEIGHT:
+ nd->pt[idx].height = value;
+ break;
+
+ case HID_DG_TIPPRESSURE:
+ nd->pt[idx].pressure = value;
+ break;
+ //end of new usage for SiS817 Extend Class Device
+
+ case HID_DG_CONTACTCOUNT:
+ nd->total = value;
+ if ( (nd->ReportID & 0xf0) <= REPORTID_TYPE1 )
+ sis_event_lastdata(hid, nd, input);
+ break;
+ default:
+ //fallback to the generic hidinput handling
+ return 0;
+ }
+ }
+
+ /* we have handled the hidinput part, now remains hiddev */
+ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
+ hid->hiddev_hid_event(hid, field, usage, value);
+
+ return 1;
+}
+
+#ifdef CONFIG_HID_SIS_UPDATE_FW
+int sis_cdev_open(struct inode *inode, struct file *filp) //20120306 Yuger ioctl for tool
+{
+ //20110511, Yuger, kill current urb by method of usbhid_stop
+ struct usbhid_device *usbhid;
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_open\n" );
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if ( !hid_dev_backup )
+ {
+ printk( KERN_INFO "sis_cdev_open : hid_dev_backup is not initialized yet" );
+ return -1;
+ }
+
+ usbhid = hid_dev_backup->driver_data;
+
+ //20110602, Yuger, fix bug: not contact usb cause kernel panic
+ if( !usbhid )
+ {
+ printk( KERN_INFO "sis_cdev_open : usbhid is not initialized yet" );
+ return -1;
+ }
+ else if ( !usbhid->urbin )
+ {
+ printk( KERN_INFO "sis_cdev_open : usbhid->urbin is not initialized yet" );
+ return -1;
+ }
+ else if (hid_dev_backup->vendor == USB_VENDOR_ID_SIS2_TOUCH)
+ {
+ usb_fill_int_urb(backup_urb, usbhid->urbin->dev, usbhid->urbin->pipe,
+ usbhid->urbin->transfer_buffer, usbhid->urbin->transfer_buffer_length,
+ usbhid->urbin->complete, usbhid->urbin->context, usbhid->urbin->interval);
+
+ clear_bit( HID_STARTED, &usbhid->iofl );
+ set_bit( HID_DISCONNECTED, &usbhid->iofl );
+
+ usb_kill_urb( usbhid->urbin );
+ usb_free_urb( usbhid->urbin );
+ usbhid->urbin = NULL;
+ return 0;
+ }
+ else
+ {
+ printk (KERN_INFO "This is not a SiS device");
+ return -801;
+ }
+}
+
+int sis_cdev_release(struct inode *inode, struct file *filp)
+{
+ //20110505, Yuger, rebuild the urb which is at the same urb address, then re-submit it
+ int ret;
+ struct usbhid_device *usbhid;
+ unsigned long flags;
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_release" );
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if ( !hid_dev_backup )
+ {
+ printk( KERN_INFO "sis_cdev_release : hid_dev_backup is not initialized yet" );
+ return -1;
+ }
+
+ usbhid = hid_dev_backup->driver_data;
+
+ printk( KERN_INFO "sys_sis_HID_start" );
+
+ if( !usbhid )
+ {
+ printk( KERN_INFO "sis_cdev_release : usbhid is not initialized yet" );
+ return -1;
+ }
+
+ if( !backup_urb )
+ {
+ printk( KERN_INFO "sis_cdev_release : urb_backup is not initialized yet" );
+ return -1;
+ }
+
+ clear_bit( HID_DISCONNECTED, &usbhid->iofl );
+ usbhid->urbin = usb_alloc_urb( 0, GFP_KERNEL );
+
+ if( !backup_urb->interval )
+ {
+ printk( KERN_INFO "sis_cdev_release : urb_backup->interval does not exist" );
+ return -1;
+ }
+
+ usb_fill_int_urb(usbhid->urbin, backup_urb->dev, backup_urb->pipe,
+ backup_urb->transfer_buffer, backup_urb->transfer_buffer_length,
+ backup_urb->complete, backup_urb->context, backup_urb->interval);
+ usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
+ usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ set_bit( HID_STARTED, &usbhid->iofl );
+
+ //method at hid_start_in
+ spin_lock_irqsave( &usbhid->lock, flags );
+ ret = usb_submit_urb( usbhid->urbin, GFP_ATOMIC );
+ spin_unlock_irqrestore( &usbhid->lock, flags );
+ //yy
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_release : ret = %d", ret );
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ return ret;
+}
+
+//SiS 817 only
+ssize_t sis_cdev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ int actual_length = 0, timeout = 0;
+ u8 *rep_data = NULL;
+ u16 size = 0;
+ long rep_ret;
+ struct usb_interface *intf = to_usb_interface(hid_dev_backup->dev.parent);
+ struct usb_device *dev = interface_to_usbdev(intf);
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_read\n");
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ size = (((u16)(buf[64] & 0xff)) << 24) + (((u16)(buf[65] & 0xff)) << 16) +
+ (((u16)(buf[66] & 0xff)) << 8) + (u16)(buf[67] & 0xff);
+ timeout = (((int)(buf[68] & 0xff)) << 24) + (((int)(buf[69] & 0xff)) << 16) +
+ (((int)(buf[70] & 0xff)) << 8) + (int)(buf[71] & 0xff);
+
+ rep_data = kzalloc(size, GFP_KERNEL);
+ if (!rep_data)
+ return -12;
+
+ if ( copy_from_user( rep_data, (void*)buf, size) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+ return -19;
+ }
+
+ rep_ret = usb_interrupt_msg(dev, backup_urb->pipe,
+ rep_data, size, &actual_length, timeout);
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk(KERN_INFO "sis_cdev_read : rep_data = ");
+ for (i=0; i<8; i++)
+ {
+ printk ("%02X ", rep_data[i]);
+ }
+ printk ("\n");
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if( rep_ret == 0 )
+ {
+ rep_ret = actual_length;
+ if ( copy_to_user( (void*)buf, rep_data, rep_ret ) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+ return -19;
+ }
+ }
+
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_read : rep_ret = %ld\n", rep_ret );
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ return rep_ret;
+}
+
+ssize_t sis_cdev_write( struct file *file, const char __user *buf, size_t count, loff_t *f_pos )
+{
+ int i, actual_length = 0;
+ u8 *tmp_data = NULL; //include report id
+ u8 *rep_data = NULL;
+ long rep_ret;
+ struct usb_interface *intf = to_usb_interface( hid_dev_backup->dev.parent );
+ struct usb_device *dev = interface_to_usbdev( intf );
+ struct usbhid_device *usbhid = hid_dev_backup->driver_data;
+
+ if ( hid_dev_backup->product == USB_PRODUCT_ID_SIS817_TOUCH || hid_dev_backup->product == USB_PRODUCT_ID_SISF817_TOUCH ) //817 method
+ {
+ u16 size = (((u16)(buf[64] & 0xff)) << 24) + (((u16)(buf[65] & 0xff)) << 16) +
+ (((u16)(buf[66] & 0xff)) << 8) + (u16)(buf[67] & 0xff);
+ int timeout = (((int)(buf[68] & 0xff)) << 24) + (((int)(buf[69] & 0xff)) << 16) +
+ (((int)(buf[70] & 0xff)) << 8) + (int)(buf[71] & 0xff);
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_write : 817 method\n");
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ rep_data = kzalloc(size, GFP_KERNEL);
+ if (!rep_data)
+ return -12;
+
+ if ( copy_from_user( rep_data, (void*)buf, size) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+ return -19;
+ }
+
+ rep_ret = usb_interrupt_msg( dev, usbhid->urbout->pipe,
+ rep_data, size, &actual_length, timeout );
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk(KERN_INFO "sis_cdev_write : rep_data = ");
+ for (i=0; i<size-1; i++)
+ {
+ printk ("%02X ", rep_data[i]);
+ }
+ if (i == size-1)
+ printk ("%02X\n", rep_data[i]);
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if( rep_ret == 0 )
+ {
+ rep_ret = actual_length;
+ if ( copy_to_user( (void*)buf, rep_data, rep_ret ) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+ return -19;
+ }
+ }
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_write : rep_ret = %ld\n", rep_ret );
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ }
+ else //816 method
+ {
+
+ u8 request = buf[0];
+ u8 dir = buf[1];
+ u16 value = (((u16)(buf[2] & 0xff)) << 24) + (((u16)(buf[3] & 0xff)) << 16) + (((u16)(buf[4] & 0xff)) << 8) + (u16)(buf[5] & 0xff);
+ u16 index = (((u16)(buf[6] & 0xff)) << 24) + (((u16)(buf[7] & 0xff)) << 16) + (((u16)(buf[8] & 0xff)) << 8) + (u16)(buf[9] & 0xff);
+
+
+ u16 size = (((u16)(buf[29] & 0xff)) << 24) + (((u16)(buf[30] & 0xff)) << 16) + (((u16)(buf[31] & 0xff)) << 8) + (u16)(buf[32] & 0xff);
+ int timeout = (((int)(buf[33] & 0xff)) << 24) + (((int)(buf[34] & 0xff)) << 16) + (((int)(buf[35] & 0xff)) << 8) + (int)(buf[36] & 0xff);
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "sis_cdev_write : 816 method\n");
+ printk (KERN_INFO "dir = %d, value = %d, index = %d, timeout = %d\n", dir, value, index, timeout);
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ rep_data = kzalloc( size , GFP_KERNEL );
+ if ( rep_data == 0 )
+ {
+ return -12;
+ }
+
+ for( i = 0; i < size; i++)
+ {
+ rep_data[i] = buf[10+i];
+ }
+
+ tmp_data = kzalloc( size + 1, GFP_KERNEL ); //include report id, so size +1
+
+ for( i = 0; i < size; i++ )
+ {
+ tmp_data[i+1] = rep_data[i];
+ }
+
+ buf += 10;
+
+ if( dir & DIR_IN )
+ {
+ if ( hid_dev_backup->product == USB_PRODUCT_ID_SIS2_TOUCH || hid_dev_backup->product == USB_PRODUCT_ID_NEW_SIS2_TOUCH )
+ {
+ //20110510, Yuger, for correcting intr data send into interrupt msg(receive, in, endp=2)
+ tmp_data[0] = 0x0A;//in
+
+ rep_ret = usb_interrupt_msg( dev, backup_urb->pipe, tmp_data, size+1, &actual_length, timeout );
+ //yy
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk(KERN_INFO "(INT_IN)rep_ret = %ld, actual_length = %d", rep_ret, actual_length);
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if( rep_ret == 0 )
+ {
+ rep_ret = actual_length;
+ }
+
+ //20110510, Yuger, for recovering rep_data
+ for( i = 0; i < size; i++ )
+ {
+ rep_data[i] = tmp_data[i+1];
+ }
+ //yy
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk(KERN_INFO "(INT_IN)size = %u, dir = %u, rep_ret = %ld, rep_data = %X %X %X", size, dir, rep_ret, rep_data[0], rep_data[1], rep_data[2]);
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if ( copy_to_user( (void*)buf, rep_data, rep_ret ) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ kfree( tmp_data );
+ rep_data = NULL;
+ tmp_data = NULL;
+ return -19;
+ }
+ }
+ else
+ {
+ //control message
+ rep_ret = usb_control_msg( dev, usb_rcvctrlpipe( dev, CTRL ),
+ request, (USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE),
+ value, index, rep_data, size, timeout );
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk ("(CTRL) size = %d, dir = %d, rep_ret = %ld, rep_data = ", size, dir, rep_ret);
+ for (i=0; i<size-1; i++)
+ {
+ printk ("%02X ", rep_data[i]);
+ }
+ if (i == size-1)
+ printk ("%02X\n", rep_data[i]);
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if ( copy_to_user( (void*)buf, rep_data, rep_ret ) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+ return -19;
+ }
+ }
+ }
+ else
+ {
+ if ( hid_dev_backup->product == USB_PRODUCT_ID_SIS2_TOUCH || hid_dev_backup->product == USB_PRODUCT_ID_NEW_SIS2_TOUCH )
+ {
+ //20110510, Yuger, for correcting intr data send into interrupt msg(send, out, endp=1)
+ tmp_data[0] = 0x09;//out
+
+ rep_ret = usb_interrupt_msg( dev, usbhid->urbout->pipe, tmp_data, size + 1, &actual_length, timeout );
+
+ //just return success or not(no need to return actual_length if succeed)
+
+ //20110510, Yuger, for recovering rep_data
+ for( i = 0; i < size; i++ )
+ {
+ rep_data[i] = tmp_data[i+1];
+ }
+ //yy
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk(KERN_INFO "(INT_OUT)size = %u, actual_length = %d, rep_ret = %ld, rep_data = %x %x %x", size, actual_length, rep_ret, rep_data[0], rep_data[1], rep_data[2]);
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if ( copy_to_user( (void*)buf, rep_data, actual_length-1 ) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ kfree( tmp_data );
+ rep_data = NULL;
+ tmp_data = NULL;
+ return -19;
+ }
+ }
+ else
+ {
+ //control message
+ rep_ret = usb_control_msg( dev, usb_sndctrlpipe( dev, CTRL ),
+ request, (USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE),
+ value, index, rep_data, 16, timeout );
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk ("(CTRL) size = %d, dir = %d, rep_ret = %ld, rep_data = ", size, dir, rep_ret);
+ for (i=0; i<size-1; i++)
+ {
+ printk ("%02X ", rep_data[i]);
+ }
+ if (i == size-1)
+ printk ("%02X\n", rep_data[i]);
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ if ( copy_to_user( (void*)buf, rep_data, rep_ret ) )
+ {
+ printk( KERN_INFO "copy_to_user fail\n" );
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+ return -19;
+ }
+ }
+ }
+ //free allocated data
+ kfree( tmp_data );
+ tmp_data = NULL;
+ }
+ //free allocated data
+ kfree( rep_data );
+ rep_data = NULL;
+
+#ifdef CONFIG_DEBUG_HID_SIS_UPDATE_FW
+ printk( KERN_INFO "End of sis_cdev_write\n" );
+#endif //CONFIG_DEBUG_HID_SIS_UPDATE_FW
+
+ return rep_ret;
+}
+
+//~TT
+
+//for ioctl
+static const struct file_operations sis_cdev_fops = {
+ .owner = THIS_MODULE,
+ .read = sis_cdev_read,
+ .write = sis_cdev_write,
+ .open = sis_cdev_open,
+ .release= sis_cdev_release,
+};
+
+//for ioctl
+static int sis_setup_chardev(struct hid_device *hdev, struct sis_data *nd)
+{
+
+ dev_t dev = MKDEV(sis_char_major, 0);
+ int alloc_ret = 0;
+ int cdev_err = 0;
+ int input_err = 0;
+ struct device *class_dev = NULL;
+ void *ptr_err;
+
+ printk("sis_setup_chardev.\n");
+
+ if (nd == NULL)
+ {
+ input_err = -ENOMEM;
+ goto error;
+ }
+
+ // dynamic allocate driver handle
+ if (hdev->product == USB_PRODUCT_ID_SIS9200_TOUCH)
+ alloc_ret = alloc_chrdev_region(&dev, 0, sis_char_devs_count, BRIDGE_DEVICE_NAME);
+ else if (hdev->product == USB_PRODUCT_ID_SIS817_TOUCH)
+ alloc_ret = alloc_chrdev_region(&dev, 0, sis_char_devs_count, SIS817_DEVICE_NAME);
+ else if (hdev->product == USB_PRODUCT_ID_SISF817_TOUCH)
+ alloc_ret = alloc_chrdev_region(&dev, 0, sis_char_devs_count, SISF817_DEVICE_NAME);
+ else
+ alloc_ret = alloc_chrdev_region(&dev, 0, sis_char_devs_count, INTERNAL_DEVICE_NAME);
+
+ if (alloc_ret)
+ goto error;
+
+ sis_char_major = MAJOR(dev);
+ cdev_init(&sis_char_cdev, &sis_cdev_fops);
+ sis_char_cdev.owner = THIS_MODULE;
+ cdev_err = cdev_add(&sis_char_cdev, MKDEV(sis_char_major, 0), sis_char_devs_count);
+ if (cdev_err)
+ goto error;
+
+ if (hdev->product == USB_PRODUCT_ID_SIS9200_TOUCH)
+ printk(KERN_INFO "%s driver(major %d) installed.\n", BRIDGE_DEVICE_NAME, sis_char_major);
+ else if (hdev->product == USB_PRODUCT_ID_SIS817_TOUCH)
+ printk(KERN_INFO "%s driver(major %d) installed.\n", SIS817_DEVICE_NAME, sis_char_major);
+ else if (hdev->product == USB_PRODUCT_ID_SISF817_TOUCH)
+ printk(KERN_INFO "%s driver(major %d) installed.\n", SISF817_DEVICE_NAME, sis_char_major);
+ else
+ printk(KERN_INFO "%s driver(major %d) installed.\n", INTERNAL_DEVICE_NAME, sis_char_major);
+
+ // register class
+ if (hdev->product == USB_PRODUCT_ID_SIS9200_TOUCH)
+ sis_char_class = class_create(THIS_MODULE, BRIDGE_DEVICE_NAME);
+ else if (hdev->product == USB_PRODUCT_ID_SIS817_TOUCH)
+ sis_char_class = class_create(THIS_MODULE, SIS817_DEVICE_NAME);
+ else if (hdev->product == USB_PRODUCT_ID_SISF817_TOUCH)
+ sis_char_class = class_create(THIS_MODULE, SISF817_DEVICE_NAME);
+ else
+ sis_char_class = class_create(THIS_MODULE, INTERNAL_DEVICE_NAME);
+
+ if(IS_ERR(ptr_err = sis_char_class))
+ {
+ goto err2;
+ }
+
+ if (hdev->product == USB_PRODUCT_ID_SIS9200_TOUCH)
+ class_dev = device_create(sis_char_class, NULL, MKDEV(sis_char_major, 0), NULL, BRIDGE_DEVICE_NAME);
+ else if (hdev->product == USB_PRODUCT_ID_SIS817_TOUCH)
+ class_dev = device_create(sis_char_class, NULL, MKDEV(sis_char_major, 0), NULL, SIS817_DEVICE_NAME);
+ else if (hdev->product == USB_PRODUCT_ID_SISF817_TOUCH)
+ class_dev = device_create(sis_char_class, NULL, MKDEV(sis_char_major, 0), NULL, SISF817_DEVICE_NAME);
+ else
+ class_dev = device_create(sis_char_class, NULL, MKDEV(sis_char_major, 0), NULL, INTERNAL_DEVICE_NAME);
+
+ if(IS_ERR(ptr_err = class_dev))
+ {
+ goto err;
+ }
+
+ return 0;
+error:
+ if (cdev_err == 0)
+ cdev_del(&sis_char_cdev);
+ if (alloc_ret == 0)
+ unregister_chrdev_region(MKDEV(sis_char_major, 0), sis_char_devs_count);
+ if(input_err != 0)
+ {
+ printk("sis_ts_bak error!\n");
+ }
+err:
+ device_destroy(sis_char_class, MKDEV(sis_char_major, 0));
+err2:
+ class_destroy(sis_char_class);
+ return -1;
+}
+#endif //CONFIG_HID_SIS_UPDATE_FW
+
+static int sis_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct sis_data *nd;
+ u8 *rep_data = NULL;
+
+#ifdef CONFIG_HID_SIS_UPDATE_FW
+ hid_dev_backup = hdev;
+#endif //CONFIG_HID_SIS_UPDATE_FW
+
+ printk(KERN_INFO "sis_probe\n");
+
+#ifdef CONFIG_HID_SIS_UPDATE_FW
+ backup_urb = usb_alloc_urb(0, GFP_KERNEL); //0721test
+ if (!backup_urb) {
+ dev_err(&hdev->dev, "cannot allocate backup_urb\n");
+ return -ENOMEM;
+ }
+#endif //CONFIG_HID_SIS_UPDATE_FW
+
+// command Set_Feature for changing device from mouse to touch device
+ rep_data = kmalloc(3,GFP_KERNEL); //return value will be 0xabcd
+ if (hdev->product == USB_PRODUCT_ID_SIS9200_TOUCH)
+ {
+ if(!rep_data)
+ return -ENOMEM;
+ rep_data[0] = 0x07;
+ rep_data[1] = 0x02;
+ rep_data[2] = 0xA9;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_CONFIGURATION, (USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE), 0x0307, 0, rep_data, 3, 1000);
+ }
+
+// allocate memory for sis_data struct
+ nd = kzalloc(sizeof(struct sis_data), GFP_KERNEL);
+ if (!nd) {
+ dev_err(&hdev->dev, "cannot allocate SiS 9200 data\n");
+ kfree(rep_data);
+ rep_data = NULL;
+ return -ENOMEM;
+ }
+
+ hid_set_drvdata(hdev, nd);
+
+#ifdef CONFIG_HID_SIS_UPDATE_FW
+ //for ioctl
+ ret = sis_setup_chardev(hdev, nd);
+ if(ret)
+ {
+ printk( KERN_INFO "sis_setup_chardev fail\n");
+ }
+#endif //CONFIG_HID_SIS_UPDATE_FW
+
+ //set noget for not init reports (speed improvement)
+ hdev->quirks |= HID_QUIRK_NOGET;
+ hdev->quirks &= ~HID_QUIRK_MULTITOUCH; //only hid-multitouch cat use this flag!
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ dev_err(&hdev->dev, "parse failed\n");
+ goto err_free;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret) {
+ dev_err(&hdev->dev, "hw start failed\n");
+ goto err_free;
+ }
+
+err_free:
+ kfree(rep_data);
+ rep_data = NULL;
+ return ret;
+}
+
+static void sis_remove(struct hid_device *hdev)
+{
+#ifdef CONFIG_HID_SIS_UPDATE_FW
+ //for ioctl
+ dev_t dev = MKDEV(sis_char_major, 0);
+
+ printk(KERN_INFO "sis_remove\n");
+
+ cdev_del(&sis_char_cdev);
+ unregister_chrdev_region(dev, sis_char_devs_count);
+ device_destroy(sis_char_class, MKDEV(sis_char_major, 0));
+ class_destroy(sis_char_class);
+#else //CONFIG_HID_SIS_UPDATE_FW
+
+ printk(KERN_INFO "sis_remove\n");
+
+#endif //CONFIG_HID_SIS_UPDATE_FW
+
+#ifdef CONFIG_HID_SIS_UPDATE_FW
+ usb_kill_urb( backup_urb );
+ usb_free_urb( backup_urb );
+ backup_urb = NULL;
+#endif
+ hid_hw_stop(hdev);
+ kfree(hid_get_drvdata(hdev));
+ hid_set_drvdata(hdev, NULL);
+}
+
+static const struct hid_device_id sis_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_PRODUCT_ID_SIS2_TOUCH) }, //0x0457, 0x0151
+ { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_PRODUCT_ID_SIS_TOUCH) }, //0x0457, 0x0810
+ { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_PRODUCT_ID_NEW_SIS2_TOUCH) }, //0x0457, 0x0816
+ { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_PRODUCT_ID_SIS9200_TOUCH) }, //0x0457, 0x9200
+ { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_PRODUCT_ID_SIS817_TOUCH) }, //0x0457, 0x0817
+ { HID_USB_DEVICE(USB_VENDOR_ID_SIS2_TOUCH, USB_PRODUCT_ID_SISF817_TOUCH) }, //0x0457, 0xF817
+ { }
+};
+MODULE_DEVICE_TABLE(hid, sis_devices);
+
+
+
+static struct hid_driver sis_driver = {
+ .name = "sis",
+ .id_table = sis_devices,
+ .probe = sis_probe,
+ .remove = sis_remove,
+ .raw_event = sis_raw_event,
+ .input_mapped = sis_input_mapped,
+ .input_mapping = sis_input_mapping,
+ .event = sis_event,
+};
+
+static int __init sis_init(void)
+{
+ printk(KERN_INFO "sis_init\n");
+ return hid_register_driver(&sis_driver);
+}
+
+static void __exit sis_exit(void)
+{
+ printk(KERN_INFO "sis_exit\n");
+ hid_unregister_driver(&sis_driver);
+}
+
+module_init(sis_init);
+module_exit(sis_exit);
+MODULE_DESCRIPTION("SiS 817 Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/sitronix/Kconfig b/drivers/input/touchscreen/sitronix/Kconfig
new file mode 100755
index 00000000..eb37231f
--- /dev/null
+++ b/drivers/input/touchscreen/sitronix/Kconfig
@@ -0,0 +1,11 @@
+config TOUCHSCREEN_SITRONIX
+ tristate "Sitronix ST1xx series Capacity Touchscreen Device Support"
+ default m
+ depends on ARCH_WMT
+ ---help---
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+ If unsure, say N.
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_sitronix.ko.
+
diff --git a/drivers/input/touchscreen/sitronix/Makefile b/drivers/input/touchscreen/sitronix/Makefile
new file mode 100755
index 00000000..b38deb74
--- /dev/null
+++ b/drivers/input/touchscreen/sitronix/Makefile
@@ -0,0 +1,34 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_sitronix
+
+#obj-$(CONFIG_TOUCHSCREEN_SITRONIX) := $(MY_MODULE_NAME).o
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := sitronix_i2c.o irq_gpio.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ @rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ @rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
+
diff --git a/drivers/input/touchscreen/sitronix/irq_gpio.c b/drivers/input/touchscreen/sitronix/irq_gpio.c
new file mode 100755
index 00000000..bf57ff92
--- /dev/null
+++ b/drivers/input/touchscreen/sitronix/irq_gpio.c
@@ -0,0 +1,148 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include "irq_gpio.h"
+
+int wmt_enable_gpirq(int num)
+{
+ if(num > 15)
+ return -1;
+
+ if(num < 4)
+ REG32_VAL(__GPIO_BASE+0x0300) |= 1<<(num*8+7); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) |= 1<<((num-4)*8+7); //enable interrupt
+ else if(num >= 8 && num < 12)
+ REG32_VAL(__GPIO_BASE+0x0308) |= 1<<((num-8)*8+7); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x030C) |= 1<<((num-12)*8+7); //enable interrupt
+
+ return 0;
+}
+
+int wmt_disable_gpirq(int num)
+{
+ if(num > 15)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) &= ~(1<<(num*8+7)); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) &= ~(1<<((num-4)*8+7)); //enable interrupt
+ else if(num >= 8 && num < 12)
+ REG32_VAL(__GPIO_BASE+0x0308) &= ~(1<<((num-8)*8+7)); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x030C) &= ~(1<<((num-12)*8+7)); //enable interrupt
+
+ return 0;
+}
+
+int wmt_is_tsirq_enable(int num)
+{
+ int val = 0;
+
+ if(num > 15)
+ return 0;
+
+ if(num<4)
+ val = REG32_VAL(__GPIO_BASE+0x0300) & (1<<(num*8+7));
+ else if(num >= 4 && num < 8)
+ val = REG32_VAL(__GPIO_BASE+0x0304) & (1<<((num-4)*8+7));
+ else if(num >= 8 && num < 12)
+ val = REG32_VAL(__GPIO_BASE+0x0308) & (1<<((num-8)*8+7));
+ else
+ val = REG32_VAL(__GPIO_BASE+0x030C) & (1<<((num-12)*8+7));
+
+ return val?1:0;
+
+}
+
+int wmt_is_tsint(int num)
+{
+ if (num > 15)
+ {
+ return 0;
+ }
+ return (REG32_VAL(__GPIO_BASE+0x0360) & (1<<num)) ? 1: 0;
+}
+
+void wmt_clr_int(int num)
+{
+ if (num > 15)
+ {
+ return;
+ }
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num;
+}
+
+int wmt_set_gpirq(int num, int type)
+{
+ int shift;
+ int offset;
+ unsigned long reg;
+
+ if(num >15)
+ return -1;
+ //if (num > 9)
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; // gpio10,11 as gpio
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num);//|=(1<<num);// //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull down
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down
+
+ //set gpio irq triger type
+ if(num < 4){//[0,3]
+ shift = num;
+ offset = 0x0300;
+ }else if(num >= 4 && num < 8){//[4,7]
+ shift = num-4;
+ offset = 0x0304;
+ }else if(num >= 8 && num < 12){//[8,11]
+ shift = num-8;
+ offset = 0x0308;
+ }else{// [12,15]
+ shift = num-12;
+ offset = 0x030C;
+ }
+
+ reg = REG32_VAL(__GPIO_BASE + offset);
+
+ switch(type){
+ case IRQ_TYPE_LEVEL_LOW:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ default://both edge
+ reg |= (1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+
+ }
+ //reg |= 1<<(shift*8+7);//enable interrupt
+ reg &= ~(1<<(shift*8+7)); //disable int
+
+ REG32_VAL(__GPIO_BASE + offset) = reg;
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num; //clear interrupt status
+ msleep(5);
+ return 0;
+}
+
+
diff --git a/drivers/input/touchscreen/sitronix/irq_gpio.h b/drivers/input/touchscreen/sitronix/irq_gpio.h
new file mode 100755
index 00000000..0232bd04
--- /dev/null
+++ b/drivers/input/touchscreen/sitronix/irq_gpio.h
@@ -0,0 +1,13 @@
+#ifndef _LINUX_IRQ_GPIO_H
+#define _LINUX_IRQ_GPIO_H
+
+
+extern int wmt_enable_gpirq(int num);
+extern int wmt_disable_gpirq(int num);
+extern int wmt_is_tsirq_enable(int num);
+extern int wmt_is_tsint(int num);
+extern void wmt_clr_int(int num);
+extern int wmt_set_gpirq(int num, int type);
+
+
+#endif
diff --git a/drivers/input/touchscreen/sitronix/sitronix_i2c.c b/drivers/input/touchscreen/sitronix/sitronix_i2c.c
new file mode 100755
index 00000000..7b79aaf4
--- /dev/null
+++ b/drivers/input/touchscreen/sitronix/sitronix_i2c.c
@@ -0,0 +1,817 @@
+/* drivers/input/touchscreen/sis_i2c.c
+ *
+ * Copyright (C) 2009 SiS, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/linkage.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <mach/hardware.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include "sitronix_i2c.h"
+#include "irq_gpio.h"
+
+struct sitronix_data *pContext=NULL;
+struct i2c_client *l_client=NULL;
+
+#ifdef TOUCH_KEY
+
+#define MENU_IDX 0
+#define HOME_IDX 1
+#define BACK_IDX 2
+#define SEARCH_IDX 3
+#define NUM_KEYS 4
+
+static int virtual_keys[NUM_KEYS] ={
+ KEY_BACK,
+ KEY_HOME,
+ KEY_MENU,
+ KEY_SEARCH
+};
+#endif
+
+#define I2C_BUS1 1
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void sitronix_early_suspend(struct early_suspend *h);
+static void sitronix_late_resume(struct early_suspend *h);
+#endif
+
+static int sitronix_read(struct sitronix_data *sitronix, u8 *rxdata, int length)
+{
+ int ret;
+ struct i2c_msg msg[2];
+
+ msg[0].addr = sitronix->addr;
+ msg[0].flags = 0 | I2C_M_NOSTART;
+ msg[0].len = 1;
+ msg[0].buf = rxdata;
+
+ msg[1].addr = sitronix->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = length;
+ msg[1].buf = rxdata;
+
+ //ret = wmt_i2c_xfer_continue_if_4(msg, 2, I2C_BUS1);
+ ret = i2c_transfer(l_client->adapter, msg, 2);
+ if (ret <= 0)
+ dbg_err("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+#ifdef SITRONIX_DEBUG
+static int sitronix_write(struct sitronix_data *sitronix, u8 *txdata, int length)
+{
+ int ret;
+ struct i2c_msg msg[1];
+
+ msg[0].addr = sitronix->addr;
+ msg[0].flags = 0;
+ msg[0].len = length;
+ msg[0].buf = txdata;
+
+ //ret = wmt_i2c_xfer_continue_if_4(msg, 1, I2C_BUS1);
+ ret = i2c_transfer(l_client->adapter, msg, 1);
+ if (ret <= 0)
+ dbg_err("msg i2c read error: %d\n", ret);
+
+ return ret;
+}
+
+static int sitronix_get_fw_revision(struct sitronix_data *sitronix)
+{
+ int ret = 0;
+ uint8_t buffer[4]={FIRMWARE_REVISION_3,0};
+
+ ret = sitronix_read(sitronix, buffer, 4);
+ if (ret < 0){
+ dbg_err("read fw revision error (%d)\n", ret);
+ return ret;
+ }
+
+ memcpy(sitronix->fw_revision, buffer, 4);
+ printk("Fw Revision (hex): %x%x%x%x\n", buffer[0], buffer[1], buffer[2], buffer[3]);
+
+ return 0;
+}
+
+static int sitronix_get_max_touches(struct sitronix_data *sitronix)
+{
+ int ret = 0;
+ uint8_t buffer[1]={MAX_NUM_TOUCHES};
+
+ ret = sitronix_read(sitronix, buffer, 1);
+ if (ret < 0){
+ dbg_err("read max touches error (%d)\n", ret);
+ return ret;
+ }
+
+ sitronix->max_touches = buffer[0];
+ printk("max touches = %d \n",sitronix->max_touches);
+
+ return 0;
+}
+
+static int sitronix_get_protocol(struct sitronix_data *sitronix)
+{
+ int ret = 0;
+ uint8_t buffer[1]={I2C_PROTOCOL};
+
+ ret = sitronix_read(sitronix, buffer, 1);
+ if (ret < 0){
+ dbg_err("read i2c protocol error (%d)\n", ret);
+ return ret;
+ }
+
+ sitronix->touch_protocol_type = buffer[0] & I2C_PROTOCOL_BMSK;
+ sitronix->sensing_mode = (buffer[0] & (ONE_D_SENSING_CONTROL_BMSK << ONE_D_SENSING_CONTROL_SHFT)) >> ONE_D_SENSING_CONTROL_SHFT;
+ printk("i2c protocol = %d ,sensing mode = %d \n", sitronix->touch_protocol_type, sitronix->sensing_mode);
+
+ return 0;
+}
+
+static int sitronix_get_resolution(struct sitronix_data *sitronix)
+{
+ int ret = 0;
+ uint8_t buffer[3]={XY_RESOLUTION_HIGH};
+
+ ret = sitronix_read(sitronix, buffer, 3);
+ if (ret < 0){
+ dbg_err("read resolution error (%d)\n", ret);
+ return ret;
+ }
+
+ sitronix->resolution_x = ((buffer[0] & (X_RES_H_BMSK << X_RES_H_SHFT)) << 4) | buffer[1];
+ sitronix->resolution_y = ((buffer[0] & Y_RES_H_BMSK) << 8) | buffer[2];
+ printk("Resolution: %d x %d\n", sitronix->resolution_x, sitronix->resolution_y);
+
+ return 0;
+}
+
+static int sitronix_get_chip_id(struct sitronix_data *sitronix)
+{
+ int ret = 0;
+ uint8_t buffer[3]={CHIP_ID};
+
+ ret = sitronix_read(sitronix, buffer, 3);
+ if (ret < 0){
+ dbg_err("read Chip ID error (%d)\n", ret);
+ return ret;
+ }
+
+ if(buffer[0] == 0){
+ if(buffer[1] + buffer[2] > 32)
+ sitronix->chip_id = 2;
+ else
+ sitronix->chip_id = 0;
+ }else
+ sitronix->chip_id = buffer[0];
+
+ sitronix->Num_X = buffer[1];
+ sitronix->Num_Y = buffer[2];
+ printk("Chip ID = %d, Num_X = %d, Num_Y = %d\n", sitronix->chip_id, sitronix->Num_X, sitronix->Num_Y);
+
+ return 0;
+}
+
+static int sitronix_get_device_status(struct sitronix_data *sitronix)
+{
+ int ret = 0;
+ uint8_t buf[3]={FIRMWARE_VERSION,0};
+
+ ret = sitronix_read(sitronix, buf, 3);
+ if (ret < 0){
+ dbg_err("read resolution error (%d)\n", ret);
+ return ret;
+ }
+
+ printk("Firmware version:%02x, Status Reg:%02x,Ctrl Reg:%02x\n", buf[0], buf[1],buf[2]);
+ return 0;
+
+}
+
+static int sitronix_get_device_info(struct sitronix_data *sitronix)
+{
+ int ret = 0;
+
+ ret = sitronix_get_resolution(sitronix);
+ if(ret < 0) return ret;
+
+ ret = sitronix_get_chip_id(sitronix);
+ if(ret < 0) return ret;
+
+ ret = sitronix_get_fw_revision(sitronix);
+ if(ret < 0) return ret;
+
+ ret = sitronix_get_protocol(sitronix);
+ if(ret < 0) return ret;
+
+ ret = sitronix_get_max_touches(sitronix);
+ if(ret < 0) return ret;
+
+ ret = sitronix_get_device_status(sitronix);
+ if(ret < 0) return ret;
+
+ if((sitronix->fw_revision[0] == 0) && (sitronix->fw_revision[1] == 0)){
+ if(sitronix->touch_protocol_type == SITRONIX_RESERVED_TYPE_0){
+ sitronix->touch_protocol_type = SITRONIX_B_TYPE;
+ printk("i2c protocol (revised) = %d \n", sitronix->touch_protocol_type);
+ }
+ }
+
+ if(sitronix->touch_protocol_type == SITRONIX_A_TYPE)
+ sitronix->pixel_length = PIXEL_DATA_LENGTH_A;
+ else if(sitronix->touch_protocol_type == SITRONIX_B_TYPE){
+ sitronix->pixel_length = PIXEL_DATA_LENGTH_B;
+ sitronix->max_touches = 2;
+ printk("max touches (revised) = %d \n", sitronix->max_touches);
+ }
+
+ return 0;
+}
+#endif
+
+static void sitronix_read_work(struct work_struct *work)
+{
+ struct sitronix_data *sitronix= container_of(work, struct sitronix_data, read_work);
+ int ret = -1;
+ u8 buf[22] = {FINGERS,0};
+ u8 i = 0, fingers = 0, tskey = 0;
+ u16 px = 0, py = 0;
+ u16 x = 0, y = 0;
+
+ ret = sitronix_read(sitronix, buf,sizeof(buf));
+ if(ret <= 0){
+ dbg_err("get raw data failed!\n");
+ goto err_exit;
+ }
+
+ fingers = buf[0]&0x0f;
+ if( fingers ){
+ /* Report co-ordinates to the multi-touch stack */
+ for(i=0; i < fingers; i++){
+ if(sitronix->swap){
+ y = ((buf[i*4+2]<<4)&0x0700)|buf[i*4+3];
+ x = ((buf[i*4+2]<<8)&0x0700)|buf[i*4+4];
+ }else{
+ x = ((buf[i*4+2]<<4)&0x0700)|buf[i*4+3];
+ y = ((buf[i*4+2]<<8)&0x0700)|buf[i*4+4];
+ }
+
+ if(!(buf[i*4+2]&0x80)) continue; /*check valid bit */
+
+
+ if(x > sitronix->xresl ) x = sitronix->xresl ;
+ if(y > sitronix->yresl ) y = sitronix->yresl ;
+
+ px = x;
+ py = y;
+ if(sitronix->xch) px = sitronix->xresl - x;
+ if(sitronix->ych) py = sitronix->yresl - y;
+
+ if (sitronix->lcd_exchg) {
+ int tmp;
+ tmp = px;
+ px = py;
+ py = sitronix->xresl - tmp;
+ }
+
+ input_report_abs(sitronix->input_dev, ABS_MT_POSITION_X, px);
+ input_report_abs(sitronix->input_dev, ABS_MT_POSITION_Y, py);
+ input_report_abs(sitronix->input_dev, ABS_MT_TRACKING_ID, i+1);
+ input_mt_sync(sitronix->input_dev);
+ sitronix->penup = 0;
+ if(sitronix->dbg) printk("F%d,raw data: x=%-4d, y=%-4d; report data: px=%-4d, py=%-4d\n", i, x, y, px, py);
+ }
+ input_sync(sitronix->input_dev);
+
+ }
+ else if(!sitronix->penup){
+ dbg("pen up.\n");
+ sitronix->penup = 1;
+ input_mt_sync(sitronix->input_dev);
+ input_sync(sitronix->input_dev);
+ }
+
+ /* virtual keys */
+ tskey = buf[1];
+ if(tskey){
+ if(!sitronix->tkey_idx){
+ sitronix->tkey_idx = tskey;
+ input_report_key(sitronix->input_dev,virtual_keys[sitronix->tkey_idx>>1] , 1);
+ input_sync(sitronix->input_dev);
+ dbg("virtual key down, idx=%d\n",sitronix->tkey_idx);
+ }
+ }else{
+ if(sitronix->tkey_idx){
+ dbg("virtual key up , idx=%d\n",sitronix->tkey_idx);
+ input_report_key(sitronix->input_dev,virtual_keys[sitronix->tkey_idx>>1] , 0);
+ input_sync(sitronix->input_dev);
+ sitronix->tkey_idx = tskey;
+ }
+ }
+
+err_exit:
+ wmt_enable_gpirq(sitronix->irqgpio);
+ return;
+}
+
+
+static irqreturn_t sitronix_isr_handler(int irq, void *dev)
+{
+ struct sitronix_data *sitronix = dev;
+
+ if (wmt_is_tsint(sitronix->irqgpio))
+ {
+ wmt_clr_int(sitronix->irqgpio);
+ if (wmt_is_tsirq_enable(sitronix->irqgpio))
+ {
+ wmt_disable_gpirq(sitronix->irqgpio);
+ if(!sitronix->earlysus) queue_work(sitronix->workqueue, &sitronix->read_work);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static void sitronix_reset(struct sitronix_data *sitronix)
+{
+
+ gpio_set_value(sitronix->rstgpio, 1);
+ mdelay(5);
+ gpio_set_value(sitronix->rstgpio, 0);
+ mdelay(5);
+ gpio_set_value(sitronix->rstgpio, 1);
+ mdelay(5);
+
+ return;
+}
+
+static int sitronix_auto_clb(struct sitronix_data *sitronix)
+{
+ return 1;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void sitronix_early_suspend(struct early_suspend *handler)
+{
+ struct sitronix_data *sitronix = container_of(handler, struct sitronix_data, early_suspend);
+ sitronix->earlysus = 1;
+ wmt_disable_gpirq(sitronix->irqgpio);
+ return;
+}
+
+static void sitronix_late_resume(struct early_suspend *handler)
+{
+ struct sitronix_data *sitronix = container_of(handler, struct sitronix_data, early_suspend);
+
+ sitronix->earlysus = 0;
+ sitronix_reset(sitronix);
+ msleep(200);
+
+ wmt_set_gpirq(sitronix->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_enable_gpirq(sitronix->irqgpio);
+
+ return;
+}
+#endif //CONFIG_HAS_EARLYSUSPEND
+
+
+static int sitronix_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct sitronix_data *sitronix = platform_get_drvdata(pdev);
+ sitronix->earlysus = 1;
+ wmt_disable_gpirq(sitronix->irqgpio);
+ return 0;
+}
+
+static int sitronix_resume(struct platform_device *pdev)
+{
+ struct sitronix_data *sitronix = platform_get_drvdata(pdev);
+
+ sitronix->earlysus = 0;
+ sitronix_reset(sitronix);
+ msleep(200);
+
+ wmt_set_gpirq(sitronix->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_enable_gpirq(sitronix->irqgpio);
+ return 0;
+}
+
+static ssize_t cat_dbg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "dbg \n");
+}
+
+static ssize_t echo_dbg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sitronix_data *sitronix = pContext;
+
+ sscanf(buf,"%d",&sitronix->dbg);
+
+ return count;
+}
+static DEVICE_ATTR(dbg, S_IRUGO | S_IWUSR, cat_dbg, echo_dbg);
+
+static ssize_t cat_clb(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "calibrate --echo 1 >clb \n");
+}
+
+static ssize_t echo_clb(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int cal ;
+ struct sitronix_data *sitronix = pContext;
+
+ sscanf(buf, "%d", &cal);
+ if(cal){
+ if(sitronix_auto_clb(sitronix) <= 0) printk("Auto calibrate failed.\n");
+ }
+
+ return count;
+}
+static DEVICE_ATTR(clb, S_IRUGO | S_IWUSR, cat_clb, echo_clb);
+
+static struct attribute *sitronix_attributes[] = {
+ &dev_attr_clb.attr,
+ &dev_attr_dbg.attr,
+ NULL
+};
+
+static const struct attribute_group sitronix_group = {
+ .attrs = sitronix_attributes,
+};
+
+static int sitronix_sysfs_create_group(struct sitronix_data *sitronix, const struct attribute_group *group)
+{
+ int err;
+
+ sitronix->kobj = kobject_create_and_add("wmtts", NULL) ;
+ if(!sitronix->kobj){
+ dbg_err("kobj create failed.\n");
+ return -ENOMEM;
+ }
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(sitronix->kobj, group);
+ if (err < 0){
+ kobject_del(sitronix->kobj);
+ dbg_err("Create sysfs group failed!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void sitronix_sysfs_remove_group(struct sitronix_data *sitronix, const struct attribute_group *group)
+{
+ sysfs_remove_group(sitronix->kobj, group);
+ kobject_del(sitronix->kobj);
+ return;
+}
+
+static int sitronix_probe(struct platform_device *pdev)
+{
+ int i;
+ int err = 0;
+ struct sitronix_data *sitronix = platform_get_drvdata(pdev);
+
+ INIT_WORK(&sitronix->read_work, sitronix_read_work);
+ sitronix->workqueue = create_singlethread_workqueue(sitronix->name);
+ if (!sitronix->workqueue) {
+ err = -ESRCH;
+ goto exit_create_singlethread;
+ }
+
+ err = sitronix_sysfs_create_group(sitronix, &sitronix_group);
+ if(err < 0){
+ dbg("create sysfs group failed.\n");
+ goto exit_create_group;
+ }
+
+ sitronix->input_dev = input_allocate_device();
+ if (!sitronix->input_dev) {
+ err = -ENOMEM;
+ dbg("failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ sitronix->input_dev->name = sitronix->name;
+ sitronix->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(INPUT_PROP_DIRECT, sitronix->input_dev->propbit);
+
+ if (sitronix->lcd_exchg) {
+ input_set_abs_params(sitronix->input_dev,
+ ABS_MT_POSITION_X, 0, sitronix->yresl, 0, 0);
+ input_set_abs_params(sitronix->input_dev,
+ ABS_MT_POSITION_Y, 0, sitronix->xresl, 0, 0);
+ } else {
+ input_set_abs_params(sitronix->input_dev,
+ ABS_MT_POSITION_X, 0, sitronix->xresl, 0, 0);
+ input_set_abs_params(sitronix->input_dev,
+ ABS_MT_POSITION_Y, 0, sitronix->yresl, 0, 0);
+ }
+ input_set_abs_params(sitronix->input_dev,
+ ABS_MT_TRACKING_ID, 0, 20, 0, 0);
+#ifdef TOUCH_KEY
+ for (i = 0; i <NUM_KEYS; i++)
+ set_bit(virtual_keys[i], sitronix->input_dev->keybit);
+
+ sitronix->input_dev->keycode = virtual_keys;
+ sitronix->input_dev->keycodesize = sizeof(unsigned int);
+ sitronix->input_dev->keycodemax = NUM_KEYS;
+#endif
+
+ err = input_register_device(sitronix->input_dev);
+ if (err) {
+ dbg_err("sitronix_ts_probe: failed to register input device.\n");
+ goto exit_input_register_device_failed;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ sitronix->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ sitronix->early_suspend.suspend = sitronix_early_suspend;
+ sitronix->early_suspend.resume = sitronix_late_resume;
+ register_early_suspend(&sitronix->early_suspend);
+#endif
+
+ if(request_irq(sitronix->irq, sitronix_isr_handler, IRQF_SHARED, sitronix->name, sitronix) < 0){
+ dbg_err("Could not allocate irq for ts_sitronix !\n");
+ err = -1;
+ goto exit_register_irq;
+ }
+
+ wmt_set_gpirq(sitronix->irqgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_enable_gpirq(sitronix->irqgpio);
+ sitronix_reset(sitronix);
+ msleep(200);
+#ifdef SITRONIX_DEBUG
+ sitronix_get_device_info(sitronix);
+#endif
+
+ return 0;
+
+exit_register_irq:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&sitronix->early_suspend);
+#endif
+exit_input_register_device_failed:
+ input_free_device(sitronix->input_dev);
+exit_input_dev_alloc_failed:
+ sitronix_sysfs_remove_group(sitronix, &sitronix_group);
+exit_create_group:
+ cancel_work_sync(&sitronix->read_work);
+ destroy_workqueue(sitronix->workqueue);
+exit_create_singlethread:
+ //kfree(sitronix);
+ return err;
+}
+
+static int sitronix_remove(struct platform_device *pdev)
+{
+ struct sitronix_data *sitronix = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&sitronix->read_work);
+ flush_workqueue(sitronix->workqueue);
+ destroy_workqueue(sitronix->workqueue);
+
+ free_irq(sitronix->irq, sitronix);
+ wmt_disable_gpirq(sitronix->irqgpio);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&sitronix->early_suspend);
+#endif
+ input_unregister_device(sitronix->input_dev);
+
+ sitronix_sysfs_remove_group(sitronix, &sitronix_group);
+ //kfree(sitronix);
+
+ dbg("remove...\n");
+ return 0;
+}
+
+static void sitronix_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device sitronix_device = {
+ .name = DEV_SITRONIX,
+ .id = 0,
+ .dev = {.release = sitronix_release},
+};
+
+static struct platform_driver sitronix_driver = {
+ .driver = {
+ .name = DEV_SITRONIX,
+ .owner = THIS_MODULE,
+ },
+ .probe = sitronix_probe,
+ .remove = sitronix_remove,
+ .suspend = sitronix_suspend,
+ .resume = sitronix_resume,
+};
+
+static int check_touch_env(struct sitronix_data *sitronix)
+{
+ int len = 96;
+ int Enable;
+ char retval[96] = {0};
+ char *p=NULL;
+ int ret;
+
+ // Get u-boot parameter
+ if(wmt_getsyspara("wmt.io.touch", retval, &len)) return -EIO;
+
+ sscanf(retval,"%d:",&Enable);
+ //check touch enable
+ if(Enable == 0) return -ENODEV;
+
+ p = strchr(retval,':');
+ p++;
+
+ if(strncmp(p,"st1536",6)) return -ENODEV;
+
+ sitronix->name = DEV_SITRONIX;
+ sitronix->addr = SITRONIX_ADDR;
+ p = strchr(p,':');
+ p++;
+ sscanf(p,"%d:%d:%d:%d:%d:%d:%d",
+ &sitronix->xresl, &sitronix->yresl, &sitronix->irqgpio, &sitronix->rstgpio, &sitronix->swap, &sitronix->xch, &sitronix->ych);
+
+ sitronix->irq = IRQ_GPIO;
+ printk("%s reslx=%d, resly=%d, irqgpio_num=%d, rstgpio_num=%d, XYswap=%d, Xdirch=%d, Ydirch=%d\n", sitronix->name,
+ sitronix->xresl, sitronix->yresl, sitronix->irqgpio, sitronix->rstgpio, sitronix->swap, sitronix->xch, sitronix->ych);
+
+ sitronix->penup = 1;
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ sitronix->lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = DEV_SITRONIX,
+ .flags = 0x00,
+ .addr = SITRONIX_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(I2C_BUS1);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+
+static int __init sitronix_init(void)
+{
+ int ret = -ENOMEM;
+ struct sitronix_data *sitronix=NULL;
+
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ sitronix = kzalloc(sizeof(struct sitronix_data), GFP_KERNEL);
+ if(!sitronix){
+ dbg_err("mem alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ pContext = sitronix;
+ ret = check_touch_env(sitronix);
+ if(ret < 0)
+ goto exit_free_mem;
+
+ ret = gpio_request(sitronix->irqgpio, "ts_irq");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen irq request fail\n", sitronix->irqgpio);
+ goto exit_free_mem;
+ }
+
+ ret = gpio_request(sitronix->rstgpio, "ts_rst");
+ if (ret < 0) {
+ printk("gpio(%d) touchscreen reset request fail\n", sitronix->rstgpio);
+ goto exit_free_irqgpio;
+ }
+ gpio_direction_output(sitronix->rstgpio, 1);
+
+
+ ret = platform_device_register(&sitronix_device);
+ if(ret){
+ dbg_err("register platform drivver failed!\n");
+ goto exit_free_gpio;
+ }
+ platform_set_drvdata(&sitronix_device, sitronix);
+
+ ret = platform_driver_register(&sitronix_driver);
+ if(ret){
+ dbg_err("register platform device failed!\n");
+ goto exit_unregister_pdev;
+ }
+
+ return ret;
+
+exit_unregister_pdev:
+ platform_device_unregister(&sitronix_device);
+exit_free_gpio:
+
+ gpio_free(sitronix->rstgpio);
+exit_free_irqgpio:
+ gpio_free(sitronix->irqgpio);
+
+exit_free_mem:
+ kfree(sitronix);
+ pContext = NULL;
+ ts_i2c_unregister_device();
+ return ret;
+}
+
+static void sitronix_exit(void)
+{
+ struct sitronix_data *sitronix;
+
+ if(!pContext) return;
+
+ sitronix = pContext;
+
+ gpio_free(sitronix->irqgpio);
+ gpio_free(sitronix->rstgpio);
+
+
+ platform_driver_unregister(&sitronix_driver);
+ platform_device_unregister(&sitronix_device);
+ kfree(pContext);
+
+ ts_i2c_unregister_device();
+ return;
+}
+
+late_initcall(sitronix_init);
+module_exit(sitronix_exit);
+
+MODULE_DESCRIPTION("Sitronix Multi-Touch Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/sitronix/sitronix_i2c.h b/drivers/input/touchscreen/sitronix/sitronix_i2c.h
new file mode 100755
index 00000000..8d3df91d
--- /dev/null
+++ b/drivers/input/touchscreen/sitronix/sitronix_i2c.h
@@ -0,0 +1,137 @@
+#ifndef _LINUX_SIT_I2C_H
+#define _LINUX_SIT_I2C_H
+
+#define SITRONIX_ADDR 0x60
+#define DEV_SITRONIX "touch_sitronix"
+#define SITRONIX_MAX_SUPPORTED_POINT 5
+#define TOUCH_KEY
+
+struct sitronix_data {
+ u16 addr;
+ const char *name;
+
+ struct input_dev *input_dev;
+ struct work_struct read_work;
+ struct workqueue_struct *workqueue;
+ struct kobject *kobj;
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ int earlysus;
+
+ int xresl;
+ int yresl;
+
+ int irq;
+ int irqgpio;
+
+ int rstgpio;
+
+ int xch;
+ int ych;
+ int swap;
+ int lcd_exchg;
+
+ int penup;
+ int dbg;
+#ifdef TOUCH_KEY
+ //int tkey_pressed;
+ int tkey_idx;
+#endif
+ u8 fw_revision[4];
+ int resolution_x;
+ int resolution_y;
+ u8 max_touches;
+ u8 touch_protocol_type;
+ u8 chip_id;
+
+ u8 Num_X;
+ u8 Num_Y;
+ u8 sensing_mode;
+ u8 pixel_length;
+};
+
+typedef enum{
+ FIRMWARE_VERSION,
+ STATUS_REG,
+ DEVICE_CONTROL_REG,
+ TIMEOUT_TO_IDLE_REG,
+ XY_RESOLUTION_HIGH,
+ X_RESOLUTION_LOW,
+ Y_RESOLUTION_LOW,
+ FIRMWARE_REVISION_3 = 0x0C,
+ FIRMWARE_REVISION_2,
+ FIRMWARE_REVISION_1,
+ FIRMWARE_REVISION_0,
+ FINGERS,
+ KEYS_REG,
+ XY0_COORD_H,
+ X0_COORD_L,
+ Y0_COORD_L,
+ I2C_PROTOCOL = 0x3E,
+ MAX_NUM_TOUCHES,
+ DATA_0_HIGH,
+ DATA_0_LOW,
+ CHIP_ID = 0xF4,
+
+ PAGE_REG = 0xff,
+}RegisterOffset;
+
+
+typedef enum{
+ XY_COORD_H,
+ X_COORD_L,
+ Y_COORD_L,
+ PIXEL_DATA_LENGTH_B,
+ PIXEL_DATA_LENGTH_A,
+}PIXEL_DATA_FORMAT;
+
+#define X_RES_H_SHFT 4
+#define X_RES_H_BMSK 0xf
+#define Y_RES_H_SHFT 0
+#define Y_RES_H_BMSK 0xf
+#define FINGERS_SHFT 0
+#define FINGERS_BMSK 0xf
+#define X_COORD_VALID_SHFT 7
+#define X_COORD_VALID_BMSK 0x1
+#define X_COORD_H_SHFT 4
+#define X_COORD_H_BMSK 0x7
+#define Y_COORD_H_SHFT 0
+#define Y_COORD_H_BMSK 0x7
+
+typedef enum{
+ SITRONIX_RESERVED_TYPE_0,
+ SITRONIX_A_TYPE,
+ SITRONIX_B_TYPE,
+}I2C_PROTOCOL_TYPE;
+
+#define I2C_PROTOCOL_SHFT 0x0
+#define I2C_PROTOCOL_BMSK 0x3
+
+typedef enum{
+ SENSING_BOTH,
+ SENSING_X_ONLY,
+ SENSING_Y_ONLY,
+ SENSING_BOTH_NOT,
+}ONE_D_SENSING_CONTROL_MODE;
+
+#define ONE_D_SENSING_CONTROL_SHFT 0x2
+#define ONE_D_SENSING_CONTROL_BMSK 0x3
+
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+//extern int wmt_i2c_xfer_continue_if_4(struct i2c_msg *msg, unsigned int num,int bus_id);
+
+//#define SITRONIX_DEBUG
+
+#undef dbg
+#ifdef SITRONIX_DEBUG
+ #define dbg(fmt,args...) printk("DBG:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+#else
+ #define dbg(fmt,args...)
+#endif
+
+#undef dbg_err
+#define dbg_err(fmt,args...) printk("ERR:%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
+
+#endif /* _LINUX_SIS_I2C_H */
diff --git a/drivers/input/touchscreen/ssd253x_ts/Kconfig b/drivers/input/touchscreen/ssd253x_ts/Kconfig
new file mode 100755
index 00000000..a5d9aa73
--- /dev/null
+++ b/drivers/input/touchscreen/ssd253x_ts/Kconfig
@@ -0,0 +1,16 @@
+#
+# SSD253x capacity touch screen driver configuration
+#
+config TOUCHSCREEN_SSD253X
+ tristate "SSD253X I2C Touchscreen Input Driver Support"
+ depends on ARCH_WMT
+ default y
+ help
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_ssd253x.
+
diff --git a/drivers/input/touchscreen/ssd253x_ts/Makefile b/drivers/input/touchscreen/ssd253x_ts/Makefile
new file mode 100755
index 00000000..d78c8466
--- /dev/null
+++ b/drivers/input/touchscreen/ssd253x_ts/Makefile
@@ -0,0 +1,32 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_ssd253x
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := ssd253x-ts.o wmt_ts.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers
diff --git a/drivers/input/touchscreen/ssd253x_ts/ssd253x-ts.c b/drivers/input/touchscreen/ssd253x_ts/ssd253x-ts.c
new file mode 100755
index 00000000..c02392cb
--- /dev/null
+++ b/drivers/input/touchscreen/ssd253x_ts/ssd253x-ts.c
@@ -0,0 +1,1827 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+//#include <asm/gpio.h>
+#include <asm/irq.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <mach/hardware.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/slab.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+
+#include "ssd253x-ts.h"
+#include "wmt_ts.h"
+
+//#define CONFIG_TOUCHSCREEN_SSL_DEBUG
+#undef CONFIG_TOUCHSCREEN_SSL_DEBUG
+
+#define DEVICE_ID_REG 2
+#define VERSION_ID_REG 3
+#define AUTO_INIT_RST_REG 68
+#define EVENT_STATUS 121
+#define EVENT_MSK_REG 122
+#define IRQ_MSK_REG 123
+#define FINGER01_REG 124
+#define EVENT_STACK 128
+#define EVENT_FIFO_SCLR 135
+#define TIMESTAMP_REG 136
+#define SELFCAP_STATUS_REG 185
+
+#define ON_TOUCH_INT INT_EI11 //GPIO :set the interrupt
+#define DEVICE_NAME "touch_ssd253x"
+#define SSD253X_I2C_ADDR 0x48 //0x48
+
+// SSD2533 Setting
+// Touch Panel Example
+static struct ChipSetting* ssd253xcfgTable = NULL;
+static int l_cfglen = 0;
+
+static struct ChipSetting ssd253xcfgTable_default[]={
+{2,0x06,0x1B,0x28},
+{2,0xd7,0x00,0x00},
+{2,0xd8,0x00,0x07},
+{2,0xdb,0x00,0x01},
+{2,0x30,0x03,0x08},
+{2,0x34,0xd4,0x1e},
+{2,0x57,0x00,0x06},
+{2,0x40,0x00,0xc8},
+{2,0x41,0x00,0x30},
+{2,0x42,0x00,0xc0},
+{2,0x43,0x00,0x30},
+{2,0x44,0x00,0xc0},
+{2,0x45,0x00,0xc0},
+{2,0x46,0x00,0x0f},
+{2,0x5f,0x00,0x00},
+{2,0x2d,0x00,0x00},
+{2,0x66,0x1F,0x38},
+{2,0x67,0x1c,0x92},
+{2,0x25,0x00,0x02},
+};
+
+
+// For SSD2533 Bug Version Only //
+//#define SSD2533FIXEDCODE
+ struct ChipSetting ssd253xcfgTable1[]={
+{ 1, 0xA4, 0x00, 0x00}, //MCU prescaler default=01
+{ 1, 0xD4, 0x08, 0x00}, //Dummy Code
+{ 1, 0xD4, 0x08, 0x00}, //Set Osc frequency default=8, range 0 to F
+};
+
+ struct ChipSetting Reset[]={
+{ 0, 0x04, 0x00, 0x00}, // SSD2533
+};
+
+ struct ChipSetting Resume[]={
+{ 0, 0x04, 0x00, 0x00}, // SSD2533
+{ 1, 0x25, 0x12, 0x00}, // Set Operation Mode //Set from int setting
+};
+
+ struct ChipSetting Suspend[] ={
+{ 1, 0x25, 0x00, 0x00}, // Set Operation Mode
+{ 0, 0x05, 0x00, 0x00}, // SSD2533
+};
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ssd253x_ts_early_suspend(struct early_suspend *h);
+static void ssd253x_ts_late_resume(struct early_suspend *h);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+static irqreturn_t ssd253x_ts_isr(int irq, void *dev_id);
+static enum hrtimer_restart ssd253x_ts_timer(struct hrtimer *timer);
+//extern int wmt_i2c_xfer_continue_if_4(struct i2c_msg *msg, unsigned int num,int bus_id);
+
+
+static int SSDS53X_SCREEN_MAX_X = 800;
+static int SSDS53X_SCREEN_MAX_Y = 480;
+
+
+
+enum{
+ IC_SSD2533 = 1,
+ IC_SSD2543,
+ IC_SSD2531
+};
+
+static int ic_flag;
+
+static struct workqueue_struct *ssd253x_wq;
+
+int Ssd_Timer1,Ssd_Timer2,Ssd_Timer_flag;
+
+struct ssl_ts_priv {
+ struct input_dev *input;
+ struct hrtimer timer;
+ struct work_struct ssl_work;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+
+ int irq;
+ int use_irq;
+ int FingerNo;
+ int earlysus;
+
+ int FingerX[FINGERNO];
+ int FingerY[FINGERNO];
+ int FingerP[FINGERNO];
+
+ int Resolution;
+ int EventStatus;
+ int FingerDetect;
+
+ int sFingerX[FINGERNO];
+ int sFingerY[FINGERNO];
+ int pFingerX[FINGERNO];
+ int pFingerY[FINGERNO];
+};
+
+static struct ssl_ts_priv* l_ts = NULL;
+struct wmtts_device ssd253x_tsdev;
+static DECLARE_WAIT_QUEUE_HEAD(ts_penup_wait_queue);
+
+#define SD_INIT
+#ifdef SD_INIT
+#define TP_CHR "tp_chr"
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+
+static long tp_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static int tp_open(struct inode *inode, struct file *file);
+static int tp_release(struct inode *inode, struct file *file);
+static ssize_t tp_read(struct file *file, char __user *buf, size_t count,loff_t *offset);
+static ssize_t tp_write(struct file *file, const char __user *buf,size_t count, loff_t *offset);
+
+//void InitFromSD(struct i2c_client *client);
+
+//struct ChipSetting _ssd253xcfgTable[200];
+//int sd_init_size=0;
+
+
+//struct i2c_client *g_tp_client;
+
+#endif
+
+
+
+static int ReadRegister(/*struct i2c_client *client,*/uint8_t reg,int ByteNo)
+{
+ unsigned char buf[4];
+ struct i2c_msg msg[2];
+ int ret;
+ struct i2c_client* client = ts_get_i2c_client();
+
+ memset(buf, 0xFF, sizeof(buf));
+ msg[0].addr = SSD253X_I2C_ADDR;
+ msg[0].flags = 0 | I2C_M_NOSTART;
+ msg[0].len = 1;
+ msg[0].buf = &reg;
+
+ msg[1].addr = SSD253X_I2C_ADDR;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = ByteNo;
+ msg[1].buf = buf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret <= 0)
+ {
+ printk("read the address (0x%x) of the ssd253x fail, ret=%d.\n", reg, ret);
+ return -1;
+ }
+
+ if(ByteNo==1) return (int)((unsigned int)buf[0]<<0);
+ if(ByteNo==2) return (int)((unsigned int)buf[1]<<0)|((unsigned int)buf[0]<<8);
+ if(ByteNo==3) return (int)((unsigned int)buf[2]<<0)|((unsigned int)buf[1]<<8)|((unsigned int)buf[0]<<16);
+ if(ByteNo==4) return (int)((unsigned int)buf[3]<<0)|((unsigned int)buf[2]<<8)|((unsigned int)buf[1]<<16)|(buf[0]<<24);
+ return 0;
+}
+
+static int WriteRegister(/*struct i2c_client *client,*/uint8_t Reg,unsigned char Data1,unsigned char Data2,int ByteNo)
+{
+ struct i2c_msg msg;
+ unsigned char buf[4];
+ int ret;
+ struct i2c_client* client = ts_get_i2c_client();
+
+ buf[0]=Reg;
+ buf[1]=Data1;
+ buf[2]=Data2;
+ buf[3]=0;
+
+ msg.addr = SSD253X_I2C_ADDR;
+ msg.flags = 0;
+ msg.len = ByteNo+1;
+ msg.buf = (char *)buf;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret <= 0)
+ {
+ printk(KERN_ERR "write the address (0x%x) of the ssd25xx fail, ret=%d.\n", buf[0], ret);
+ return -1;
+ }
+ return 0;
+
+}
+
+int SSD253xdeviceInit1(void)
+{
+#ifdef SSD2533FIXEDCODE
+ int i;
+ mdelay(600); //SSD2533 ESD2 EEPROM VERSION
+ for(i=0;i<sizeof(ssd253xcfgTable1)/sizeof(ssd253xcfgTable1[0]);i++)
+ {
+ if (WriteRegister(ssd253xcfgTable1[i].Reg,
+ ssd253xcfgTable1[i].Data1,ssd253xcfgTable1[i].Data2,
+ ssd253xcfgTable1[i].No))
+ {
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+int SSD253xdeviceInit(void)
+{
+ int i;
+
+ for(i=0;i<l_cfglen/*sizeof(ssd253xcfgTable)/sizeof(ssd253xcfgTable[0])*/;i++)
+ {
+ if (WriteRegister(ssd253xcfgTable[i].Reg,
+ ssd253xcfgTable[i].Data1,ssd253xcfgTable[i].Data2,
+ ssd253xcfgTable[i].No))
+ {
+ return -1;
+ }
+ if (0 == i)
+ {
+ msleep(300);
+ }
+ }
+ msleep(300);
+ return 0;
+}
+
+int deviceReset(void)
+{
+ int i;
+
+ i = 0;//just for remove warning message
+ wmt_rst_output(1);
+ mdelay(5);
+ wmt_rst_output(0);
+ mdelay(10);
+ wmt_rst_output(1);
+ msleep(200);
+
+ //if(ic_flag == IC_SSD2533){
+ for(i=0;i<sizeof(Reset)/sizeof(Reset[0]);i++)
+ {
+ if (WriteRegister(Reset[i].Reg,
+ Reset[i].Data1,Reset[i].Data2,
+ Reset[i].No))
+ {
+ return -1;
+ }
+ }
+ //}
+
+ mdelay(100);
+ if (SSD253xdeviceInit1())
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int deviceResume(void)
+{
+ int i;
+ for(i=0;i<sizeof(Resume)/sizeof(Resume[0]);i++)
+ {
+ if (WriteRegister(Resume[i].Reg,
+ Resume[i].Data1,Resume[i].Data2,
+ Resume[i].No))
+ {
+ return -1;
+ }
+ mdelay(100);
+ }
+ return 0;
+}
+
+int deviceSuspend(void)
+{
+ int i;
+ //int timeout=10;
+ //int status;
+
+ for(i=0;i<sizeof(Suspend)/sizeof(Suspend[0]);i++)
+ {
+ if (WriteRegister(Suspend[i].Reg,
+ Suspend[i].Data1,Suspend[i].Data2,
+ Suspend[i].No))
+ {
+ return -1;
+ }
+ mdelay(100);
+ }
+ return 0;
+}
+
+#define Mode RunningAverageMode
+#define Dist RunningAverageDist
+void RunningAverage(unsigned short *xpos,unsigned short *ypos,int No,struct ssl_ts_priv *ssl_priv)
+{
+ int FilterMode[4][2]={{0,8},{5,3},{6,2},{7,1}};
+ int dx,dy;
+ int X,Y;
+
+ X=*xpos;
+ Y=*ypos;
+ if((ssl_priv->pFingerX[No]!=0x0FFF)&&(X!=0x0FFF))
+ {
+ dx=abs(ssl_priv->pFingerX[No]-X);
+ dy=abs(ssl_priv->pFingerY[No]-Y);
+ if(dx+dy<Dist*64)
+ {
+ ssl_priv->pFingerX[No]=(FilterMode[Mode][0]*ssl_priv->pFingerX[No]+FilterMode[Mode][1]*X)/8;
+ ssl_priv->pFingerY[No]=(FilterMode[Mode][0]*ssl_priv->pFingerY[No]+FilterMode[Mode][1]*Y)/8;
+ }
+ else
+ {
+ ssl_priv->pFingerX[No]=X;
+ ssl_priv->pFingerY[No]=Y;
+ }
+ }
+ else
+ {
+ ssl_priv->pFingerX[No]=X;
+ ssl_priv->pFingerY[No]=Y;
+ }
+ *xpos=ssl_priv->pFingerX[No];
+ *ypos=ssl_priv->pFingerY[No];
+}
+
+void FingerCheckSwap(int *FingerX,int *FingerY,int *FingerP,int FingerNo,int *sFingerX,int *sFingerY)
+{
+ int i,j;
+ int index1,index2;
+ int Vx,Vy;
+ int Ux,Uy;
+ int R1x,R1y;
+ int R2x,R2y;
+ for(i=0;i<FingerNo;i++)
+ {
+ index1=i;
+ if( FingerX[index1]!=0xFFF)
+ if(sFingerX[index1]!=0xFFF)
+ {
+ for(j=i+1;j<FingerNo+3;j++)
+ {
+ index2=j%FingerNo;
+ if( FingerX[index2]!=0xFFF)
+ if(sFingerX[index2]!=0xFFF)
+ {
+ Ux=sFingerX[index1]-sFingerX[index2];
+ Uy=sFingerY[index1]-sFingerY[index2];
+ Vx= FingerX[index1]- FingerX[index2];
+ Vy= FingerY[index1]- FingerY[index2];
+
+ R1x=Ux-Vx;
+ R1y=Uy-Vy;
+ R2x=Ux+Vx;
+ R2y=Uy+Vy;
+
+ R1x=R1x*R1x;
+ R1y=R1y*R1y;
+ R2x=R2x*R2x;
+ R2y=R2y*R2y;
+
+ if(R1x+R1y>R2x+R2y)
+ {
+ Ux=FingerX[index1];
+ Uy=FingerY[index1];
+ Vx=FingerP[index1];
+
+ FingerX[index1]=FingerX[index2];
+ FingerY[index1]=FingerY[index2];
+ FingerP[index1]=FingerP[index2];
+
+ FingerX[index2]=Ux;
+ FingerY[index2]=Uy;
+ FingerP[index2]=Vx;
+ }
+ break;
+ }
+ }
+ }
+ }
+ for(i=0;i<FingerNo;i++)
+ {
+ sFingerX[i]=FingerX[i];
+ sFingerY[i]=FingerY[i];
+ }
+}
+
+#ifdef USE_TOUCH_KEY
+static void ssd2533_ts_send_keyevent(struct ssl_ts_priv *ssl_priv,u8 btn_status, int downup)
+{
+
+ switch(btn_status & 0x0f)
+ {
+ case 0x01:
+ input_report_key(ssl_priv->input, KEY_SEARCH, downup);
+ break;
+ case 0x02:
+ input_report_key(ssl_priv->input, KEY_BACK, downup);
+ break;
+ case 0x04:
+ input_report_key(ssl_priv->input, KEY_HOME, downup);
+ break;
+ case 0x08:
+ input_report_key(ssl_priv->input, KEY_MENU, downup);
+ break;
+ default:
+ break;
+ }
+ dbg("send %x %x\n", btn_status, downup);
+}
+#endif
+
+// for ssd2533(no test)
+static int ssd253x_ts_cut_edge0(unsigned short pos,unsigned short x_y)
+{
+ u8 cut_value = 26; //26 cut_value < 32
+ if(pos == 0xfff)
+ {
+ return pos;
+ }
+ //printk("X: rude data %d\n",pos);
+ if(x_y) //xpos
+ {
+
+ if(pos < 16)
+ pos = cut_value + pos*(48 - cut_value) / 16;
+ else if(pos > (XPOS_MAX - 16) )
+ pos = XPOS_MAX + 16 + (pos - (XPOS_MAX -16))*(48 - cut_value) / 16;
+ else
+ pos = pos + 32;
+
+ pos = SSDS53X_SCREEN_MAX_X * pos / (DRIVENO * 64);
+ //printk("X: changed data %d\n",pos);
+ return pos;
+ }
+ else //ypos
+ {
+ if(pos < 16)
+ pos = cut_value + pos*(48 - cut_value) / 16;
+ else if(pos > (YPOS_MAX - 16) )
+ pos = YPOS_MAX + 16 + (pos - (YPOS_MAX -16))*(48 - cut_value) / 16;
+ else
+ pos = pos + 32;
+ //printk("Y: rude data %d\n",pos);
+ pos = SSDS53X_SCREEN_MAX_Y* pos / (SENSENO * 64);
+ //printk("Y: changed data %d\n",pos);
+ return pos;
+ }
+
+
+}
+
+// for ssd2532
+static int ssd253x_ts_cut_edge1(unsigned short pos,unsigned short x_y)
+{
+ u8 cut_value = 15; //cut_value < 32
+
+ if(pos == 0xfff){
+ return pos;
+ }
+
+ if(x_y){ //xpos 64-->96 //MAX=896
+ pos = pos + cut_value;//????????Ôµ
+ pos = SSDS53X_SCREEN_MAX_X * pos / (790+cut_value*2);//SSDS53X_SCREEN_MAX_X?????Ò±?Ôµ
+ return pos;
+ }else{ //ypos //MAX=576
+ pos = pos + cut_value;//?????ϱ?Ե
+ pos = SSDS53X_SCREEN_MAX_Y* pos / (470+cut_value*2);//SSDS53X_SCREEN_MAX_Y?????±?Ե
+ return pos;
+ }
+}
+
+// for ssd2532,8" ssd253x_pydctp80a1.ts
+// x_y:1--x,0--y
+static int ssd253x_ts_cut_edge2(unsigned short pos,unsigned short x_y)
+{
+ int tpos;
+
+ if (pos == 0xfff){
+ return pos;
+ }
+
+ tpos = pos;
+ if (x_y)
+ {
+ if (tpos<20)
+ {
+ tpos= tpos+18;
+ } else if (tpos>585)
+ {
+ tpos = tpos-18;
+ } else {
+ tpos = (tpos-20)*565/575+30;
+ }
+ pos = tpos;
+ return pos;
+ } else {
+ if (tpos <10)
+ {
+ tpos = tpos+10;
+ } else if (tpos >795)
+ {
+ tpos = 795;
+ } else {
+ tpos = (tpos-10)*775/785+20;
+ }
+ pos = tpos;
+ return pos;
+ }
+
+}
+// for ssd2532
+static int ssd253x_ts_cut_edge3(unsigned short pos,unsigned short x_y)
+{
+ u8 cut_value = 15;
+
+ if(pos == 0xfff){
+ return pos;
+ }
+
+ if(x_y){
+ pos = pos + cut_value;
+ pos = SSDS53X_SCREEN_MAX_X * pos / (896+cut_value*2);
+ return pos;
+ }else{
+ pos = pos + cut_value;
+ pos = SSDS53X_SCREEN_MAX_Y* pos / (576+cut_value*2);
+ return pos;
+ }
+}
+
+// for jun feng TP
+static int ssd253x_ts_cut_edge4(unsigned short pos,unsigned short x_y)
+{
+ unsigned short Cut_Edge_XLeft[64]={
+ 0x0008,0x0009,0x000B,0x000C,0x000D,0x000E,0x0010,0x0011,
+ 0x0012,0x0013,0x0015,0x0016,0x0017,0x0018,0x001A,0x001B,
+ 0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,0x0024,0x0025,
+ 0x0026,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,
+ 0x002C,0x002D,0x002E,0x002F,0x0030,0x0031,0x0032,0x0032,
+ 0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0038,0x0039,
+ 0x003A,0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,
+ 0x0041,0x0042,0x0043,0x0044,0x0044,0x0045,0x0046,0x0047
+ };
+
+ unsigned short Cut_Edge_XRight[64]={
+ 0x0318,0x0317,0x0315,0x0314,0x0313,0x0312,0x0310,0x030F,
+ 0x030E,0x030D,0x030B,0x030A,0x0309,0x0308,0x0306,0x0305,
+ 0x0304,0x0303,0x0301,0x0300,0x02FF,0x02FE,0x02FC,0x02FB,
+ 0x02FA,0x02FA,0x02F9,0x02F8,0x02F7,0x02F6,0x02F5,0x02F4,
+ 0x02F4,0x02F3,0x02F2,0x02F1,0x02F0,0x02EF,0x02EE,0x02EE,
+ 0x02ED,0x02EC,0x02EB,0x02EA,0x02E9,0x02E8,0x02E8,0x02E7,
+ 0x02E6,0x02E5,0x02E4,0x02E3,0x02E2,0x02E2,0x02E1,0x02E0,
+ 0x02DF,0x02DE,0x02DD,0x02DC,0x02DC,0x02DB,0x02DA,0x02D9
+ };
+
+ unsigned short Cut_Edge_YUp[64]={
+ 0x0006,0x0007,0x0008,0x000A,0x000B,0x000C,0x000D,0x000F,
+ 0x0010,0x0011,0x0012,0x0014,0x0015,0x0016,0x0017,0x0018,
+ 0x001A,0x001B,0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,
+ 0x0022,0x0023,0x0024,0x0025,0x0025,0x0026,0x0027,0x0028,
+ 0x0029,0x0029,0x002A,0x002B,0x002C,0x002C,0x002D,0x002E,
+ 0x002F,0x0030,0x0030,0x0031,0x0032,0x0033,0x0033,0x0034,
+ 0x0035,0x0036,0x0037,0x0037,0x0038,0x0039,0x003A,0x003A,
+ 0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,0x0041
+ };
+
+ unsigned short Cut_Edge_YDown[64]={
+ 0x01DA,0x01D9,0x01D8,0x01D6,0x01D5,0x01D4,0x01D3,0x01D1,
+ 0x01D0,0x01CF,0x01CE,0x01CC,0x01CB,0x01CA,0x01C9,0x01C8,
+ 0x01C6,0x01C5,0x01C4,0x01C3,0x01C1,0x01C0,0x01BF,0x01BE,
+ 0x01BE,0x01BD,0x01BC,0x01BB,0x01BB,0x01BA,0x01B9,0x01B8,
+ 0x01B7,0x01B7,0x01B6,0x01B5,0x01B4,0x01B4,0x01B3,0x01B2,
+ 0x01B1,0x01B0,0x01B0,0x01AF,0x01AE,0x01AD,0x01AD,0x01AC,
+ 0x01AB,0x01AA,0x01A9,0x01A9,0x01A8,0x01A7,0x01A6,0x01A6,
+ 0x01A5,0x01A4,0x01A3,0x01A2,0x01A2,0x01A1,0x01A0,0x019F
+ };
+ int cut_value = 5; //cut_value < 32
+ if(pos == 0xfff)
+ {
+ return pos;
+ }
+ if(x_y) //xpos
+ {
+ dbg("X: Raw data %d\n",pos);
+ if (pos >=XPOS_MAX)
+ {
+ pos = XPOS_MAX;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_XLeft[pos]; //Left cut edge
+ }
+ else
+ if ((XPOS_MAX - pos) <64)
+ {
+ pos = Cut_Edge_XRight[XPOS_MAX - pos]; //Right cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSDS53X_SCREEN_MAX_X* pos / (790 + cut_value*2);//SSD253X_SCREEN_MAX_X|?????2????????|?
+ }
+ dbg("X: Cut edge data %d\n",pos);
+ return pos;
+ }
+ else //ypos
+ {
+
+ dbg("Y: Raw data %d\n",pos);
+ if (pos >=YPOS_MAX)
+ {
+ pos = YPOS_MAX;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_YUp[pos]; //Up cut edge
+ }
+ else
+ if ((YPOS_MAX - pos) <64)
+ {
+ pos = Cut_Edge_YDown[YPOS_MAX - pos]; //Down cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSDS53X_SCREEN_MAX_Y* pos / (470 + cut_value*2);//SSD253X_SCREEN_MAX_X|?????2????????|?
+ //tpos = pos;
+ //tpos = /*SSDS53X_SCREEN_MAX_Y*/ (800* pos) / (470 + cut_value*2);
+ dbg("XPOS_MAX=%d,\n", XPOS_MAX);
+ dbg("YPOS_MAX=%d,\n",YPOS_MAX);
+ dbg("Y: Cut edge data pos= %d,tpos=%d\n",pos,tpos);
+ }
+
+ return pos;
+ }
+
+
+}
+
+static int ssd253x_ts_cut_edge5(unsigned short pos,unsigned short x_y)
+{
+ unsigned short Cut_Edge_XLeft[64]={
+ 0x0008,0x0009,0x000B,0x000C,0x000D,0x000E,0x0010,0x0011,
+ 0x0012,0x0013,0x0015,0x0016,0x0017,0x0018,0x001A,0x001B,
+ 0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,0x0024,0x0025,
+ 0x0026,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,
+ 0x002C,0x002D,0x002E,0x002F,0x0030,0x0031,0x0032,0x0032,
+ 0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0038,0x0039,
+ 0x003A,0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,
+ 0x0041,0x0042,0x0043,0x0044,0x0044,0x0045,0x0046,0x0047
+ };
+
+ unsigned short Cut_Edge_XRight[64]={
+ 0x0318,0x0317,0x0315,0x0314,0x0313,0x0312,0x0310,0x030F,
+ 0x030E,0x030D,0x030B,0x030A,0x0309,0x0308,0x0306,0x0305,
+ 0x0304,0x0303,0x0301,0x0300,0x02FF,0x02FE,0x02FC,0x02FB,
+ 0x02FA,0x02FA,0x02F9,0x02F8,0x02F7,0x02F6,0x02F5,0x02F4,
+ 0x02F4,0x02F3,0x02F2,0x02F1,0x02F0,0x02EF,0x02EE,0x02EE,
+ 0x02ED,0x02EC,0x02EB,0x02EA,0x02E9,0x02E8,0x02E8,0x02E7,
+ 0x02E6,0x02E5,0x02E4,0x02E3,0x02E2,0x02E2,0x02E1,0x02E0,
+ 0x02DF,0x02DE,0x02DD,0x02DC,0x02DC,0x02DB,0x02DA,0x02D9
+ };
+
+ unsigned short Cut_Edge_YUp[64]={
+ 0x0006,0x0007,0x0008,0x000A,0x000B,0x000C,0x000D,0x000F,
+ 0x0010,0x0011,0x0012,0x0014,0x0015,0x0016,0x0017,0x0018,
+ 0x001A,0x001B,0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,
+ 0x0022,0x0023,0x0024,0x0025,0x0025,0x0026,0x0027,0x0028,
+ 0x0029,0x0029,0x002A,0x002B,0x002C,0x002C,0x002D,0x002E,
+ 0x002F,0x0030,0x0030,0x0031,0x0032,0x0033,0x0033,0x0034,
+ 0x0035,0x0036,0x0037,0x0037,0x0038,0x0039,0x003A,0x003A,
+ 0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,0x0041
+ };
+
+ unsigned short Cut_Edge_YDown[64]={
+ 0x01DA,0x01D9,0x01D8,0x01D6,0x01D5,0x01D4,0x01D3,0x01D1,
+ 0x01D0,0x01CF,0x01CE,0x01CC,0x01CB,0x01CA,0x01C9,0x01C8,
+ 0x01C6,0x01C5,0x01C4,0x01C3,0x01C1,0x01C0,0x01BF,0x01BE,
+ 0x01BE,0x01BD,0x01BC,0x01BB,0x01BB,0x01BA,0x01B9,0x01B8,
+ 0x01B7,0x01B7,0x01B6,0x01B5,0x01B4,0x01B4,0x01B3,0x01B2,
+ 0x01B1,0x01B0,0x01B0,0x01AF,0x01AE,0x01AD,0x01AD,0x01AC,
+ 0x01AB,0x01AA,0x01A9,0x01A9,0x01A8,0x01A7,0x01A6,0x01A6,
+ 0x01A5,0x01A4,0x01A3,0x01A2,0x01A2,0x01A1,0x01A0,0x019F
+ };
+ u8 cut_value = 20; //cut_value < 32
+ if(pos == 0xfff)
+ {
+ return pos;
+ }
+ if(x_y) //xpos
+ {
+ dbg("X: Raw data %d\n",pos);
+ if (pos >=XPOS_MAX)
+ {
+ pos = XPOS_MAX;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_XLeft[pos]; //Left cut edge
+ }
+ else
+ if ((XPOS_MAX - pos) <64)
+ {
+ pos = Cut_Edge_XRight[XPOS_MAX - pos]; //Right cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSDS53X_SCREEN_MAX_X * pos / (XPOS_MAX + cut_value*2);//SSD253X_SCREEN_MAX_X|??????????|? }
+ dbg("X: Cut edge data %d\n",pos);
+ return pos;
+ }
+ }
+ else //ypos
+ {
+
+ dbg("Y: Raw data %d\n",pos);
+ if (pos >=YPOS_MAX)
+ {
+ pos = YPOS_MAX;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_YUp[pos]; //Up cut edge
+ }
+ else
+ if ((YPOS_MAX - pos) <64)
+ {
+ pos = Cut_Edge_YDown[YPOS_MAX - pos]; //Down cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSDS53X_SCREEN_MAX_Y * pos / (YPOS_MAX + cut_value*2);//SSD253X_SCREEN_MAX_X|??????????|? }
+ dbg("Y: Cut edge data %d\n",pos);
+ return pos;
+ }
+ }
+ return -1;
+}
+
+static int ssd253x_ts_cut_edge6(unsigned short pos,unsigned short x_y)
+{
+
+ #define XPOS_MAX_D (DRIVENO -EdgeDisable) *64
+ #define YPOS_MAX_D (SENSENO -EdgeDisable) *64
+ #undef SSD253X_SCREEN_MAX_X
+ #define SSD253X_SCREEN_MAX_X 800
+ #define SSD253X_SCREEN_MAX_Y 480
+
+ u8 cut_value = 20; //cut_value < 32
+ unsigned short Cut_Edge_XLeft[64]={
+ 0x0008,0x0009,0x000B,0x000C,0x000D,0x000E,0x0010,0x0011,
+ 0x0012,0x0013,0x0015,0x0016,0x0017,0x0018,0x001A,0x001B,
+ 0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,0x0024,0x0025,
+ 0x0026,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,
+ 0x002C,0x002D,0x002E,0x002F,0x0030,0x0031,0x0032,0x0032,
+ 0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0038,0x0039,
+ 0x003A,0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,
+ 0x0041,0x0042,0x0043,0x0044,0x0044,0x0045,0x0046,0x0047
+ };
+
+ unsigned short Cut_Edge_XRight[64]={
+ 0x0318,0x0317,0x0315,0x0314,0x0313,0x0312,0x0310,0x030F,
+ 0x030E,0x030D,0x030B,0x030A,0x0309,0x0308,0x0306,0x0305,
+ 0x0304,0x0303,0x0301,0x0300,0x02FF,0x02FE,0x02FC,0x02FB,
+ 0x02FA,0x02FA,0x02F9,0x02F8,0x02F7,0x02F6,0x02F5,0x02F4,
+ 0x02F4,0x02F3,0x02F2,0x02F1,0x02F0,0x02EF,0x02EE,0x02EE,
+ 0x02ED,0x02EC,0x02EB,0x02EA,0x02E9,0x02E8,0x02E8,0x02E7,
+ 0x02E6,0x02E5,0x02E4,0x02E3,0x02E2,0x02E2,0x02E1,0x02E0,
+ 0x02DF,0x02DE,0x02DD,0x02DC,0x02DC,0x02DB,0x02DA,0x02D9
+ };
+
+ unsigned short Cut_Edge_YUp[64]={
+ 0x0006,0x0007,0x0008,0x000A,0x000B,0x000C,0x000D,0x000F,
+ 0x0010,0x0011,0x0012,0x0014,0x0015,0x0016,0x0017,0x0018,
+ 0x001A,0x001B,0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,
+ 0x0022,0x0023,0x0024,0x0025,0x0025,0x0026,0x0027,0x0028,
+ 0x0029,0x0029,0x002A,0x002B,0x002C,0x002C,0x002D,0x002E,
+ 0x002F,0x0030,0x0030,0x0031,0x0032,0x0033,0x0033,0x0034,
+ 0x0035,0x0036,0x0037,0x0037,0x0038,0x0039,0x003A,0x003A,
+ 0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,0x0041
+ };
+
+ unsigned short Cut_Edge_YDown[64]={
+ 0x01DA,0x01D9,0x01D8,0x01D6,0x01D5,0x01D4,0x01D3,0x01D1,
+ 0x01D0,0x01CF,0x01CE,0x01CC,0x01CB,0x01CA,0x01C9,0x01C8,
+ 0x01C6,0x01C5,0x01C4,0x01C3,0x01C1,0x01C0,0x01BF,0x01BE,
+ 0x01BE,0x01BD,0x01BC,0x01BB,0x01BB,0x01BA,0x01B9,0x01B8,
+ 0x01B7,0x01B7,0x01B6,0x01B5,0x01B4,0x01B4,0x01B3,0x01B2,
+ 0x01B1,0x01B0,0x01B0,0x01AF,0x01AE,0x01AD,0x01AD,0x01AC,
+ 0x01AB,0x01AA,0x01A9,0x01A9,0x01A8,0x01A7,0x01A6,0x01A6,
+ 0x01A5,0x01A4,0x01A3,0x01A2,0x01A2,0x01A1,0x01A0,0x019F
+ };
+
+
+ if(pos == 0xfff)
+ {
+ return pos;
+ }
+ if(x_y) //xpos
+ {
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("X: Raw data %d\n",pos);
+ //#endif
+ if (pos >=XPOS_MAX_D)
+ {
+ pos = XPOS_MAX_D;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_XLeft[pos]; //Left cut edge
+ }
+ else
+ if ((XPOS_MAX_D - pos) <64)
+ {
+ pos = Cut_Edge_XRight[XPOS_MAX_D - pos]; //Right cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSD253X_SCREEN_MAX_X * pos / (XPOS_MAX_D + cut_value*2);//SSD253X_SCREEN_MAX_X|?????2????????|?
+ }
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("X: Cut edge data %d\n",pos);
+ //#endif
+ return pos;
+ }
+ else //ypos
+ {
+
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("Y: Raw data %d\n",pos);
+ //#endif
+ if (pos >=YPOS_MAX_D)
+ {
+ pos = YPOS_MAX_D;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_YUp[pos]; //Up cut edge
+ }
+ else
+ if ((YPOS_MAX_D - pos) <64)
+ {
+ pos = Cut_Edge_YDown[YPOS_MAX_D - pos]; //Down cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSD253X_SCREEN_MAX_Y * pos / (YPOS_MAX_D + cut_value*2);//SSD253X_SCREEN_MAX_X|?????2????????|?
+ }
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("Y: Cut edge data %d\n",pos);
+ //#endif
+ return pos;
+ }
+ return -1;
+}
+
+static int ssd253x_ts_cut_edge8(unsigned short pos,unsigned short x_y)
+{
+
+ #define XPOS_MAX_D (DRIVENO -EdgeDisable) *64
+ #define YPOS_MAX_D (SENSENO -EdgeDisable) *64
+ #undef SSD253X_SCREEN_MAX_X
+ #define SSD253X_SCREEN_MAX_X 780
+ #undef SSD253X_SCREEN_MAX_Y
+ #define SSD253X_SCREEN_MAX_Y 470
+
+ u8 cut_value = 10;//30; //cut_value < 32
+ unsigned short Cut_Edge_XLeft[64]={
+ 0x0008,0x0009,0x000B,0x000C,0x000D,0x000E,0x0010,0x0011,
+ 0x0012,0x0013,0x0015,0x0016,0x0017,0x0018,0x001A,0x001B,
+ 0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,0x0024,0x0025,
+ 0x0026,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,
+ 0x002C,0x002D,0x002E,0x002F,0x0030,0x0031,0x0032,0x0032,
+ 0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0038,0x0039,
+ 0x003A,0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,
+ 0x0041,0x0042,0x0043,0x0044,0x0044,0x0045,0x0046,0x0047
+ };
+
+ unsigned short Cut_Edge_XRight[64]={
+ 0x0318,0x0317,0x0315,0x0314,0x0313,0x0312,0x0310,0x030F,
+ 0x030E,0x030D,0x030B,0x030A,0x0309,0x0308,0x0306,0x0305,
+ 0x0304,0x0303,0x0301,0x0300,0x02FF,0x02FE,0x02FC,0x02FB,
+ 0x02FA,0x02FA,0x02F9,0x02F8,0x02F7,0x02F6,0x02F5,0x02F4,
+ 0x02F4,0x02F3,0x02F2,0x02F1,0x02F0,0x02EF,0x02EE,0x02EE,
+ 0x02ED,0x02EC,0x02EB,0x02EA,0x02E9,0x02E8,0x02E8,0x02E7,
+ 0x02E6,0x02E5,0x02E4,0x02E3,0x02E2,0x02E2,0x02E1,0x02E0,
+ 0x02DF,0x02DE,0x02DD,0x02DC,0x02DC,0x02DB,0x02DA,0x02D9
+ };
+
+ unsigned short Cut_Edge_YUp[64]={
+ 0x0006,0x0007,0x0008,0x000A,0x000B,0x000C,0x000D,0x000F,
+ 0x0010,0x0011,0x0012,0x0014,0x0015,0x0016,0x0017,0x0018,
+ 0x001A,0x001B,0x001C,0x001D,0x001F,0x0020,0x0021,0x0022,
+ 0x0022,0x0023,0x0024,0x0025,0x0025,0x0026,0x0027,0x0028,
+ 0x0029,0x0029,0x002A,0x002B,0x002C,0x002C,0x002D,0x002E,
+ 0x002F,0x0030,0x0030,0x0031,0x0032,0x0033,0x0033,0x0034,
+ 0x0035,0x0036,0x0037,0x0037,0x0038,0x0039,0x003A,0x003A,
+ 0x003B,0x003C,0x003D,0x003E,0x003E,0x003F,0x0040,0x0041
+ };
+
+ unsigned short Cut_Edge_YDown[64]={
+ 0x01DA,0x01D9,0x01D8,0x01D6,0x01D5,0x01D4,0x01D3,0x01D1,
+ 0x01D0,0x01CF,0x01CE,0x01CC,0x01CB,0x01CA,0x01C9,0x01C8,
+ 0x01C6,0x01C5,0x01C4,0x01C3,0x01C1,0x01C0,0x01BF,0x01BE,
+ 0x01BE,0x01BD,0x01BC,0x01BB,0x01BB,0x01BA,0x01B9,0x01B8,
+ 0x01B7,0x01B7,0x01B6,0x01B5,0x01B4,0x01B4,0x01B3,0x01B2,
+ 0x01B1,0x01B0,0x01B0,0x01AF,0x01AE,0x01AD,0x01AD,0x01AC,
+ 0x01AB,0x01AA,0x01A9,0x01A9,0x01A8,0x01A7,0x01A6,0x01A6,
+ 0x01A5,0x01A4,0x01A3,0x01A2,0x01A2,0x01A1,0x01A0,0x019F
+ };
+
+
+ if(pos == 0xfff)
+ {
+ return pos;
+ }
+ if(x_y) //xpos
+ {
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("X: Raw data %d\n",pos);
+ //#endif
+ if (pos >=XPOS_MAX_D)
+ {
+ pos = XPOS_MAX_D;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_XLeft[pos]; //Left cut edge
+ }
+ else
+ if ((XPOS_MAX_D - pos) <64)
+ {
+ pos = Cut_Edge_XRight[XPOS_MAX_D - pos]; //Right cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSD253X_SCREEN_MAX_X * pos / (XPOS_MAX_D + cut_value*2);//SSD253X_SCREEN_MAX_X|?????2????????|?
+ }
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("X: Cut edge data %d\n",pos);
+ //#endif
+ return pos;
+ }
+ else //ypos
+ {
+
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("Y: Raw data %d\n",pos);
+ //#endif
+ if (pos >=YPOS_MAX_D)
+ {
+ pos = YPOS_MAX_D;
+ }
+ if (pos<64)
+ {
+ pos = Cut_Edge_YUp[pos]; //Up cut edge
+ }
+ else
+ if ((YPOS_MAX_D - pos) <64)
+ {
+ pos = Cut_Edge_YDown[YPOS_MAX_D - pos]; //Down cut edge
+ }
+ else
+ {
+ pos = pos + cut_value; //
+ pos = SSD253X_SCREEN_MAX_Y * pos / (YPOS_MAX_D + cut_value*2);//SSD253X_SCREEN_MAX_X|?????2????????|?
+ }
+ //#ifdef CONFIG_TS_CUTEDGE_DEBUG
+ dbg("Y: Cut edge data %d\n",pos);
+ //#endif
+ return pos;
+ }
+}
+
+static int ssd253x_ts_cut_edge7(unsigned short pos,unsigned short x_y)
+{
+ unsigned short SENSENO_7 = 15;
+ unsigned short DRIVENO_7 = 20;
+ unsigned short EdgeDisable_7 = 1; // if Edge Disable, set it to 1, else reset to 0, OR SSD2533 set 0
+ unsigned short XPOS_MAX_7 = (DRIVENO_7 -EdgeDisable_7) *64;
+ unsigned short YPOS_MAX_7 = (SENSENO_7 -EdgeDisable_7) *64;
+
+ u8 cut_value = 10; //cut_value < 32
+ dbg("enter...\n");
+
+ if(pos == 0xfff)
+ {
+ return pos;
+ }
+ if(x_y) //xpos
+ {
+ if(pos < 16)
+ pos = cut_value + pos*(48 - cut_value) / 16;
+ else if(pos > (XPOS_MAX_7 - 16) )
+ pos = XPOS_MAX_7 + 16 + (pos - (XPOS_MAX_7 -16))*(48 - cut_value) / 16;
+ else
+ pos = pos + 32;
+ dbg("xpos_b:%d\n", pos);
+ pos = SSDS53X_SCREEN_MAX_X * pos / (DRIVENO_7 * 64);
+ dbg("xpos_a:%d\n", pos);
+ return pos;
+ }
+ else //ypos
+ {
+ if(pos < 16)
+ pos = cut_value + pos*(48 - cut_value) / 16;
+ else if(pos > (YPOS_MAX_7 - 16) )
+ pos = YPOS_MAX_7 + 16 + (pos - (YPOS_MAX_7 -16))*(48 - cut_value) / 16;
+ else
+ pos = pos + 32;
+ dbg("ypos_b:%d\n", pos);
+ pos = SSDS53X_SCREEN_MAX_Y* pos / (SENSENO_7 * 64);
+ dbg("ypos_a:%d\n", pos);
+ return pos;
+ }
+
+
+}
+
+
+static int ssd253x_ts_cut_edge(unsigned short pos,unsigned short x_y)
+{
+ switch (wmt_ts_get_cutedge())
+ {
+ case 0:
+ return ssd253x_ts_cut_edge0(pos,x_y);
+ break;
+ case 1:
+ return ssd253x_ts_cut_edge1(pos,x_y);
+ break;
+ case 2:
+ return ssd253x_ts_cut_edge2(pos,x_y);
+ break;
+ case 3:
+ return ssd253x_ts_cut_edge3(pos,x_y);
+ break;
+ case 4:
+ return ssd253x_ts_cut_edge4(pos,x_y);
+ break;
+ case 5:
+ return ssd253x_ts_cut_edge5(pos,x_y);
+ break;
+ case 6:
+ return ssd253x_ts_cut_edge6(pos,x_y);
+ break;
+ case 7:
+ return ssd253x_ts_cut_edge7(pos,x_y);
+ break;
+ case 8:
+ return ssd253x_ts_cut_edge8(pos,x_y);
+ break;
+ default:
+ return -1;
+ };
+}
+
+#ifdef USE_TOUCH_KEY
+static u8 btn_status_last = 0;
+#endif
+
+static void ssd253x_ts_work(struct work_struct *work)
+{
+ int i;
+ unsigned short xpos=0, ypos=0;
+ int tx,ty;
+ //width=0;
+ int FingerInfo;
+ int EventStatus;
+ int FingerX[FINGERNO];
+ int FingerY[FINGERNO];
+ int FingerP[FINGERNO];
+ int clrFlag=0;
+ int Ssd_Timer;
+ #ifdef USE_TOUCH_KEY
+ u8 btn_status;
+ u8 btn_status_last = 0;
+ #endif
+
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ #ifdef USE_TOUCH_KEY
+ btn_status = ReadRegister(ssl_priv->client,SELFCAP_STATUS_REG, 1);
+ //#ifdef CONFIG_TOUCHSCREEN_SSL_DEBUG
+ dbg("btn pressed:%x\n", btn_status & 0x0f);
+ //#endif
+ if (btn_status_last != btn_status){
+ if(btn_status){
+ btn_status_last = btn_status;
+ ssd2533_ts_send_keyevent(ssl_priv,btn_status, 1);
+ dbg("send %x btn_status_last%d \n", btn_status,btn_status_last);
+ }
+ else{
+ ssd2533_ts_send_keyevent(ssl_priv,btn_status_last, 0);
+ btn_status_last = 0;
+ dbg("btn_status_last %x \n", btn_status_last);
+ }
+ return ;
+ }
+ #endif
+
+ Ssd_Timer = 0;
+ if(ic_flag == IC_SSD2533){
+ if(!Ssd_Timer_flag){
+ Ssd_Timer = ReadRegister(TIMESTAMP_REG,2);
+ if(!Ssd_Timer1){
+ Ssd_Timer1 = Ssd_Timer/1000;
+ }
+
+ Ssd_Timer2 = Ssd_Timer/1000;
+
+
+ if((Ssd_Timer2 - Ssd_Timer1) > 10){
+ WriteRegister(AUTO_INIT_RST_REG,0x00,0x00,1);
+ Ssd_Timer_flag = 1;
+ }
+ }
+ }
+
+ EventStatus = ReadRegister(EVENT_STATUS,2)>>4;
+ ssl_priv->FingerDetect=0;
+ for(i=0;i<ssl_priv->FingerNo;i++){
+ if((EventStatus>>i)&0x1){
+ FingerInfo=ReadRegister(FINGER01_REG+i,4);
+ xpos = ((FingerInfo>>4)&0xF00)|((FingerInfo>>24)&0xFF);
+ ypos = ((FingerInfo>>0)&0xF00)|((FingerInfo>>16)&0xFF);
+ dbg("raw data before cut, F%d:(%d,%d)\n",i,xpos,ypos);
+ if(xpos!=0xFFF){
+ ssl_priv->FingerDetect++;
+ if (wmt_ts_get_cutedge()>=0){
+ xpos = ssd253x_ts_cut_edge(xpos, 1);
+ ypos = ssd253x_ts_cut_edge(ypos, 0);
+ }
+ }else {
+ EventStatus=EventStatus&~(1<<i);
+ clrFlag=1;
+ }
+ }else{
+ xpos=ypos=0xFFF;
+ clrFlag=1;
+ }
+ FingerX[i]=xpos;
+ FingerY[i]=ypos;
+ }
+
+ if(ssl_priv->use_irq==1) wmt_enable_gpirq();
+ if(ssl_priv->use_irq==2)
+ {
+ if(ssl_priv->FingerDetect==0)
+ {
+ wmt_enable_gpirq();
+ } else {
+ hrtimer_start(&ssl_priv->timer, ktime_set(0, MicroTimeTInterupt), HRTIMER_MODE_REL);
+ }
+ }
+ if(ic_flag == IC_SSD2533){
+ if(clrFlag) WriteRegister(EVENT_FIFO_SCLR,0x01,0x00,1);
+ }
+
+ if(ssl_priv->input->id.product==0x2533)
+ if(ssl_priv->input->id.version==0x0101)
+ FingerCheckSwap(FingerX,FingerY,FingerP,ssl_priv->FingerNo,ssl_priv->sFingerX,ssl_priv->sFingerY);
+
+ // report data
+ for(i=0;i<ssl_priv->FingerNo;i++)
+ {
+ xpos=FingerX[i];
+ ypos=FingerY[i];
+ if(ssl_priv->input->id.product==0x2533){
+ if(ssl_priv->input->id.version==0x0101) RunningAverage(&xpos,&ypos,i,ssl_priv);
+ if(ssl_priv->input->id.version==0x0102) RunningAverage(&xpos,&ypos,i,ssl_priv);
+ }
+
+ if(xpos!=0xFFF)
+ {
+ dbg("raw data after cut, F%d:(%d,%d)\n",i,xpos,ypos);
+ switch (wmt_ts_get_xaxis())
+ {
+ case 1:
+ tx = ypos;
+ break;
+ case 0:
+ default:
+ tx = xpos;
+ break;
+ }
+
+ switch (wmt_ts_get_xdir())
+ {
+ case 1:
+ break;
+ case -1:
+ tx = SSDS53X_SCREEN_MAX_Y - tx;
+ break;
+ default:
+ break;
+ };
+
+ if (tx <0){
+ tx = 0;
+ } else if (tx >= SSDS53X_SCREEN_MAX_Y){
+ tx = SSDS53X_SCREEN_MAX_Y-1;
+ }
+ switch (wmt_ts_get_yaxis())
+ {
+
+ case 0:
+ ty = xpos;
+ break;
+ case 1:
+ default:
+ ty = ypos;
+ break;
+ }
+
+ switch (wmt_ts_get_ydir())
+ {
+ case 1:
+ break;
+ case -1:
+ ty = SSDS53X_SCREEN_MAX_X - ty;
+ default:
+ break;
+ }
+
+ if (ty < 0){
+ ty = 0;
+ } else if (ty >=SSDS53X_SCREEN_MAX_X){
+ ty = SSDS53X_SCREEN_MAX_X-1;
+ }
+
+ if (wmt_ts_get_lcdexchg()) {
+ int tmp;
+ tmp = tx;
+ tx = ty;
+ ty = wmt_ts_get_resolvX() - tmp;
+ }
+
+ ssd253x_tsdev.penup = 0;
+ input_report_abs(ssl_priv->input, ABS_MT_POSITION_X, tx);
+ input_report_abs(ssl_priv->input, ABS_MT_POSITION_Y, ty);
+ /*input_report_abs(ssl_priv->input, ABS_MT_POSITION_X, ty);
+ input_report_abs(ssl_priv->input, ABS_MT_POSITION_Y, tx);*/
+ input_mt_sync(ssl_priv->input);
+ dbg("report data x=%d,y=%d\n", tx, ty);
+
+ }
+ else if(ssl_priv->FingerX[i]!=0xFFF){
+ input_mt_sync(ssl_priv->input);
+ //printk("pen up...\n");
+ ssd253x_tsdev.penup = 1;
+ }
+
+ ssl_priv->FingerX[i]=FingerX[i];
+ ssl_priv->FingerY[i]=FingerY[i];
+ }
+
+ ssl_priv->EventStatus=EventStatus;
+ input_sync(ssl_priv->input);
+ if (1 == ssd253x_tsdev.penup){
+ wake_up(&ts_penup_wait_queue);
+ }
+
+}
+
+
+#define TPIC_INT_PLLLING 0
+#define TPIC_INT_INTERUPT 1
+#define TPIC_INT_HYBRID 2
+
+
+static int ssd253x_probe(struct platform_device *pdev)
+{
+ struct ssl_ts_priv *ssl_priv;
+ struct input_dev *ssl_input;
+ int error;
+ int i;
+ //unsigned int prescale;
+
+ //#ifdef SD_INIT
+ // g_tp_client = l_client;
+ //#endif
+
+ SSDS53X_SCREEN_MAX_X = wmt_ts_get_resolvY();
+ SSDS53X_SCREEN_MAX_Y = wmt_ts_get_resolvX();
+
+ ssl_priv = kzalloc(sizeof(*ssl_priv), GFP_KERNEL);
+ if (!ssl_priv)
+ {
+ errlog(" kzalloc Error!\n");
+ error=-ENODEV;
+ goto err0;
+ }
+ l_ts = ssl_priv;
+
+ ssl_input = input_allocate_device();
+ if (!ssl_input)
+ {
+ errlog(" ssd253x_ts_probe: input_allocate_device Error\n");
+ error=-ENODEV;
+ goto freealloc;
+ }
+ ssl_input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_SYN) ;
+ set_bit(INPUT_PROP_DIRECT,ssl_input->propbit);
+ ssl_input->name = DEVICE_NAME;
+ ssl_input->id.bustype = BUS_I2C;
+ ssl_input->id.vendor = 0x2878; // Modify for Vendor ID
+
+ ssl_priv->input = ssl_input;
+
+ ssl_priv->FingerNo=wmt_ts_get_fingernum();//FINGERNO;
+ ssl_priv->Resolution=64;
+
+ for(i=0;i<ssl_priv->FingerNo;i++)
+ {
+ ssl_priv->sFingerX[i]=0xFFF;
+ ssl_priv->sFingerY[i]=0xFFF;
+
+ // For Adaptive Running Average
+ ssl_priv->pFingerX[i]=0xFFF;
+ ssl_priv->pFingerY[i]=0xFFF;
+ }
+
+ deviceReset();
+ ssl_input->id.product = ReadRegister(DEVICE_ID_REG,2);
+ ssl_input->id.version = ReadRegister(VERSION_ID_REG,2);
+ ssl_input->id.product = ReadRegister(DEVICE_ID_REG,2);
+
+ ssl_input->id.version = ReadRegister(VERSION_ID_REG,2);
+ klog("SSL Touchscreen Device ID : 0x%04X\n",ssl_input->id.product);
+ klog("SSL Touchscreen Version ID : 0x%04X\n",ssl_input->id.version);
+
+ if(ssl_input->id.product == 0x2531){
+ ic_flag = IC_SSD2531;
+ }else if(ssl_input->id.product == 0x2533) {
+ ic_flag = IC_SSD2533;
+ }else if(ssl_input->id.product == 0x2543) {
+ ic_flag = IC_SSD2543;
+ }
+
+ if(ic_flag == IC_SSD2533) {
+ ssl_priv->use_irq = TPIC_INT_HYBRID;
+ }else if(ic_flag == IC_SSD2543) {
+ ssl_priv->use_irq = TPIC_INT_INTERUPT;
+ }
+
+ SSD253xdeviceInit();
+ if(ic_flag == IC_SSD2533) {
+ WriteRegister(EVENT_FIFO_SCLR,0x01,0x00,1); // clear Event FiFo
+ }
+
+
+ if(ssl_priv->input->id.product==0x2531)
+ ssl_priv->Resolution=32;
+ else if(ssl_priv->input->id.product==0x2533)
+ ssl_priv->Resolution=64;
+
+
+ if (wmt_ts_get_lcdexchg()) {
+ input_set_abs_params(ssl_input, ABS_MT_POSITION_X, 0,wmt_ts_get_resolvY(), 0, 0);
+ input_set_abs_params(ssl_input, ABS_MT_POSITION_Y, 0,wmt_ts_get_resolvX(), 0, 0);
+ } else {
+ input_set_abs_params(ssl_input, ABS_MT_POSITION_X, 0,wmt_ts_get_resolvX(), 0, 0);
+ input_set_abs_params(ssl_input, ABS_MT_POSITION_Y, 0,wmt_ts_get_resolvY(), 0, 0);
+ }
+
+#ifdef USE_TOUCH_KEY
+ set_bit(KEY_MENU, ssl_input->keybit);
+ set_bit(KEY_HOME, ssl_input->keybit);
+ set_bit(KEY_BACK, ssl_input->keybit);
+ set_bit(KEY_SEARCH, ssl_input->keybit);
+ #endif
+ error = input_register_device(ssl_input);
+ if(error)
+ {
+ errlog("input_register_device input Error!\n");
+ error=-ENODEV;
+ goto panel_init_fail;
+ }
+ if((ssl_priv->use_irq==0)||(ssl_priv->use_irq==2))
+ {
+ hrtimer_init(&ssl_priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ssl_priv->timer.function = ssd253x_ts_timer;
+ //#ifdef CONFIG_TOUCHSCREEN_SSL_DEBUG
+ dbg(" ssd253x_ts_probe: timer_init OK!\n");
+ //#endif
+ }
+
+ ssd253x_wq = create_singlethread_workqueue("ssd253x_wq");
+ INIT_WORK(&ssl_priv->ssl_work, ssd253x_ts_work);
+ error = request_irq(wmt_get_tsirqnum(), ssd253x_ts_isr, IRQF_SHARED, "ssd253x_ts_q", l_ts);
+ if(error){
+ errlog("request_irq Error!\n");
+ error=-ENODEV;
+ goto freeque;
+ }
+
+ wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);
+ wmt_disable_gpirq();
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ssl_priv->early_suspend.suspend = ssd253x_ts_early_suspend;
+ ssl_priv->early_suspend.resume = ssd253x_ts_late_resume;
+ ssl_priv->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN+2;
+ register_early_suspend(&ssl_priv->early_suspend);
+#endif
+ deviceResume();
+ wmt_enable_gpirq();
+ dbg("SSD253X init ok!\n");
+ return 0;
+
+freeque:
+ destroy_workqueue(ssd253x_wq);
+ input_unregister_device(ssl_input);
+panel_init_fail:
+ input_free_device(ssl_input);
+freealloc:
+ kfree(ssl_priv);
+err0:
+ //dev_set_drvdata(&client->dev, NULL);
+ return error;
+}
+
+static int ssd253x_remove(struct platform_device *pdev)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ if((ssl_priv->use_irq==0)||(ssl_priv->use_irq==2)) hrtimer_cancel(&ssl_priv->timer);
+
+ //disable int
+ wmt_disable_gpirq();
+ //free irq
+ free_irq(wmt_get_tsirqnum(), l_ts);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ssl_priv->early_suspend);
+#endif
+ // free queue
+ cancel_work_sync (&ssl_priv->ssl_work);
+ flush_workqueue(ssd253x_wq);
+ destroy_workqueue(ssd253x_wq);
+ input_unregister_device(ssl_priv->input);
+ input_free_device(ssl_priv->input);
+ kfree(ssl_priv);
+ l_ts = NULL;
+ return 0;
+}
+
+
+/*
+static int ssd253x_ts_open(struct input_dev *dev)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ deviceResume();
+ if(ssl_priv->use_irq)
+ {
+ wmt_enable_gpirq(); //(ssl_priv->irq);
+ } else {
+ hrtimer_start(&ssl_priv->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+ return 0;
+}
+
+
+static void ssd253x_ts_close(struct input_dev *dev)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ // disable interrupt
+ deviceSuspend();
+ if((ssl_priv->use_irq==0)||(ssl_priv->use_irq==2))
+ hrtimer_cancel(&ssl_priv->timer);
+ if((ssl_priv->use_irq==1)||(ssl_priv->use_irq==2))
+ wmt_disable_gpirq();//(ssl_priv->irq);
+}
+*/
+static int ssd253x_resume(struct platform_device *pdev)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ wmt_disable_gpirq();
+ Ssd_Timer_flag = 0;
+ deviceReset();
+ SSD253xdeviceInit();
+ if(ic_flag == IC_SSD2533){
+ WriteRegister(EVENT_FIFO_SCLR,0x01,0x00,1); // clear Event FiFo
+ }
+ deviceResume();
+ wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);
+ wmt_enable_gpirq();
+
+ if(! ssl_priv->use_irq)
+ {
+ hrtimer_start(&ssl_priv->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+ ssl_priv->earlysus = 0;
+ return 0;
+}
+
+static int ssd253x_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ if((ssl_priv->use_irq==0)||(ssl_priv->use_irq==2)) hrtimer_cancel(&ssl_priv->timer);
+ // disable irq
+ wmt_disable_gpirq();
+ Ssd_Timer_flag = 0;
+ if(ic_flag == IC_SSD2533){
+ deviceSuspend();
+ }else if(ic_flag == IC_SSD2543){
+ deviceReset();
+ }
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ssd253x_ts_late_resume(struct early_suspend *h)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ dbg("...\n");
+ if (ssl_priv->earlysus != 0)
+ {
+ wmt_disable_gpirq();
+ Ssd_Timer_flag = 0;
+ deviceReset();
+ SSD253xdeviceInit();
+ WriteRegister(EVENT_FIFO_SCLR,0x01,0x00,1); // clear Event FiFo
+ deviceResume();
+ wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);
+ wmt_enable_gpirq();
+
+ if(! ssl_priv->use_irq)
+ {
+ hrtimer_start(&ssl_priv->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+ ssl_priv->earlysus = 0;
+ }
+}
+static void ssd253x_ts_early_suspend(struct early_suspend *h)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ ssl_priv->earlysus = 1;
+ if((ssl_priv->use_irq==0)||(ssl_priv->use_irq==2)) hrtimer_cancel(&ssl_priv->timer);
+ // disable irq
+ wmt_disable_gpirq();
+ Ssd_Timer_flag = 0;
+ deviceSuspend();
+
+ return;
+}
+#endif
+
+
+static irqreturn_t ssd253x_ts_isr(int irq, void *dev_id)
+{
+ struct ssl_ts_priv *ssl_priv = l_ts;
+
+ if (wmt_is_tsint())
+ {
+ wmt_clr_int();
+ if (wmt_is_tsirq_enable())
+ {
+ wmt_disable_gpirq();
+ dbg("begin..\n");
+ if(!ssl_priv->earlysus)
+ {
+ queue_work(ssd253x_wq, &ssl_priv->ssl_work);
+ }
+ }
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static enum hrtimer_restart ssd253x_ts_timer(struct hrtimer *timer)
+{
+ struct ssl_ts_priv *ssl_priv = container_of(timer, struct ssl_ts_priv, timer);
+ #ifdef CONFIG_TOUCHSCREEN_SSL_DEBUG
+ printk("+-----------------------------------------+\n");
+ printk("| ssd253x_ts_timer! |\n");
+ printk("+-----------------------------------------+\n");
+ #endif
+ queue_work(ssd253x_wq, &ssl_priv->ssl_work);
+ if(ssl_priv->use_irq==0) hrtimer_start(&ssl_priv->timer, ktime_set(0, MicroTimeTInterupt), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+#ifdef SD_INIT
+static const struct file_operations tp_fops = {
+ .owner = THIS_MODULE,
+ .read = tp_read,
+ .write = tp_write,
+ .unlocked_ioctl = tp_ioctl,
+ .open = tp_open,
+ .release = tp_release,
+};
+
+static struct miscdevice misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = TP_CHR,
+ .fops = &tp_fops,
+};
+#endif
+
+
+static int ssd253x_init(void)
+{
+ char firmwname[60];
+ int i;
+
+ if (deviceReset() != 0)
+ return -1;
+ memset(firmwname,0,sizeof(firmwname));
+ wmt_ts_get_firmwname(firmwname);
+ i = read_firmwarefile(firmwname,&ssd253xcfgTable,0x100);
+ if (i <= 0)
+ {
+ l_cfglen = sizeof(ssd253xcfgTable_default)/sizeof(ssd253xcfgTable_default[0]);
+ ssd253xcfgTable = ssd253xcfgTable_default;
+ dbg("Using the default configure!\n");
+ } else {
+ l_cfglen = i;
+ }
+ Resume[1].No = ssd253xcfgTable[l_cfglen-1].No;
+ Resume[1].Reg = ssd253xcfgTable[l_cfglen-1].Reg;
+ Resume[1].Data1 = ssd253xcfgTable[l_cfglen-1].Data1;
+ Resume[1].Data2 = ssd253xcfgTable[l_cfglen-1].Data2;
+ if (SSD253xdeviceInit()!= 0)
+ {
+ if (i > 0)
+ {
+ kfree(ssd253xcfgTable);
+ }
+ return -1;
+ }
+ // init hardware
+
+#ifdef SD_INIT
+ misc_register(&misc);
+#endif
+
+
+ return 0;
+}
+
+static void ssd253x_exit(void)
+{
+ klog("remove the module\n");
+
+#ifdef SD_INIT
+ misc_deregister(&misc);
+#endif
+
+
+ if (ssd253xcfgTable != ssd253xcfgTable_default)
+ {
+ kfree(ssd253xcfgTable);
+ }
+
+}
+
+static int ssd253x_wait_penup(struct wmtts_device*tsdev)
+{
+ int ret = wait_event_interruptible(
+ ts_penup_wait_queue,
+ (1==tsdev->penup));
+ return ret;
+}
+
+
+struct wmtts_device raysen_tsdev = {
+ .driver_name = "ssd253x_ts",
+ .ts_id = "SSD253X",
+ .init = ssd253x_init,
+ .exit = ssd253x_exit,
+ .probe = ssd253x_probe,
+ .remove = ssd253x_remove,
+ .suspend = ssd253x_suspend,
+ .resume = ssd253x_resume,
+ .wait_penup = ssd253x_wait_penup,
+ .penup = 1,
+};
+
+#ifdef SD_INIT
+static long tp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static int tp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+static int tp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t tp_read(struct file *file, char __user *buf, size_t count,loff_t *offset)
+{
+ char *kbuf;
+ uint8_t reg;
+ int ByteNo;
+ int readValue;
+ int i;
+
+ kbuf = kmalloc(count,GFP_KERNEL);
+
+ if(copy_from_user(kbuf,buf,1)) {
+ printk("no enough memory!\n");
+ return -1;
+ }
+
+ reg = (uint8_t)kbuf[0];
+ ByteNo = count;
+
+ readValue = ReadRegister( /*g_tp_client, */reg, ByteNo);
+
+ for(i = 0;i < ByteNo;i++){
+ kbuf[i] = (readValue>>(8*i)) & 0xff;
+ }
+
+ if(copy_to_user(buf,kbuf,count)) {
+ printk("no enough memory!\n");
+ return -1;
+ }
+
+ kfree(kbuf);
+
+ return count;
+}
+
+static ssize_t tp_write(struct file *file, const char __user *buf,size_t count, loff_t *offset)
+{
+ char *kbuf;
+
+ kbuf = kmalloc(count,GFP_KERNEL);
+
+ if(copy_from_user(kbuf,buf,count)) {
+ printk("no enough memory!\n");
+ return -1;
+ }
+
+ if(kbuf[1] == 0x01){
+ wmt_rst_output(0);
+ mdelay(5);
+ wmt_rst_output(1);
+ mdelay(20);
+ }
+ else
+ {
+ WriteRegister(/*g_tp_client,*/kbuf[1],kbuf[2],kbuf[3],kbuf[0]);
+ }
+
+ kfree(kbuf);
+
+ return count;
+}
+
+#endif
+
+
+
+
+MODULE_AUTHOR("Solomon Systech Ltd - Design Technology, Icarus Choi");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ssd253x Touchscreen Driver 1.3");
diff --git a/drivers/input/touchscreen/ssd253x_ts/ssd253x-ts.h b/drivers/input/touchscreen/ssd253x_ts/ssd253x-ts.h
new file mode 100755
index 00000000..fe6edeae
--- /dev/null
+++ b/drivers/input/touchscreen/ssd253x_ts/ssd253x-ts.h
@@ -0,0 +1,28 @@
+/**************************************************************
+ʹÓÃÇ°×¢ÒâͨµÀÊý£¬Çý¶¯Ä¬ÈÏʹÓÃͨµÀÊÇsense
+´óÓÚdrive·ñÔòÐèÒª½«Ê¹Óõ½µÄDRIVENOÓëSENSENOµ÷»»
+´ËÇé¿ö°üÀ¨0x66ºÍ0x67¼Ä´æÆ÷£¬µ«²»±ØÐ޸ġ£
+***************************************************************/
+#ifndef __SSD253X_20125181742_TS_H__
+#define __SSD253X_20125181742_TS_H__
+#define DRIVENO 15
+#define SENSENO 10
+#define EdgeDisable 1 // if Edge Disable, set it to 1, else reset to 0
+#define RunningAverageMode 2 //{0,8},{5,3},{6,2},{7,1}
+#define RunningAverageDist 4 // Threshold Between two consecutive points
+#define MicroTimeTInterupt 10000000 //20000000// 100Hz - 10,000,000us
+#define FINGERNO 10
+
+//#define USE_TOUCH_KEY
+
+#define USE_CUT_EDGE //0x8b must be 0x00; EdgeDisable set 0
+//#undef USE_CUT_EDGE
+
+#ifdef USE_CUT_EDGE
+ #define XPOS_MAX 576 //(DRIVENO - EdgeDisable) *64
+ #define YPOS_MAX 896 //(SENSENO - EdgeDisable) *64
+#endif
+
+
+
+#endif
diff --git a/drivers/input/touchscreen/ssd253x_ts/wmt_ts.c b/drivers/input/touchscreen/ssd253x_ts/wmt_ts.c
new file mode 100755
index 00000000..cf63ca13
--- /dev/null
+++ b/drivers/input/touchscreen/ssd253x_ts/wmt_ts.c
@@ -0,0 +1,810 @@
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+//#include <asm/semaphore.h>
+#include <linux/proc_fs.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+
+
+#include "wmt_ts.h"
+#include "ssd253x-ts.h"
+
+/////////////////////////////////////////////////////////////////
+
+// commands for ui
+#define TS_IOC_MAGIC 't'
+
+#define TS_IOCTL_CAL_START _IO(TS_IOC_MAGIC, 1)
+#define TS_IOCTL_CAL_DONE _IOW(TS_IOC_MAGIC, 2, int*)
+#define TS_IOCTL_GET_RAWDATA _IOR(TS_IOC_MAGIC, 3, int*)
+#define TS_IOCTL_CAL_QUIT _IOW(TS_IOC_MAGIC, 4, int*)
+#define TS_IOCTL_AUTO_CALIBRATION _IOW(TS_IOC_MAGIC, 5, int*)
+#define TS_IOC_MAXNR 5
+
+//
+#define TS_MAJOR 11
+#define TS_DRIVER_NAME "wmtts_touch"
+#define TS_NAME "wmtts"
+#define WMTTS_PROC_NAME "wmtts_config"
+
+#define EXT_GPIO0 0
+#define EXT_GPIO1 1
+#define EXT_GPIO2 2
+#define EXT_GPIO3 3
+#define EXT_GPIO4 4
+#define EXT_GPIO5 5
+#define EXT_GPIO6 6
+#define EXT_GPIO7 7
+
+typedef struct {
+ int a1;
+ int b1;
+ int c1;
+ int a2;
+ int b2;
+ int c2;
+ int delta;
+}CALIBRATION_PARAMETER, *PCALIBRATION_PARAMETER;
+
+
+static int lcd_exchg = 0;
+static int irq_gpio;
+static int rst_gpio;
+static int panelres_x;
+static int panelres_y;
+static int l_xaxis=0;
+static int l_xdirect=1;
+static int l_yaxis=1;
+static int l_ydirect=1;
+static int l_cutedge=-1;
+static DECLARE_WAIT_QUEUE_HEAD(queue);
+static CALIBRATION_PARAMETER g_CalcParam;
+static TS_EVENT g_evLast;
+static struct mutex cal_mutex;
+static DECLARE_WAIT_QUEUE_HEAD(ts_penup_wait_queue);
+
+extern struct wmtts_device raysen_tsdev;
+static struct wmtts_device* l_tsdev = &raysen_tsdev;
+static struct i2c_client *l_client=NULL;
+static int l_penup = 0; // 1-pen up,0-pen down
+static char l_firmid[21];
+
+/////////////////////////////////////////////////////
+// function declare
+/////////////////////////////////////////////////////
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+
+///////////////////////////////////////////////////////////////////////
+void TouchPanelCalibrateAPoint(
+ int UncalX, //@PARM The uncalibrated X coordinate
+ int UncalY, //@PARM The uncalibrated Y coordinate
+ int *pCalX, //@PARM The calibrated X coordinate
+ int *pCalY //@PARM The calibrated Y coordinate
+ )
+{
+ int x, y;
+ mutex_lock(&cal_mutex);
+ x = (g_CalcParam.a1 * UncalX + g_CalcParam.b1 * UncalY +
+ g_CalcParam.c1) / g_CalcParam.delta;
+ y = (g_CalcParam.a2 * UncalX + g_CalcParam.b2 * UncalY +
+ g_CalcParam.c2) / g_CalcParam.delta;
+
+//klog("afer(%d,%d)(%d,%d)\n", x,y,panelres_x,panelres_y);
+ if ( x < 0 )
+ x = 0;
+
+ if ( y < 0 )
+ y = 0;
+ if (x >= panelres_x)
+ x = panelres_x-1;
+ if (y >= panelres_y)
+ y = panelres_y-1;
+
+ *pCalX = x;
+ *pCalY = y;
+ mutex_unlock(&cal_mutex);
+ return;
+}
+
+static int parse_firmwarefile(const char* filedata, struct ChipSetting** firmarr, int maxlen)
+{
+ char endflag[]="/* End flag */";
+ const char* p = filedata;
+ int val[4];
+ int i = 0;
+ int j = 0;
+ const char* s = NULL;
+
+ // the first {
+ while (*p!='{') p++;
+ p++;
+ s = p;
+ // calculate the number of array
+ while (strncmp(p,endflag,strlen(endflag)))
+ {
+ if (*p=='{')
+ {
+ i++;
+ }
+ p++;
+ };
+ dbg("the number of arry:0x%x\n", i);
+ // alloc the memory for array
+ *firmarr = kzalloc(sizeof(struct ChipSetting)*i, GFP_KERNEL);
+ // parse the value of array
+ p = s;
+ j = 0;
+ while (strncmp(p,endflag,strlen(endflag)))
+ {
+ if (*p=='{')
+ {
+ memset(val,0,sizeof(val));
+ sscanf(p,"{%x,%x,%x,%x}",val,val+1,val+2,val+3);
+ (*firmarr)[j].No = val[0]&0x00FF;
+ (*firmarr)[j].Reg = val[1]&0x00FF;
+ (*firmarr)[j].Data1 = val[2]&0x00FF;
+ (*firmarr)[j].Data2 = val[3]&0x00FF;
+ dbg("arry[0x%x]:%x,%x,%x,%x\n",j,(*firmarr)[j].No,(*firmarr)[j].Reg,(*firmarr)[j].Data1,
+ (*firmarr)[j].Data2);
+ j++;
+ }
+ //p = strchr(p,'}');
+ p++;
+ if (j>=i-2)
+ {
+ dbg("%s",p);
+ }
+
+ };
+ if (i != j)
+ {
+ errlog("Error parsing file(the number of arry not match)!\n");
+ return -1;
+ };
+ dbg("paring firmware file end.\n");
+ return i;
+}
+
+
+static struct device* get_tp_device(void){
+ if(l_client == NULL){
+ errlog("l_client is NULL\n");
+ }
+ return &l_client->dev;
+}
+
+
+//filepath:the path of firmware file;
+//firmdata:store the data from firmware file;
+//maxlen: the max len of firmdata;
+//return:the number of firmware data,negative-parsing error.
+int read_firmwarefile(char* filepath, struct ChipSetting** firmdata, int maxlen)
+{
+ const u8 *data = NULL;
+ int i = 0;
+ int ret = -1;
+ const struct firmware* tpfirmware = NULL;
+
+ klog("ts config file:%s\n",filepath);
+
+
+ ret = request_firmware(&tpfirmware, filepath, get_tp_device());
+ if (ret < 0) {
+ errlog("Failed load tp firmware: %s ret=%d\n", filepath,ret);
+ goto err_end;
+ }
+
+ data = tpfirmware->data;
+
+ i = parse_firmwarefile(data,firmdata,maxlen);
+ if (i <= 0)
+ {
+ errlog("error to parse firmware file.\n");
+ ret = -1;
+ goto error_parse_fw;
+ }
+ ret = i;
+
+
+ dbg("success to read firmware file!\n");;
+
+error_parse_fw:
+ if(tpfirmware){
+ release_firmware(tpfirmware);
+ tpfirmware = NULL;
+ }
+err_end:
+ return ret;
+}
+
+
+ int wmt_ts_get_gpionum(void)
+{
+ return irq_gpio;
+}
+
+int wmt_ts_get_resetgpnum(void)
+{
+ return rst_gpio;
+}
+
+int wmt_ts_get_lcdexchg(void)
+{
+ return lcd_exchg;
+}
+
+int wmt_ts_get_resolvX(void)
+{
+ return panelres_x;
+}
+
+int wmt_ts_get_resolvY(void)
+{
+ return panelres_y;
+}
+
+int wmt_ts_get_xaxis(void)
+{
+ return l_xaxis;
+}
+
+int wmt_ts_get_xdir(void)
+{
+ return l_xdirect;
+}
+
+int wmt_ts_get_yaxis(void)
+{
+ return l_yaxis;
+}
+
+int wmt_ts_get_ydir(void)
+{
+ return l_ydirect;
+}
+
+int wmt_ts_get_cutedge(void)
+{
+ return l_cutedge;
+}
+
+void wmt_ts_get_firmwname(char* firmname)
+{
+ sprintf(firmname,"ssd253x_%s_cfg.tpf",l_firmid);
+}
+
+int wmt_ts_get_fingernum(void)
+{
+ if (!strcmp(l_firmid,"10rs10f1609043psy1"))
+ {
+ return 10;
+ }
+ return 5;
+}
+
+//up:1-pen up,0-pen down
+void wmt_ts_set_penup(int up)
+{
+ l_penup = up;
+}
+
+//
+int wmt_ts_wait_penup(void)
+{
+ int ret = wait_event_interruptible(
+ ts_penup_wait_queue,
+ (1==l_penup));
+ return ret;
+}
+
+// return:1-pen up,0-pen dwon
+int wmt_ts_ispenup(void)
+{
+ return l_penup;
+}
+
+
+void wmt_ts_wakeup_penup(void)
+{
+ wake_up(&ts_penup_wait_queue);
+}
+
+int wmt_is_tsirq_enable(void)
+{
+ int val = 0;
+ int num = irq_gpio;
+
+ if(num > 11)
+ return 0;
+
+ if(num<4)
+ val = REG32_VAL(__GPIO_BASE+0x0300) & (1<<(num*8+7));
+ else if(num >= 4 && num < 8)
+ val = REG32_VAL(__GPIO_BASE+0x0304) & (1<<((num-4)*8+7));
+ else
+ val = REG32_VAL(__GPIO_BASE+0x0308) & (1<<((num-8)*8+7));
+
+ return val?1:0;
+
+}
+
+int wmt_is_tsint(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return 0;
+ }
+ return (REG32_VAL(__GPIO_BASE+0x0360) & (1<<num)) ? 1: 0;
+}
+
+void wmt_clr_int(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return;
+ }
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num;
+}
+
+void wmt_tsreset_init(void)
+{
+ int num = rst_gpio;
+
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num);//&= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<num); // out low
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<num); //output enable
+ msleep(10);
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<num); // out high
+}
+
+// enable:0-disable,1-enable
+void wmt_enable_rst_pull(int enable)
+{
+ if (enable)
+ {
+ REG32_VAL(__GPIO_BASE+0x0480) |= (1<<rst_gpio); //enable pull up/down
+ } else {
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<rst_gpio); //disable pull up/down
+ }
+}
+
+// up:0-pull down,1-pull up
+void wmt_set_rst_pull(int up)
+{
+ if (up)
+ {
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<rst_gpio); //pull up
+ } else {
+ REG32_VAL(__GPIO_BASE+0x04c0) &= ~(1<<rst_gpio); //pull down
+ }
+}
+
+// high:0-low level,1-high level
+void wmt_rst_output(int high)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ if (high)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<rst_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<rst_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<rst_gpio); //set output
+}
+
+void wmt_rst_input(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<rst_gpio); //set input
+}
+
+void wmt_set_intasgp(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<irq_gpio); //enable gpio
+}
+
+// val:1--high,0-low
+void wmt_intgp_out(int val)
+{
+ if (val)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<irq_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<irq_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<irq_gpio); //set output
+}
+
+void wmt_ts_set_irqinput(void)
+{
+ int num = irq_gpio;
+
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+}
+
+unsigned int wmt_ts_irqinval(void)
+{
+ return REG32_VAL(__GPIO_BASE+0x0000)&(1<<irq_gpio);
+}
+
+int wmt_set_gpirq(int type)
+{
+ int shift;
+ int offset;
+ unsigned long reg;
+ int num = irq_gpio;
+
+ if(num >11)
+ return -1;
+ //if (num > 9)
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; // gpio10,11 as gpio
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull down
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down
+
+ //set gpio irq triger type
+ if(num < 4){//[0,3]
+ shift = num;
+ offset = 0x0300;
+ }else if(num >= 4 && num < 8){//[4,7]
+ shift = num-4;
+ offset = 0x0304;
+ }else{// [8,11]
+ shift = num-8;
+ offset = 0x0308;
+ }
+
+ reg = REG32_VAL(__GPIO_BASE + offset);
+
+ switch(type){
+ case IRQ_TYPE_LEVEL_LOW:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ default://both edge
+ reg |= (1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+
+ }
+ //reg |= 1<<(shift*8+7);//enable interrupt
+ reg &= ~(1<<(shift*8+7)); //disable int
+
+ REG32_VAL(__GPIO_BASE + offset) = reg;
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num; //clear interrupt status
+ msleep(5);
+ return 0;
+}
+
+int wmt_enable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) |= 1<<(num*8+7); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) |= 1<<((num-4)*8+7); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) |= 1<<((num-8)*8+7); //enable interrupt
+
+ return 0;
+}
+
+int wmt_disable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) &= ~(1<<(num*8+7)); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) &= ~(1<<((num-4)*8+7)); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) &= ~(1<<((num-8)*8+7)); //enable interrupt
+
+ return 0;
+}
+
+
+int wmt_get_tsirqnum(void)
+{
+ return IRQ_GPIO;
+}
+
+
+int wmt_ts_set_rawcoord(unsigned short x, unsigned short y)
+{
+ g_evLast.x = x;
+ g_evLast.y = y;
+ //dbg("raw(%d,%d)*\n", x, y);
+ return 0;
+}
+
+static void wmt_ts_platform_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device wmt_ts_plt_device = {
+ .name = TS_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = wmt_ts_platform_release,
+ },
+// .num_resources = ARRAY_SIZE(wm9715_ts_resources),
+// .resource = wm9715_ts_resources,
+};
+
+static int wmt_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dbg("ts suspend....\n");
+ return l_tsdev->suspend(pdev, state);
+}
+static int wmt_ts_resume(struct platform_device *pdev)
+{
+ dbg("ts resume....\n");
+ return l_tsdev->resume(pdev);
+}
+
+static int wmt_ts_probe(struct platform_device *pdev)
+{
+
+ if (l_tsdev->probe != NULL)
+ return l_tsdev->probe(pdev);
+ else
+ return 0;
+}
+
+static int wmt_ts_remove(struct platform_device *pdev)
+{
+
+ if (l_tsdev->remove != NULL)
+ return l_tsdev->remove(pdev);
+ else
+ return 0;
+}
+
+static struct platform_driver wmt_ts_plt_driver = {
+ .driver = {
+ .name = TS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = wmt_ts_probe,
+ .remove = wmt_ts_remove,
+ .suspend = wmt_ts_suspend,
+ .resume = wmt_ts_resume,
+};
+
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 150;
+ char retval[150] = {0},*p=NULL;
+ int Enable=0,Gpio=0,PX=0,PY=0;
+ char* s=NULL;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ errlog("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ sscanf(retval,"%d:",&Enable);
+ //check touch enable
+ if(Enable == 0){
+ errlog("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(retval,':');
+ p++;
+ if(strncmp(p, l_tsdev->ts_id,strlen(l_tsdev->ts_id))){//check touch ID
+ errlog("[WMTENV] %s is not found\n", l_tsdev->ts_id);
+ return -ENODEV;
+ }
+ // get firmwareid
+ s = p+strlen(l_tsdev->ts_id)+1; //point to firmware id
+ p = strchr(p,':');
+ memset(l_firmid,0,sizeof(l_firmid));
+ len = p-s;
+ if (len>=20)
+ {
+ len = 19;
+ }
+ strncpy(l_firmid,s,len);
+ p++;
+ sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%d",&Gpio,&PX,&PY,&rst_gpio,
+ &l_xaxis,&l_xdirect,
+ &l_yaxis,&l_ydirect,
+ &l_cutedge);
+
+ irq_gpio = Gpio;
+ panelres_x = PX;
+ panelres_y = PY;
+ dbg("p.x=%d,p.y=%d,gpio=%d,resetgpio=%d,\nx-axis=%d,x_dir=%d,y-axis=%d,y_dir=%d,cutedge=%d\nfirmwareid:%s\n",
+ panelres_x, panelres_y, irq_gpio, rst_gpio,
+ l_xaxis,l_xdirect,l_yaxis,l_ydirect,l_cutedge,
+ l_firmid);
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = WMT_TS_I2C_NAME,
+ .flags = 0x00,
+ .addr = WMT_TS_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(1);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+struct i2c_client* ts_get_i2c_client(void)
+{
+ return l_client;
+}
+
+static int __init wmt_ts_init(void)
+{
+ int ret = 0;
+
+ if(wmt_check_touch_env())
+ return -ENODEV;
+
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+ mutex_init(&cal_mutex);
+
+ if (l_tsdev->init() < 0){
+ dbg("Errors to init %s ts IC!!!\n", l_tsdev->ts_id);
+ ret = -1;
+ goto err_init;
+ }
+ // Create device node
+/* if (register_chrdev (TS_MAJOR, TS_NAME, &wmt_ts_fops)) {
+ printk (KERN_ERR "wmt touch: unable to get major %d\n", TS_MAJOR);
+ return -EIO;
+ }
+
+ l_dev_class = class_create(THIS_MODULE, TS_NAME);
+ if (IS_ERR(l_dev_class)){
+ ret = PTR_ERR(l_dev_class);
+ printk(KERN_ERR "Can't class_create touch device !!\n");
+ return ret;
+ }
+ l_clsdevice = device_create(l_dev_class, NULL, MKDEV(TS_MAJOR, 0), NULL, TS_NAME);
+ if (IS_ERR(l_clsdevice)){
+ ret = PTR_ERR(l_clsdevice);
+ printk(KERN_ERR "Failed to create device %s !!!",TS_NAME);
+ return ret;
+ }
+*/
+ // register device and driver of platform
+ ret = platform_device_register(&wmt_ts_plt_device);
+ if(ret){
+ errlog("wmt ts plat device register failed!\n");
+ return ret;
+ }
+ ret = platform_driver_register(&wmt_ts_plt_driver);
+ if(ret){
+ errlog("can not register platform_driver_register\n");
+ platform_device_unregister(&wmt_ts_plt_device);
+ return ret;
+ }
+
+ klog("%s driver init ok!\n",l_tsdev->ts_id);
+ return 0;
+err_init:
+ ts_i2c_unregister_device();
+ return ret;
+}
+
+static void __exit wmt_ts_exit(void)
+{
+ dbg("%s\n",__FUNCTION__);
+
+ l_tsdev->exit();
+ platform_driver_unregister(&wmt_ts_plt_driver);
+ platform_device_unregister(&wmt_ts_plt_device);
+ //device_destroy(l_dev_class, MKDEV(TS_MAJOR, 0));
+ //unregister_chrdev(TS_MAJOR, TS_NAME);
+ //class_destroy(l_dev_class);
+ mutex_destroy(&cal_mutex);
+ ts_i2c_unregister_device();
+}
+
+
+module_init(wmt_ts_init);
+module_exit(wmt_ts_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/ssd253x_ts/wmt_ts.h b/drivers/input/touchscreen/ssd253x_ts/wmt_ts.h
new file mode 100755
index 00000000..3506773a
--- /dev/null
+++ b/drivers/input/touchscreen/ssd253x_ts/wmt_ts.h
@@ -0,0 +1,116 @@
+
+#ifndef WMT_TSH_201010191758
+#define WMT_TSH_201010191758
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+
+
+//#define DEBUG_WMT_TS
+#undef dbg
+#ifdef DEBUG_WMT_TS
+#define dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args)
+#else
+#define dbg(fmt, args...)
+#endif
+
+#undef errlog
+#undef klog
+#define errlog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+#define klog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+
+#define WMT_TS_I2C_NAME "ssd253x-ts"
+#define WMT_TS_I2C_ADDR 0x01
+
+//////////////////////////////data type///////////////////////////
+typedef struct {
+ short pressure;
+ short x;
+ short y;
+ //short millisecs;
+} TS_EVENT;
+
+struct wmtts_device
+{
+ //data
+ char* driver_name;
+ char* ts_id;
+ //function
+ int (*init)(void);
+ int (*probe)(struct platform_device *platdev);
+ int (*remove)(struct platform_device *pdev);
+ void (*exit)(void);
+ int (*suspend)(struct platform_device *pdev, pm_message_t state);
+ int (*resume)(struct platform_device *pdev);
+ int (*capacitance_calibrate)(void);
+ int (*wait_penup)(struct wmtts_device*tsdev); // waiting untill penup
+ int penup; // 0--pendown;1--penup
+
+};
+
+struct ChipSetting {
+ char No;
+ char Reg;
+ char Data1;
+ char Data2;
+};
+
+
+//////////////////////////function interface/////////////////////////
+extern int wmt_ts_get_gpionum(void);
+extern int wmt_ts_iscalibrating(void);
+extern int wmt_ts_get_resolvX(void);
+extern int wmt_ts_get_resolvY(void);
+extern int wmt_ts_set_rawcoord(unsigned short x, unsigned short y);
+extern int wmt_set_gpirq(int type);
+extern int wmt_get_tsirqnum(void);
+extern int wmt_disable_gpirq(void);
+extern int wmt_enable_gpirq(void);
+extern int wmt_is_tsirq_enable(void);
+extern int wmt_is_tsint(void);
+extern void wmt_clr_int(void);
+extern void wmt_tsreset_init(void);
+extern int wmt_ts_get_resetgpnum(void);
+extern int wmt_ts_get_lcdexchg(void);
+extern void wmt_enable_rst_pull(int enable);
+extern void wmt_set_rst_pull(int up);
+extern void wmt_rst_output(int high);
+extern void wmt_rst_input(void);
+extern void wmt_set_intasgp(void);
+extern void wmt_intgp_out(int val);
+extern void wmt_ts_set_irqinput(void);
+extern unsigned int wmt_ts_irqinval(void);
+extern void wmt_ts_set_penup(int up);
+extern int wmt_ts_wait_penup(void);
+extern void wmt_ts_wakeup_penup(void);
+extern struct i2c_client* ts_get_i2c_client(void);
+extern int wmt_ts_ispenup(void);
+extern int wmt_ts_get_xaxis(void);
+extern int wmt_ts_get_xdir(void);
+extern int wmt_ts_get_yaxis(void);
+extern int wmt_ts_get_ydir(void);
+extern int wmt_ts_get_cutedge(void);
+extern void wmt_ts_get_firmwname(char* firmname);
+extern int wmt_ts_get_fingernum(void);
+
+extern void TouchPanelCalibrateAPoint(
+ int UncalX, //@PARM The uncalibrated X coordinate
+ int UncalY, //@PARM The uncalibrated Y coordinate
+ int *pCalX, //@PARM The calibrated X coordinate
+ int *pCalY //@PARM The calibrated Y coordinate
+ );
+
+//filepath:the path of firmware file;
+//firmdata:store the data from firmware file;
+//maxlen: the max len of firmdata;
+//return:the number of firmware data,negative-parsing error.
+extern int read_firmwarefile(char* filepath, struct ChipSetting** firmdata, int maxlen);
+
+#endif
+
+
+
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
new file mode 100644
index 00000000..cbbf71b2
--- /dev/null
+++ b/drivers/input/touchscreen/st1232.c
@@ -0,0 +1,275 @@
+/*
+ * ST1232 Touchscreen Controller Driver
+ *
+ * Copyright (C) 2010 Renesas Solutions Corp.
+ * Tony SIM <chinyeow.sim.xt@renesas.com>
+ *
+ * Using code from:
+ * - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define ST1232_TS_NAME "st1232-ts"
+
+#define MIN_X 0x00
+#define MIN_Y 0x00
+#define MAX_X 0x31f /* (800 - 1) */
+#define MAX_Y 0x1df /* (480 - 1) */
+#define MAX_AREA 0xff
+#define MAX_FINGERS 2
+
+struct st1232_ts_finger {
+ u16 x;
+ u16 y;
+ u8 t;
+ bool is_valid;
+};
+
+struct st1232_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct st1232_ts_finger finger[MAX_FINGERS];
+ struct dev_pm_qos_request low_latency_req;
+};
+
+static int st1232_ts_read_data(struct st1232_ts_data *ts)
+{
+ struct st1232_ts_finger *finger = ts->finger;
+ struct i2c_client *client = ts->client;
+ struct i2c_msg msg[2];
+ int error;
+ u8 start_reg;
+ u8 buf[10];
+
+ /* read touchscreen data from ST1232 */
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &start_reg;
+ start_reg = 0x10;
+
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(buf);
+ msg[1].buf = buf;
+
+ error = i2c_transfer(client->adapter, msg, 2);
+ if (error < 0)
+ return error;
+
+ /* get "valid" bits */
+ finger[0].is_valid = buf[2] >> 7;
+ finger[1].is_valid = buf[5] >> 7;
+
+ /* get xy coordinate */
+ if (finger[0].is_valid) {
+ finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
+ finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
+ finger[0].t = buf[8];
+ }
+
+ if (finger[1].is_valid) {
+ finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
+ finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
+ finger[1].t = buf[9];
+ }
+
+ return 0;
+}
+
+static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
+{
+ struct st1232_ts_data *ts = dev_id;
+ struct st1232_ts_finger *finger = ts->finger;
+ struct input_dev *input_dev = ts->input_dev;
+ int count = 0;
+ int i, ret;
+
+ ret = st1232_ts_read_data(ts);
+ if (ret < 0)
+ goto end;
+
+ /* multi touch protocol */
+ for (i = 0; i < MAX_FINGERS; i++) {
+ if (!finger[i].is_valid)
+ continue;
+
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
+ input_mt_sync(input_dev);
+ count++;
+ }
+
+ /* SYN_MT_REPORT only if no contact */
+ if (!count) {
+ input_mt_sync(input_dev);
+ if (ts->low_latency_req.dev) {
+ dev_pm_qos_remove_request(&ts->low_latency_req);
+ ts->low_latency_req.dev = NULL;
+ }
+ } else if (!ts->low_latency_req.dev) {
+ /* First contact, request 100 us latency. */
+ dev_pm_qos_add_ancestor_request(&ts->client->dev,
+ &ts->low_latency_req, 100);
+ }
+
+ /* SYN_REPORT */
+ input_sync(input_dev);
+
+end:
+ return IRQ_HANDLED;
+}
+
+static int __devinit st1232_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct st1232_ts_data *ts;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+ return -EIO;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
+
+ ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->client = client;
+ ts->input_dev = input_dev;
+
+ input_dev->name = "st1232-touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
+
+ error = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler,
+ IRQF_ONESHOT, client->name, ts);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(ts->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Unable to register %s input device\n",
+ input_dev->name);
+ goto err_free_irq;
+ }
+
+ i2c_set_clientdata(client, ts);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ return error;
+}
+
+static int __devexit st1232_ts_remove(struct i2c_client *client)
+{
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+ device_init_wakeup(&client->dev, 0);
+ free_irq(client->irq, ts);
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int st1232_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+ else
+ disable_irq(client->irq);
+
+ return 0;
+}
+
+static int st1232_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+ else
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops st1232_ts_pm_ops = {
+ .suspend = st1232_ts_suspend,
+ .resume = st1232_ts_resume,
+};
+#endif
+
+static const struct i2c_device_id st1232_ts_id[] = {
+ { ST1232_TS_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
+
+static struct i2c_driver st1232_ts_driver = {
+ .probe = st1232_ts_probe,
+ .remove = __devexit_p(st1232_ts_remove),
+ .id_table = st1232_ts_id,
+ .driver = {
+ .name = ST1232_TS_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &st1232_ts_pm_ops,
+#endif
+ },
+};
+
+module_i2c_driver(st1232_ts_driver);
+
+MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
+MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
new file mode 100644
index 00000000..692b6857
--- /dev/null
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -0,0 +1,387 @@
+/* STMicroelectronics STMPE811 Touchscreen Driver
+ *
+ * (C) 2010 Luotao Fu <l.fu@pengutronix.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/stmpe.h>
+
+/* Register layouts and functionalities are identical on all stmpexxx variants
+ * with touchscreen controller
+ */
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_TSC_CTRL 0x40
+#define STMPE_REG_TSC_CFG 0x41
+#define STMPE_REG_FIFO_TH 0x4A
+#define STMPE_REG_FIFO_STA 0x4B
+#define STMPE_REG_FIFO_SIZE 0x4C
+#define STMPE_REG_TSC_DATA_XYZ 0x52
+#define STMPE_REG_TSC_FRACTION_Z 0x56
+#define STMPE_REG_TSC_I_DRIVE 0x58
+
+#define OP_MOD_XYZ 0
+
+#define STMPE_TSC_CTRL_TSC_EN (1<<0)
+
+#define STMPE_FIFO_STA_RESET (1<<0)
+
+#define STMPE_IRQ_TOUCH_DET 0
+
+#define SAMPLE_TIME(x) ((x & 0xf) << 4)
+#define MOD_12B(x) ((x & 0x1) << 3)
+#define REF_SEL(x) ((x & 0x1) << 1)
+#define ADC_FREQ(x) (x & 0x3)
+#define AVE_CTRL(x) ((x & 0x3) << 6)
+#define DET_DELAY(x) ((x & 0x7) << 3)
+#define SETTLING(x) (x & 0x7)
+#define FRACTION_Z(x) (x & 0x7)
+#define I_DRIVE(x) (x & 0x1)
+#define OP_MODE(x) ((x & 0x7) << 1)
+
+#define STMPE_TS_NAME "stmpe-ts"
+#define XY_MASK 0xfff
+
+struct stmpe_touch {
+ struct stmpe *stmpe;
+ struct input_dev *idev;
+ struct delayed_work work;
+ struct device *dev;
+ u8 sample_time;
+ u8 mod_12b;
+ u8 ref_sel;
+ u8 adc_freq;
+ u8 ave_ctrl;
+ u8 touch_det_delay;
+ u8 settling;
+ u8 fraction_z;
+ u8 i_drive;
+};
+
+static int __stmpe_reset_fifo(struct stmpe *stmpe)
+{
+ int ret;
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, 0);
+}
+
+static void stmpe_work(struct work_struct *work)
+{
+ int int_sta;
+ u32 timeout = 40;
+
+ struct stmpe_touch *ts =
+ container_of(work, struct stmpe_touch, work.work);
+
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+
+ /*
+ * touch_det sometimes get desasserted or just get stuck. This appears
+ * to be a silicon bug, We still have to clearify this with the
+ * manufacture. As a workaround We release the key anyway if the
+ * touch_det keeps coming in after 4ms, while the FIFO contains no value
+ * during the whole time.
+ */
+ while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) {
+ timeout--;
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+ udelay(100);
+ }
+
+ /* reset the FIFO before we report release event */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ input_sync(ts->idev);
+}
+
+static irqreturn_t stmpe_ts_handler(int irq, void *data)
+{
+ u8 data_set[4];
+ int x, y, z;
+ struct stmpe_touch *ts = data;
+
+ /*
+ * Cancel scheduled polling for release if we have new value
+ * available. Wait if the polling is already running.
+ */
+ cancel_delayed_work_sync(&ts->work);
+
+ /*
+ * The FIFO sometimes just crashes and stops generating interrupts. This
+ * appears to be a silicon bug. We still have to clearify this with
+ * the manufacture. As a workaround we disable the TSC while we are
+ * collecting data and flush the FIFO after reading
+ */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+
+ stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set);
+
+ x = (data_set[0] << 4) | (data_set[1] >> 4);
+ y = ((data_set[1] & 0xf) << 8) | data_set[2];
+ z = data_set[3];
+
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, z);
+ input_sync(ts->idev);
+
+ /* flush the FIFO after we have read out our values. */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ /* reenable the tsc */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+
+ /* start polling for touch_det to detect release */
+ schedule_delayed_work(&ts->work, HZ / 50);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_init_hw(struct stmpe_touch *ts)
+{
+ int ret;
+ u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask;
+ struct stmpe *stmpe = ts->stmpe;
+ struct device *dev = ts->dev;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(dev, "Could not enable clock for ADC and TS\n");
+ return ret;
+ }
+
+ adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) |
+ REF_SEL(ts->ref_sel);
+ adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1,
+ adc_ctrl1_mask, adc_ctrl1);
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2,
+ ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq));
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) |
+ SETTLING(ts->settling);
+ tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg);
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z,
+ FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE,
+ I_DRIVE(0xff), I_DRIVE(ts->i_drive));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ /* set FIFO to 1 for single point reading */
+ ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1);
+ if (ret) {
+ dev_err(dev, "Could not set FIFO\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL,
+ OP_MODE(0xff), OP_MODE(OP_MOD_XYZ));
+ if (ret) {
+ dev_err(dev, "Could not set mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stmpe_ts_open(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+ int ret = 0;
+
+ ret = __stmpe_reset_fifo(ts->stmpe);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+}
+
+static void stmpe_ts_close(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&ts->work);
+
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+}
+
+static int __devinit stmpe_input_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_platform_data *pdata = stmpe->pdata;
+ struct stmpe_touch *ts;
+ struct input_dev *idev;
+ struct stmpe_ts_platform_data *ts_pdata = NULL;
+ int ret;
+ int ts_irq;
+
+ ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+ if (ts_irq < 0)
+ return ts_irq;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ idev = input_allocate_device();
+ if (!idev) {
+ ret = -ENOMEM;
+ goto err_free_ts;
+ }
+
+ platform_set_drvdata(pdev, ts);
+ ts->stmpe = stmpe;
+ ts->idev = idev;
+ ts->dev = &pdev->dev;
+
+ if (pdata)
+ ts_pdata = pdata->ts;
+
+ if (ts_pdata) {
+ ts->sample_time = ts_pdata->sample_time;
+ ts->mod_12b = ts_pdata->mod_12b;
+ ts->ref_sel = ts_pdata->ref_sel;
+ ts->adc_freq = ts_pdata->adc_freq;
+ ts->ave_ctrl = ts_pdata->ave_ctrl;
+ ts->touch_det_delay = ts_pdata->touch_det_delay;
+ ts->settling = ts_pdata->settling;
+ ts->fraction_z = ts_pdata->fraction_z;
+ ts->i_drive = ts_pdata->i_drive;
+ }
+
+ INIT_DELAYED_WORK(&ts->work, stmpe_work);
+
+ ret = request_threaded_irq(ts_irq, NULL, stmpe_ts_handler,
+ IRQF_ONESHOT, STMPE_TS_NAME, ts);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq);
+ goto err_free_input;
+ }
+
+ ret = stmpe_init_hw(ts);
+ if (ret)
+ goto err_free_irq;
+
+ idev->name = STMPE_TS_NAME;
+ idev->id.bustype = BUS_I2C;
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ idev->open = stmpe_ts_open;
+ idev->close = stmpe_ts_close;
+
+ input_set_drvdata(idev, ts);
+
+ input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register input device\n");
+ goto err_free_irq;
+ }
+
+ return ret;
+
+err_free_irq:
+ free_irq(ts_irq, ts);
+err_free_input:
+ input_free_device(idev);
+ platform_set_drvdata(pdev, NULL);
+err_free_ts:
+ kfree(ts);
+err_out:
+ return ret;
+}
+
+static int __devexit stmpe_ts_remove(struct platform_device *pdev)
+{
+ struct stmpe_touch *ts = platform_get_drvdata(pdev);
+ unsigned int ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+
+ stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN);
+
+ free_irq(ts_irq, ts);
+
+ platform_set_drvdata(pdev, NULL);
+
+ input_unregister_device(ts->idev);
+
+ kfree(ts);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_ts_driver = {
+ .driver = {
+ .name = STMPE_TS_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = stmpe_input_probe,
+ .remove = __devexit_p(stmpe_ts_remove),
+};
+module_platform_driver(stmpe_ts_driver);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" STMPE_TS_NAME);
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c
new file mode 100644
index 00000000..5729602c
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c
@@ -0,0 +1,675 @@
+/* drivers/input/keyboard/synaptics_i2c_rmi.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/synaptics_i2c_rmi.h>
+
+static struct workqueue_struct *synaptics_wq;
+
+struct synaptics_ts_data {
+ uint16_t addr;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ int use_irq;
+ bool has_relative_report;
+ struct hrtimer timer;
+ struct work_struct work;
+ uint16_t max[2];
+ int snap_state[2][2];
+ int snap_down_on[2];
+ int snap_down_off[2];
+ int snap_up_on[2];
+ int snap_up_off[2];
+ int snap_down[2];
+ int snap_up[2];
+ uint32_t flags;
+ int reported_finger_count;
+ int8_t sensitivity_adjust;
+ int (*power)(int on);
+ struct early_suspend early_suspend;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void synaptics_ts_early_suspend(struct early_suspend *h);
+static void synaptics_ts_late_resume(struct early_suspend *h);
+#endif
+
+static int synaptics_init_panel(struct synaptics_ts_data *ts)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n");
+ goto err_page_select_failed;
+ }
+ ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */
+ if (ret < 0)
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n");
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0x44,
+ ts->sensitivity_adjust);
+ if (ret < 0)
+ pr_err("synaptics_ts: failed to set Sensitivity Adjust\n");
+
+err_page_select_failed:
+ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */
+ if (ret < 0)
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n");
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n");
+ return ret;
+}
+
+static void synaptics_ts_work_func(struct work_struct *work)
+{
+ int i;
+ int ret;
+ int bad_data = 0;
+ struct i2c_msg msg[2];
+ uint8_t start_reg;
+ uint8_t buf[15];
+ struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work);
+ int buf_len = ts->has_relative_report ? 15 : 13;
+
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &start_reg;
+ start_reg = 0x00;
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = buf_len;
+ msg[1].buf = buf;
+
+ /* printk("synaptics_ts_work_func\n"); */
+ for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) {
+ ret = i2c_transfer(ts->client->adapter, msg, 2);
+ if (ret < 0) {
+ printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n");
+ bad_data = 1;
+ } else {
+ /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */
+ /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */
+ /* buf[0], buf[1], buf[2], buf[3], */
+ /* buf[4], buf[5], buf[6], buf[7], */
+ /* buf[8], buf[9], buf[10], buf[11], */
+ /* buf[12], buf[13], buf[14], ret); */
+ if ((buf[buf_len - 1] & 0xc0) != 0x40) {
+ printk(KERN_WARNING "synaptics_ts_work_func:"
+ " bad read %x %x %x %x %x %x %x %x %x"
+ " %x %x %x %x %x %x, ret %d\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], ret);
+ if (bad_data)
+ synaptics_init_panel(ts);
+ bad_data = 1;
+ continue;
+ }
+ bad_data = 0;
+ if ((buf[buf_len - 1] & 1) == 0) {
+ /* printk("read %d coordinates\n", i); */
+ break;
+ } else {
+ int pos[2][2];
+ int f, a;
+ int base;
+ /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */
+ /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */
+ int z = buf[1];
+ int w = buf[0] >> 4;
+ int finger = buf[0] & 7;
+
+ /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */
+ /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */
+ /* int z2 = buf[1+6]; */
+ /* int w2 = buf[0+6] >> 4; */
+ /* int finger2 = buf[0+6] & 7; */
+
+ /* int dx = (int8_t)buf[12]; */
+ /* int dy = (int8_t)buf[13]; */
+ int finger2_pressed;
+
+ /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */
+ /* x, y, z, w, finger, */
+ /* x2, y2, z2, w2, finger2, */
+ /* dx, dy); */
+
+ base = 2;
+ for (f = 0; f < 2; f++) {
+ uint32_t flip_flag = SYNAPTICS_FLIP_X;
+ for (a = 0; a < 2; a++) {
+ int p = buf[base + 1];
+ p |= (uint16_t)(buf[base] & 0x1f) << 8;
+ if (ts->flags & flip_flag)
+ p = ts->max[a] - p;
+ if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) {
+ if (ts->snap_state[f][a]) {
+ if (p <= ts->snap_down_off[a])
+ p = ts->snap_down[a];
+ else if (p >= ts->snap_up_off[a])
+ p = ts->snap_up[a];
+ else
+ ts->snap_state[f][a] = 0;
+ } else {
+ if (p <= ts->snap_down_on[a]) {
+ p = ts->snap_down[a];
+ ts->snap_state[f][a] = 1;
+ } else if (p >= ts->snap_up_on[a]) {
+ p = ts->snap_up[a];
+ ts->snap_state[f][a] = 1;
+ }
+ }
+ }
+ pos[f][a] = p;
+ base += 2;
+ flip_flag <<= 1;
+ }
+ base += 2;
+ if (ts->flags & SYNAPTICS_SWAP_XY)
+ swap(pos[f][0], pos[f][1]);
+ }
+ if (z) {
+ input_report_abs(ts->input_dev, ABS_X, pos[0][0]);
+ input_report_abs(ts->input_dev, ABS_Y, pos[0][1]);
+ }
+ input_report_abs(ts->input_dev, ABS_PRESSURE, z);
+ input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w);
+ input_report_key(ts->input_dev, BTN_TOUCH, finger);
+ finger2_pressed = finger > 1 && finger != 7;
+ input_report_key(ts->input_dev, BTN_2, finger2_pressed);
+ if (finger2_pressed) {
+ input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]);
+ input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]);
+ }
+
+ if (!finger)
+ z = 0;
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[0][1]);
+ input_mt_sync(ts->input_dev);
+ if (finger2_pressed) {
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[1][0]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[1][1]);
+ input_mt_sync(ts->input_dev);
+ } else if (ts->reported_finger_count > 1) {
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
+ input_mt_sync(ts->input_dev);
+ }
+ ts->reported_finger_count = finger;
+ input_sync(ts->input_dev);
+ }
+ }
+ }
+ if (ts->use_irq)
+ enable_irq(ts->client->irq);
+}
+
+static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer)
+{
+ struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer);
+ /* printk("synaptics_ts_timer_func\n"); */
+
+ queue_work(synaptics_wq, &ts->work);
+
+ hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id)
+{
+ struct synaptics_ts_data *ts = dev_id;
+
+ /* printk("synaptics_ts_irq_handler\n"); */
+ disable_irq_nosync(ts->client->irq);
+ queue_work(synaptics_wq, &ts->work);
+ return IRQ_HANDLED;
+}
+
+static int synaptics_ts_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct synaptics_ts_data *ts;
+ uint8_t buf0[4];
+ uint8_t buf1[8];
+ struct i2c_msg msg[2];
+ int ret = 0;
+ uint16_t max_x, max_y;
+ int fuzz_x, fuzz_y, fuzz_p, fuzz_w;
+ struct synaptics_i2c_rmi_platform_data *pdata;
+ unsigned long irqflags;
+ int inactive_area_left;
+ int inactive_area_right;
+ int inactive_area_top;
+ int inactive_area_bottom;
+ int snap_left_on;
+ int snap_left_off;
+ int snap_right_on;
+ int snap_right_off;
+ int snap_top_on;
+ int snap_top_off;
+ int snap_bottom_on;
+ int snap_bottom_off;
+ uint32_t panel_version;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n");
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_data_failed;
+ }
+ INIT_WORK(&ts->work, synaptics_ts_work_func);
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+ pdata = client->dev.platform_data;
+ if (pdata)
+ ts->power = pdata->power;
+ if (ts->power) {
+ ret = ts->power(1);
+ if (ret < 0) {
+ printk(KERN_ERR "synaptics_ts_probe power on failed\n");
+ goto err_power_failed;
+ }
+ }
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed\n");
+ /* fail? */
+ }
+ {
+ int retry = 10;
+ while (retry-- > 0) {
+ ret = i2c_smbus_read_byte_data(ts->client, 0xe4);
+ if (ret >= 0)
+ break;
+ msleep(100);
+ }
+ }
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret);
+ panel_version = ret << 8;
+ ret = i2c_smbus_read_byte_data(ts->client, 0xe5);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret);
+ panel_version |= ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, 0xe3);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret);
+
+ if (pdata) {
+ while (pdata->version > panel_version)
+ pdata++;
+ ts->flags = pdata->flags;
+ ts->sensitivity_adjust = pdata->sensitivity_adjust;
+ irqflags = pdata->irqflags;
+ inactive_area_left = pdata->inactive_left;
+ inactive_area_right = pdata->inactive_right;
+ inactive_area_top = pdata->inactive_top;
+ inactive_area_bottom = pdata->inactive_bottom;
+ snap_left_on = pdata->snap_left_on;
+ snap_left_off = pdata->snap_left_off;
+ snap_right_on = pdata->snap_right_on;
+ snap_right_off = pdata->snap_right_off;
+ snap_top_on = pdata->snap_top_on;
+ snap_top_off = pdata->snap_top_off;
+ snap_bottom_on = pdata->snap_bottom_on;
+ snap_bottom_off = pdata->snap_bottom_off;
+ fuzz_x = pdata->fuzz_x;
+ fuzz_y = pdata->fuzz_y;
+ fuzz_p = pdata->fuzz_p;
+ fuzz_w = pdata->fuzz_w;
+ } else {
+ irqflags = 0;
+ inactive_area_left = 0;
+ inactive_area_right = 0;
+ inactive_area_top = 0;
+ inactive_area_bottom = 0;
+ snap_left_on = 0;
+ snap_left_off = 0;
+ snap_right_on = 0;
+ snap_right_off = 0;
+ snap_top_on = 0;
+ snap_top_off = 0;
+ snap_bottom_on = 0;
+ snap_bottom_off = 0;
+ fuzz_x = 0;
+ fuzz_y = 0;
+ fuzz_p = 0;
+ fuzz_w = 0;
+ }
+
+ ret = i2c_smbus_read_byte_data(ts->client, 0xf0);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret);
+
+ ret = i2c_smbus_read_byte_data(ts->client, 0xf1);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret);
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed\n");
+ goto err_detect_failed;
+ }
+
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = buf0;
+ buf0[0] = 0xe0;
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 8;
+ msg[1].buf = buf1;
+ ret = i2c_transfer(ts->client->adapter, msg, 2);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_transfer failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n",
+ buf1[0], buf1[1], buf1[2], buf1[3],
+ buf1[4], buf1[5], buf1[6], buf1[7]);
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n");
+ goto err_detect_failed;
+ }
+ ret = i2c_smbus_read_word_data(ts->client, 0x02);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->has_relative_report = !(ret & 0x100);
+ printk(KERN_INFO "synaptics_ts_probe: Sensor properties %x\n", ret);
+ ret = i2c_smbus_read_word_data(ts->client, 0x04);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8);
+ ret = i2c_smbus_read_word_data(ts->client, 0x06);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8);
+ if (ts->flags & SYNAPTICS_SWAP_XY)
+ swap(max_x, max_y);
+
+ ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */
+ if (ret < 0) {
+ printk(KERN_ERR "synaptics_init_panel failed\n");
+ goto err_detect_failed;
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ ts->input_dev->name = "synaptics-rmi-touchscreen";
+ set_bit(EV_SYN, ts->input_dev->evbit);
+ set_bit(EV_KEY, ts->input_dev->evbit);
+ set_bit(BTN_TOUCH, ts->input_dev->keybit);
+ set_bit(BTN_2, ts->input_dev->keybit);
+ set_bit(EV_ABS, ts->input_dev->evbit);
+ inactive_area_left = inactive_area_left * max_x / 0x10000;
+ inactive_area_right = inactive_area_right * max_x / 0x10000;
+ inactive_area_top = inactive_area_top * max_y / 0x10000;
+ inactive_area_bottom = inactive_area_bottom * max_y / 0x10000;
+ snap_left_on = snap_left_on * max_x / 0x10000;
+ snap_left_off = snap_left_off * max_x / 0x10000;
+ snap_right_on = snap_right_on * max_x / 0x10000;
+ snap_right_off = snap_right_off * max_x / 0x10000;
+ snap_top_on = snap_top_on * max_y / 0x10000;
+ snap_top_off = snap_top_off * max_y / 0x10000;
+ snap_bottom_on = snap_bottom_on * max_y / 0x10000;
+ snap_bottom_off = snap_bottom_off * max_y / 0x10000;
+ fuzz_x = fuzz_x * max_x / 0x10000;
+ fuzz_y = fuzz_y * max_y / 0x10000;
+ ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left;
+ ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right;
+ ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top;
+ ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom;
+ ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on;
+ ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off;
+ ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on;
+ ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off;
+ ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on;
+ ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off;
+ ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on;
+ ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off;
+ printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y);
+ printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n",
+ inactive_area_left, inactive_area_right,
+ inactive_area_top, inactive_area_bottom);
+ printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n",
+ snap_left_on, snap_left_off, snap_right_on, snap_right_off,
+ snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off);
+ input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0);
+ input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0);
+ input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, fuzz_p, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, fuzz_w, 0);
+ /* ts->input_dev->name = ts->keypad_info->name; */
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name);
+ goto err_input_register_device_failed;
+ }
+ if (client->irq) {
+ ret = request_irq(client->irq, synaptics_ts_irq_handler, irqflags, client->name, ts);
+ if (ret == 0) {
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */
+ if (ret)
+ free_irq(client->irq, ts);
+ }
+ if (ret == 0)
+ ts->use_irq = 1;
+ else
+ dev_err(&client->dev, "request_irq failed\n");
+ }
+ if (!ts->use_irq) {
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = synaptics_ts_timer_func;
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = synaptics_ts_early_suspend;
+ ts->early_suspend.resume = synaptics_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling");
+
+ return 0;
+
+err_input_register_device_failed:
+ input_free_device(ts->input_dev);
+
+err_input_dev_alloc_failed:
+err_detect_failed:
+err_power_failed:
+ kfree(ts);
+err_alloc_data_failed:
+err_check_functionality_failed:
+ return ret;
+}
+
+static int synaptics_ts_remove(struct i2c_client *client)
+{
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+ unregister_early_suspend(&ts->early_suspend);
+ if (ts->use_irq)
+ free_irq(client->irq, ts);
+ else
+ hrtimer_cancel(&ts->timer);
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+ return 0;
+}
+
+static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ int ret;
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->use_irq)
+ disable_irq(client->irq);
+ else
+ hrtimer_cancel(&ts->timer);
+ ret = cancel_work_sync(&ts->work);
+ if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */
+ enable_irq(client->irq);
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n");
+
+ ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n");
+ if (ts->power) {
+ ret = ts->power(0);
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_resume power off failed\n");
+ }
+ return 0;
+}
+
+static int synaptics_ts_resume(struct i2c_client *client)
+{
+ int ret;
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->power) {
+ ret = ts->power(1);
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_resume power on failed\n");
+ }
+
+ synaptics_init_panel(ts);
+
+ if (ts->use_irq)
+ enable_irq(client->irq);
+
+ if (!ts->use_irq)
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ else
+ i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void synaptics_ts_early_suspend(struct early_suspend *h)
+{
+ struct synaptics_ts_data *ts;
+ ts = container_of(h, struct synaptics_ts_data, early_suspend);
+ synaptics_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void synaptics_ts_late_resume(struct early_suspend *h)
+{
+ struct synaptics_ts_data *ts;
+ ts = container_of(h, struct synaptics_ts_data, early_suspend);
+ synaptics_ts_resume(ts->client);
+}
+#endif
+
+static const struct i2c_device_id synaptics_ts_id[] = {
+ { SYNAPTICS_I2C_RMI_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver synaptics_ts_driver = {
+ .probe = synaptics_ts_probe,
+ .remove = synaptics_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = synaptics_ts_suspend,
+ .resume = synaptics_ts_resume,
+#endif
+ .id_table = synaptics_ts_id,
+ .driver = {
+ .name = SYNAPTICS_I2C_RMI_NAME,
+ },
+};
+
+static int __devinit synaptics_ts_init(void)
+{
+ synaptics_wq = create_singlethread_workqueue("synaptics_wq");
+ if (!synaptics_wq)
+ return -ENOMEM;
+ return i2c_add_driver(&synaptics_ts_driver);
+}
+
+static void __exit synaptics_ts_exit(void)
+{
+ i2c_del_driver(&synaptics_ts_driver);
+ if (synaptics_wq)
+ destroy_workqueue(synaptics_wq);
+}
+
+module_init(synaptics_ts_init);
+module_exit(synaptics_ts_exit);
+
+MODULE_DESCRIPTION("Synaptics Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c
new file mode 100644
index 00000000..d229c741
--- /dev/null
+++ b/drivers/input/touchscreen/ti_tscadc.c
@@ -0,0 +1,486 @@
+/*
+ * TI Touch Screen driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/input/ti_tscadc.h>
+#include <linux/delay.h>
+
+#define REG_IRQEOI 0x020
+#define REG_RAWIRQSTATUS 0x024
+#define REG_IRQSTATUS 0x028
+#define REG_IRQENABLE 0x02C
+#define REG_IRQWAKEUP 0x034
+#define REG_CTRL 0x040
+#define REG_ADCFSM 0x044
+#define REG_CLKDIV 0x04C
+#define REG_SE 0x054
+#define REG_IDLECONFIG 0x058
+#define REG_CHARGECONFIG 0x05C
+#define REG_CHARGEDELAY 0x060
+#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8))
+#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8))
+#define REG_STEPCONFIG13 0x0C4
+#define REG_STEPDELAY13 0x0C8
+#define REG_STEPCONFIG14 0x0CC
+#define REG_STEPDELAY14 0x0D0
+#define REG_FIFO0CNT 0xE4
+#define REG_FIFO1THR 0xF4
+#define REG_FIFO0 0x100
+#define REG_FIFO1 0x200
+
+/* Register Bitfields */
+#define IRQWKUP_ENB BIT(0)
+#define STPENB_STEPENB 0x7FFF
+#define IRQENB_FIFO1THRES BIT(5)
+#define IRQENB_PENUP BIT(9)
+#define STEPCONFIG_MODE_HWSYNC 0x2
+#define STEPCONFIG_SAMPLES_AVG (1 << 4)
+#define STEPCONFIG_XPP (1 << 5)
+#define STEPCONFIG_XNN (1 << 6)
+#define STEPCONFIG_YPP (1 << 7)
+#define STEPCONFIG_YNN (1 << 8)
+#define STEPCONFIG_XNP (1 << 9)
+#define STEPCONFIG_YPN (1 << 10)
+#define STEPCONFIG_INM (1 << 18)
+#define STEPCONFIG_INP (1 << 20)
+#define STEPCONFIG_INP_5 (1 << 21)
+#define STEPCONFIG_FIFO1 (1 << 26)
+#define STEPCONFIG_OPENDLY 0xff
+#define STEPCONFIG_Z1 (3 << 19)
+#define STEPIDLE_INP (1 << 22)
+#define STEPCHARGE_RFP (1 << 12)
+#define STEPCHARGE_INM (1 << 15)
+#define STEPCHARGE_INP (1 << 19)
+#define STEPCHARGE_RFM (1 << 23)
+#define STEPCHARGE_DELAY 0x1
+#define CNTRLREG_TSCSSENB (1 << 0)
+#define CNTRLREG_STEPID (1 << 1)
+#define CNTRLREG_STEPCONFIGWRT (1 << 2)
+#define CNTRLREG_4WIRE (1 << 5)
+#define CNTRLREG_5WIRE (1 << 6)
+#define CNTRLREG_8WIRE (3 << 5)
+#define CNTRLREG_TSCENB (1 << 7)
+#define ADCFSM_STEPID 0x10
+
+#define SEQ_SETTLE 275
+#define ADC_CLK 3000000
+#define MAX_12BIT ((1 << 12) - 1)
+#define TSCADC_DELTA_X 15
+#define TSCADC_DELTA_Y 15
+
+struct tscadc {
+ struct input_dev *input;
+ struct clk *tsc_ick;
+ void __iomem *tsc_base;
+ unsigned int irq;
+ unsigned int wires;
+ unsigned int x_plate_resistance;
+ bool pen_down;
+};
+
+static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg)
+{
+ return readl(ts->tsc_base + reg);
+}
+
+static void tscadc_writel(struct tscadc *tsc, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, tsc->tsc_base + reg);
+}
+
+static void tscadc_step_config(struct tscadc *ts_dev)
+{
+ unsigned int config;
+ int i;
+
+ /* Configure the Step registers */
+
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= STEPCONFIG_INP | STEPCONFIG_XNN;
+ break;
+ case 5:
+ config |= STEPCONFIG_YNN |
+ STEPCONFIG_INP_5 | STEPCONFIG_XNN |
+ STEPCONFIG_YPP;
+ break;
+ case 8:
+ config |= STEPCONFIG_INP | STEPCONFIG_XNN;
+ break;
+ }
+
+ for (i = 1; i < 7; i++) {
+ tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ config = 0;
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN |
+ STEPCONFIG_INM | STEPCONFIG_FIFO1;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= STEPCONFIG_YPP;
+ break;
+ case 5:
+ config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 |
+ STEPCONFIG_XNP | STEPCONFIG_YPN;
+ break;
+ case 8:
+ config |= STEPCONFIG_YPP;
+ break;
+ }
+
+ for (i = 7; i < 13; i++) {
+ tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ config = 0;
+ /* Charge step configuration */
+ config = STEPCONFIG_XPP | STEPCONFIG_YNN |
+ STEPCHARGE_RFP | STEPCHARGE_RFM |
+ STEPCHARGE_INM | STEPCHARGE_INP;
+
+ tscadc_writel(ts_dev, REG_CHARGECONFIG, config);
+ tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY);
+
+ config = 0;
+ /* Configure to calculate pressure */
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP |
+ STEPCONFIG_XNN | STEPCONFIG_INM;
+ tscadc_writel(ts_dev, REG_STEPCONFIG13, config);
+ tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY);
+
+ config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1;
+ tscadc_writel(ts_dev, REG_STEPCONFIG14, config);
+ tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY);
+
+ tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
+}
+
+static void tscadc_idle_config(struct tscadc *ts_config)
+{
+ unsigned int idleconfig;
+
+ idleconfig = STEPCONFIG_YNN |
+ STEPCONFIG_INM |
+ STEPCONFIG_YPN | STEPIDLE_INP;
+ tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig);
+}
+
+static void tscadc_read_coordinates(struct tscadc *ts_dev,
+ unsigned int *x, unsigned int *y)
+{
+ unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT);
+ unsigned int prev_val_x = ~0, prev_val_y = ~0;
+ unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
+ unsigned int read, diff;
+ unsigned int i;
+
+ /*
+ * Delta filter is used to remove large variations in sampled
+ * values from ADC. The filter tries to predict where the next
+ * coordinate could be. This is done by taking a previous
+ * coordinate and subtracting it form current one. Further the
+ * algorithm compares the difference with that of a present value,
+ * if true the value is reported to the sub system.
+ */
+ for (i = 0; i < fifocount - 1; i++) {
+ read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
+ diff = abs(read - prev_val_x);
+ if (diff < prev_diff_x) {
+ prev_diff_x = diff;
+ *x = read;
+ }
+ prev_val_x = read;
+
+ read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
+ diff = abs(read - prev_val_y);
+ if (diff < prev_diff_y) {
+ prev_diff_y = diff;
+ *y = read;
+ }
+ prev_val_y = read;
+ }
+}
+
+static irqreturn_t tscadc_irq(int irq, void *dev)
+{
+ struct tscadc *ts_dev = dev;
+ struct input_dev *input_dev = ts_dev->input;
+ unsigned int status, irqclr = 0;
+ unsigned int x = 0, y = 0;
+ unsigned int z1, z2, z;
+ unsigned int fsm;
+
+ status = tscadc_readl(ts_dev, REG_IRQSTATUS);
+ if (status & IRQENB_FIFO1THRES) {
+ tscadc_read_coordinates(ts_dev, &x, &y);
+
+ z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
+ z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
+
+ if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
+ /*
+ * Calculate pressure using formula
+ * Resistance(touch) = x plate resistance *
+ * x postion/4096 * ((z2 / z1) - 1)
+ */
+ z = z2 - z1;
+ z *= x;
+ z *= ts_dev->x_plate_resistance;
+ z /= z1;
+ z = (z + 2047) >> 12;
+
+ if (z <= MAX_12BIT) {
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_report_abs(input_dev, ABS_PRESSURE, z);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_sync(input_dev);
+ }
+ }
+ irqclr |= IRQENB_FIFO1THRES;
+ }
+
+ /*
+ * Time for sequencer to settle, to read
+ * correct state of the sequencer.
+ */
+ udelay(SEQ_SETTLE);
+
+ status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS);
+ if (status & IRQENB_PENUP) {
+ /* Pen up event */
+ fsm = tscadc_readl(ts_dev, REG_ADCFSM);
+ if (fsm == ADCFSM_STEPID) {
+ ts_dev->pen_down = false;
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_sync(input_dev);
+ } else {
+ ts_dev->pen_down = true;
+ }
+ irqclr |= IRQENB_PENUP;
+ }
+
+ tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+ /* check pending interrupts */
+ tscadc_writel(ts_dev, REG_IRQEOI, 0x0);
+
+ tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
+ return IRQ_HANDLED;
+}
+
+/*
+ * The functions for inserting/removing driver as a module.
+ */
+
+static int __devinit tscadc_probe(struct platform_device *pdev)
+{
+ const struct tsc_data *pdata = pdev->dev.platform_data;
+ struct resource *res;
+ struct tscadc *ts_dev;
+ struct input_dev *input_dev;
+ struct clk *clk;
+ int err;
+ int clk_value, ctrl, irq;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "missing platform data.\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq ID is specified.\n");
+ return -EINVAL;
+ }
+
+ /* Allocate memory for device */
+ ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts_dev || !input_dev) {
+ dev_err(&pdev->dev, "failed to allocate memory.\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts_dev->input = input_dev;
+ ts_dev->irq = irq;
+ ts_dev->wires = pdata->wires;
+ ts_dev->x_plate_resistance = pdata->x_plate_resistance;
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to reserve registers.\n");
+ err = -EBUSY;
+ goto err_free_mem;
+ }
+
+ ts_dev->tsc_base = ioremap(res->start, resource_size(res));
+ if (!ts_dev->tsc_base) {
+ dev_err(&pdev->dev, "failed to map registers.\n");
+ err = -ENOMEM;
+ goto err_release_mem_region;
+ }
+
+ err = request_irq(ts_dev->irq, tscadc_irq,
+ 0, pdev->dev.driver->name, ts_dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to allocate irq.\n");
+ goto err_unmap_regs;
+ }
+
+ ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick");
+ if (IS_ERR(ts_dev->tsc_ick)) {
+ dev_err(&pdev->dev, "failed to get TSC ick\n");
+ goto err_free_irq;
+ }
+ clk_enable(ts_dev->tsc_ick);
+
+ clk = clk_get(&pdev->dev, "adc_tsc_fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get TSC fck\n");
+ err = PTR_ERR(clk);
+ goto err_disable_clk;
+ }
+
+ clk_value = clk_get_rate(clk) / ADC_CLK;
+ clk_put(clk);
+
+ if (clk_value < 7) {
+ dev_err(&pdev->dev, "clock input less than min clock requirement\n");
+ goto err_disable_clk;
+ }
+ /* CLKDIV needs to be configured to the value minus 1 */
+ tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1);
+
+ /* Enable wake-up of the SoC using touchscreen */
+ tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
+
+ ctrl = CNTRLREG_STEPCONFIGWRT |
+ CNTRLREG_TSCENB |
+ CNTRLREG_STEPID;
+ switch (ts_dev->wires) {
+ case 4:
+ ctrl |= CNTRLREG_4WIRE;
+ break;
+ case 5:
+ ctrl |= CNTRLREG_5WIRE;
+ break;
+ case 8:
+ ctrl |= CNTRLREG_8WIRE;
+ break;
+ }
+ tscadc_writel(ts_dev, REG_CTRL, ctrl);
+
+ tscadc_idle_config(ts_dev);
+ tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
+ tscadc_step_config(ts_dev);
+ tscadc_writel(ts_dev, REG_FIFO1THR, 6);
+
+ ctrl |= CNTRLREG_TSCSSENB;
+ tscadc_writel(ts_dev, REG_CTRL, ctrl);
+
+ input_dev->name = "ti-tsc-adc";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
+
+ /* register to the input system */
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_disable_clk;
+
+ platform_set_drvdata(pdev, ts_dev);
+ return 0;
+
+err_disable_clk:
+ clk_disable(ts_dev->tsc_ick);
+ clk_put(ts_dev->tsc_ick);
+err_free_irq:
+ free_irq(ts_dev->irq, ts_dev);
+err_unmap_regs:
+ iounmap(ts_dev->tsc_base);
+err_release_mem_region:
+ release_mem_region(res->start, resource_size(res));
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts_dev);
+ return err;
+}
+
+static int __devexit tscadc_remove(struct platform_device *pdev)
+{
+ struct tscadc *ts_dev = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(ts_dev->irq, ts_dev);
+
+ input_unregister_device(ts_dev->input);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iounmap(ts_dev->tsc_base);
+ release_mem_region(res->start, resource_size(res));
+
+ clk_disable(ts_dev->tsc_ick);
+ clk_put(ts_dev->tsc_ick);
+
+ kfree(ts_dev);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver ti_tsc_driver = {
+ .probe = tscadc_probe,
+ .remove = __devexit_p(tscadc_remove),
+ .driver = {
+ .name = "tsc",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(ti_tsc_driver);
+
+MODULE_DESCRIPTION("TI touchscreen controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c
new file mode 100644
index 00000000..7e748809
--- /dev/null
+++ b/drivers/input/touchscreen/tnetv107x-ts.c
@@ -0,0 +1,386 @@
+/*
+ * Texas Instruments TNETV107X Touchscreen Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/tnetv107x.h>
+
+#define TSC_PENUP_POLL (HZ / 5)
+#define IDLE_TIMEOUT 100 /* msec */
+
+/*
+ * The first and last samples of a touch interval are usually garbage and need
+ * to be filtered out with these devices. The following definitions control
+ * the number of samples skipped.
+ */
+#define TSC_HEAD_SKIP 1
+#define TSC_TAIL_SKIP 1
+#define TSC_SKIP (TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1)
+#define TSC_SAMPLES (TSC_SKIP + 1)
+
+/* Register Offsets */
+struct tsc_regs {
+ u32 rev;
+ u32 tscm;
+ u32 bwcm;
+ u32 swc;
+ u32 adcchnl;
+ u32 adcdata;
+ u32 chval[4];
+};
+
+/* TSC Mode Configuration Register (tscm) bits */
+#define WMODE BIT(0)
+#define TSKIND BIT(1)
+#define ZMEASURE_EN BIT(2)
+#define IDLE BIT(3)
+#define TSC_EN BIT(4)
+#define STOP BIT(5)
+#define ONE_SHOT BIT(6)
+#define SINGLE BIT(7)
+#define AVG BIT(8)
+#define AVGNUM(x) (((x) & 0x03) << 9)
+#define PVSTC(x) (((x) & 0x07) << 11)
+#define PON BIT(14)
+#define PONBG BIT(15)
+#define AFERST BIT(16)
+
+/* ADC DATA Capture Register bits */
+#define DATA_VALID BIT(16)
+
+/* Register Access Macros */
+#define tsc_read(ts, reg) __raw_readl(&(ts)->regs->reg)
+#define tsc_write(ts, reg, val) __raw_writel(val, &(ts)->regs->reg);
+#define tsc_set_bits(ts, reg, val) \
+ tsc_write(ts, reg, tsc_read(ts, reg) | (val))
+#define tsc_clr_bits(ts, reg, val) \
+ tsc_write(ts, reg, tsc_read(ts, reg) & ~(val))
+
+struct sample {
+ int x, y, p;
+};
+
+struct tsc_data {
+ struct input_dev *input_dev;
+ struct resource *res;
+ struct tsc_regs __iomem *regs;
+ struct timer_list timer;
+ spinlock_t lock;
+ struct clk *clk;
+ struct device *dev;
+ int sample_count;
+ struct sample samples[TSC_SAMPLES];
+ int tsc_irq;
+};
+
+static int tsc_read_sample(struct tsc_data *ts, struct sample* sample)
+{
+ int x, y, z1, z2, t, p = 0;
+ u32 val;
+
+ val = tsc_read(ts, chval[0]);
+ if (val & DATA_VALID)
+ x = val & 0xffff;
+ else
+ return -EINVAL;
+
+ y = tsc_read(ts, chval[1]) & 0xffff;
+ z1 = tsc_read(ts, chval[2]) & 0xffff;
+ z2 = tsc_read(ts, chval[3]) & 0xffff;
+
+ if (z1) {
+ t = ((600 * x) * (z2 - z1));
+ p = t / (u32) (z1 << 12);
+ if (p < 0)
+ p = 0;
+ }
+
+ sample->x = x;
+ sample->y = y;
+ sample->p = p;
+
+ return 0;
+}
+
+static void tsc_poll(unsigned long data)
+{
+ struct tsc_data *ts = (struct tsc_data *)data;
+ unsigned long flags;
+ int i, val, x, y, p;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ if (ts->sample_count >= TSC_SKIP) {
+ input_report_abs(ts->input_dev, ABS_PRESSURE, 0);
+ input_report_key(ts->input_dev, BTN_TOUCH, 0);
+ input_sync(ts->input_dev);
+ } else if (ts->sample_count > 0) {
+ /*
+ * A touch event lasted less than our skip count. Salvage and
+ * report anyway.
+ */
+ for (i = 0, val = 0; i < ts->sample_count; i++)
+ val += ts->samples[i].x;
+ x = val / ts->sample_count;
+
+ for (i = 0, val = 0; i < ts->sample_count; i++)
+ val += ts->samples[i].y;
+ y = val / ts->sample_count;
+
+ for (i = 0, val = 0; i < ts->sample_count; i++)
+ val += ts->samples[i].p;
+ p = val / ts->sample_count;
+
+ input_report_abs(ts->input_dev, ABS_X, x);
+ input_report_abs(ts->input_dev, ABS_Y, y);
+ input_report_abs(ts->input_dev, ABS_PRESSURE, p);
+ input_report_key(ts->input_dev, BTN_TOUCH, 1);
+ input_sync(ts->input_dev);
+ }
+
+ ts->sample_count = 0;
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static irqreturn_t tsc_irq(int irq, void *dev_id)
+{
+ struct tsc_data *ts = (struct tsc_data *)dev_id;
+ struct sample *sample;
+ int index;
+
+ spin_lock(&ts->lock);
+
+ index = ts->sample_count % TSC_SAMPLES;
+ sample = &ts->samples[index];
+ if (tsc_read_sample(ts, sample) < 0)
+ goto out;
+
+ if (++ts->sample_count >= TSC_SKIP) {
+ index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES;
+ sample = &ts->samples[index];
+
+ input_report_abs(ts->input_dev, ABS_X, sample->x);
+ input_report_abs(ts->input_dev, ABS_Y, sample->y);
+ input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p);
+ if (ts->sample_count == TSC_SKIP)
+ input_report_key(ts->input_dev, BTN_TOUCH, 1);
+ input_sync(ts->input_dev);
+ }
+ mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL);
+out:
+ spin_unlock(&ts->lock);
+ return IRQ_HANDLED;
+}
+
+static int tsc_start(struct input_dev *dev)
+{
+ struct tsc_data *ts = input_get_drvdata(dev);
+ unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT);
+ u32 val;
+
+ clk_enable(ts->clk);
+
+ /* Go to idle mode, before any initialization */
+ while (time_after(timeout, jiffies)) {
+ if (tsc_read(ts, tscm) & IDLE)
+ break;
+ }
+
+ if (time_before(timeout, jiffies)) {
+ dev_warn(ts->dev, "timeout waiting for idle\n");
+ clk_disable(ts->clk);
+ return -EIO;
+ }
+
+ /* Configure TSC Control register*/
+ val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN);
+ tsc_write(ts, tscm, val);
+
+ /* Bring TSC out of reset: Clear AFE reset bit */
+ val &= ~(AFERST);
+ tsc_write(ts, tscm, val);
+
+ /* Configure all pins for hardware control*/
+ tsc_write(ts, bwcm, 0);
+
+ /* Finally enable the TSC */
+ tsc_set_bits(ts, tscm, TSC_EN);
+
+ return 0;
+}
+
+static void tsc_stop(struct input_dev *dev)
+{
+ struct tsc_data *ts = input_get_drvdata(dev);
+
+ tsc_clr_bits(ts, tscm, TSC_EN);
+ synchronize_irq(ts->tsc_irq);
+ del_timer_sync(&ts->timer);
+ clk_disable(ts->clk);
+}
+
+static int __devinit tsc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tsc_data *ts;
+ int error = 0;
+ u32 rev = 0;
+
+ ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL);
+ if (!ts) {
+ dev_err(dev, "cannot allocate device info\n");
+ return -ENOMEM;
+ }
+
+ ts->dev = dev;
+ spin_lock_init(&ts->lock);
+ setup_timer(&ts->timer, tsc_poll, (unsigned long)ts);
+ platform_set_drvdata(pdev, ts);
+
+ ts->tsc_irq = platform_get_irq(pdev, 0);
+ if (ts->tsc_irq < 0) {
+ dev_err(dev, "cannot determine device interrupt\n");
+ error = -ENODEV;
+ goto error_res;
+ }
+
+ ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ts->res) {
+ dev_err(dev, "cannot determine register area\n");
+ error = -ENODEV;
+ goto error_res;
+ }
+
+ if (!request_mem_region(ts->res->start, resource_size(ts->res),
+ pdev->name)) {
+ dev_err(dev, "cannot claim register memory\n");
+ ts->res = NULL;
+ error = -EINVAL;
+ goto error_res;
+ }
+
+ ts->regs = ioremap(ts->res->start, resource_size(ts->res));
+ if (!ts->regs) {
+ dev_err(dev, "cannot map register memory\n");
+ error = -ENOMEM;
+ goto error_map;
+ }
+
+ ts->clk = clk_get(dev, NULL);
+ if (IS_ERR(ts->clk)) {
+ dev_err(dev, "cannot claim device clock\n");
+ error = PTR_ERR(ts->clk);
+ goto error_clk;
+ }
+
+ error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, 0,
+ dev_name(dev), ts);
+ if (error < 0) {
+ dev_err(ts->dev, "Could not allocate ts irq\n");
+ goto error_irq;
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (!ts->input_dev) {
+ dev_err(dev, "cannot allocate input device\n");
+ error = -ENOMEM;
+ goto error_input;
+ }
+ input_set_drvdata(ts->input_dev, ts);
+
+ ts->input_dev->name = pdev->name;
+ ts->input_dev->id.bustype = BUS_HOST;
+ ts->input_dev->dev.parent = &pdev->dev;
+ ts->input_dev->open = tsc_start;
+ ts->input_dev->close = tsc_stop;
+
+ clk_enable(ts->clk);
+ rev = tsc_read(ts, rev);
+ ts->input_dev->id.product = ((rev >> 8) & 0x07);
+ ts->input_dev->id.version = ((rev >> 16) & 0xfff);
+ clk_disable(ts->clk);
+
+ __set_bit(EV_KEY, ts->input_dev->evbit);
+ __set_bit(EV_ABS, ts->input_dev->evbit);
+ __set_bit(BTN_TOUCH, ts->input_dev->keybit);
+
+ input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0);
+ input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0);
+ input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0);
+
+ error = input_register_device(ts->input_dev);
+ if (error < 0) {
+ dev_err(dev, "failed input device registration\n");
+ goto error_reg;
+ }
+
+ return 0;
+
+error_reg:
+ input_free_device(ts->input_dev);
+error_input:
+ free_irq(ts->tsc_irq, ts);
+error_irq:
+ clk_put(ts->clk);
+error_clk:
+ iounmap(ts->regs);
+error_map:
+ release_mem_region(ts->res->start, resource_size(ts->res));
+error_res:
+ platform_set_drvdata(pdev, NULL);
+ kfree(ts);
+
+ return error;
+}
+
+static int __devexit tsc_remove(struct platform_device *pdev)
+{
+ struct tsc_data *ts = platform_get_drvdata(pdev);
+
+ input_unregister_device(ts->input_dev);
+ free_irq(ts->tsc_irq, ts);
+ clk_put(ts->clk);
+ iounmap(ts->regs);
+ release_mem_region(ts->res->start, resource_size(ts->res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(ts);
+
+ return 0;
+}
+
+static struct platform_driver tsc_driver = {
+ .probe = tsc_probe,
+ .remove = __devexit_p(tsc_remove),
+ .driver.name = "tnetv107x-ts",
+ .driver.owner = THIS_MODULE,
+};
+module_platform_driver(tsc_driver);
+
+MODULE_AUTHOR("Cyril Chemparathy");
+MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
+MODULE_ALIAS("platform:tnetv107x-ts");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/touchit213.c b/drivers/input/touchscreen/touchit213.c
new file mode 100644
index 00000000..d1297ba1
--- /dev/null
+++ b/drivers/input/touchscreen/touchit213.c
@@ -0,0 +1,234 @@
+/*
+ * Sahara TouchIT-213 serial touchscreen driver
+ *
+ * Copyright (c) 2007-2008 Claudio Nieder <private@claudio.ch>
+ *
+ * Based on Touchright driver (drivers/input/touchscreen/touchright.c)
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Sahara TouchIT-213 serial touchscreen driver"
+
+MODULE_AUTHOR("Claudio Nieder <private@claudio.ch>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+/*
+ * Data is received through COM1 at 9600bit/s,8bit,no parity in packets
+ * of 5 byte each.
+ *
+ * +--------+ +--------+ +--------+ +--------+ +--------+
+ * |1000000p| |0xxxxxxx| |0xxxxxxx| |0yyyyyyy| |0yyyyyyy|
+ * +--------+ +--------+ +--------+ +--------+ +--------+
+ * MSB LSB MSB LSB
+ *
+ * The value of p is 1 as long as the screen is touched and 0 when
+ * reporting the location where touching stopped, e.g. where the pen was
+ * lifted from the screen.
+ *
+ * When holding the screen in landscape mode as the BIOS text output is
+ * presented, x is the horizontal axis with values growing from left to
+ * right and y is the vertical axis with values growing from top to
+ * bottom.
+ *
+ * When holding the screen in portrait mode with the Sahara logo in its
+ * correct position, x ist the vertical axis with values growing from
+ * top to bottom and y is the horizontal axis with values growing from
+ * right to left.
+ */
+
+#define T213_FORMAT_TOUCH_BIT 0x01
+#define T213_FORMAT_STATUS_BYTE 0x80
+#define T213_FORMAT_STATUS_MASK ~T213_FORMAT_TOUCH_BIT
+
+/*
+ * On my Sahara Touch-IT 213 I have observed x values from 0 to 0x7f0
+ * and y values from 0x1d to 0x7e9, so the actual measurement is
+ * probably done with an 11 bit precision.
+ */
+#define T213_MIN_XC 0
+#define T213_MAX_XC 0x07ff
+#define T213_MIN_YC 0
+#define T213_MAX_YC 0x07ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct touchit213 {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char csum;
+ unsigned char data[5];
+ char phys[32];
+};
+
+static irqreturn_t touchit213_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct touchit213 *touchit213 = serio_get_drvdata(serio);
+ struct input_dev *dev = touchit213->dev;
+
+ touchit213->data[touchit213->idx] = data;
+
+ switch (touchit213->idx++) {
+ case 0:
+ if ((touchit213->data[0] & T213_FORMAT_STATUS_MASK) !=
+ T213_FORMAT_STATUS_BYTE) {
+ pr_debug("unsynchronized data: 0x%02x\n", data);
+ touchit213->idx = 0;
+ }
+ break;
+
+ case 4:
+ touchit213->idx = 0;
+ input_report_abs(dev, ABS_X,
+ (touchit213->data[1] << 7) | touchit213->data[2]);
+ input_report_abs(dev, ABS_Y,
+ (touchit213->data[3] << 7) | touchit213->data[4]);
+ input_report_key(dev, BTN_TOUCH,
+ touchit213->data[0] & T213_FORMAT_TOUCH_BIT);
+ input_sync(dev);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * touchit213_disconnect() is the opposite of touchit213_connect()
+ */
+
+static void touchit213_disconnect(struct serio *serio)
+{
+ struct touchit213 *touchit213 = serio_get_drvdata(serio);
+
+ input_get_device(touchit213->dev);
+ input_unregister_device(touchit213->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(touchit213->dev);
+ kfree(touchit213);
+}
+
+/*
+ * touchit213_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int touchit213_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct touchit213 *touchit213;
+ struct input_dev *input_dev;
+ int err;
+
+ touchit213 = kzalloc(sizeof(struct touchit213), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!touchit213 || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ touchit213->serio = serio;
+ touchit213->dev = input_dev;
+ snprintf(touchit213->phys, sizeof(touchit213->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Sahara Touch-iT213 Serial TouchScreen";
+ input_dev->phys = touchit213->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHIT213;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(touchit213->dev, ABS_X,
+ T213_MIN_XC, T213_MAX_XC, 0, 0);
+ input_set_abs_params(touchit213->dev, ABS_Y,
+ T213_MIN_YC, T213_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, touchit213);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(touchit213->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(touchit213);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id touchit213_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHIT213,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, touchit213_serio_ids);
+
+static struct serio_driver touchit213_drv = {
+ .driver = {
+ .name = "touchit213",
+ },
+ .description = DRIVER_DESC,
+ .id_table = touchit213_serio_ids,
+ .interrupt = touchit213_interrupt,
+ .connect = touchit213_connect,
+ .disconnect = touchit213_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init touchit213_init(void)
+{
+ return serio_register_driver(&touchit213_drv);
+}
+
+static void __exit touchit213_exit(void)
+{
+ serio_unregister_driver(&touchit213_drv);
+}
+
+module_init(touchit213_init);
+module_exit(touchit213_exit);
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
new file mode 100644
index 00000000..3a5c142c
--- /dev/null
+++ b/drivers/input/touchscreen/touchright.c
@@ -0,0 +1,194 @@
+/*
+ * Touchright serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Touchright serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TR_FORMAT_TOUCH_BIT 0x01
+#define TR_FORMAT_STATUS_BYTE 0x40
+#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT
+
+#define TR_LENGTH 5
+
+#define TR_MIN_XC 0
+#define TR_MAX_XC 0x1ff
+#define TR_MIN_YC 0
+#define TR_MAX_YC 0x1ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tr {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[TR_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tr_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+ struct input_dev *dev = tr->dev;
+
+ tr->data[tr->idx] = data;
+
+ if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) {
+ if (++tr->idx == TR_LENGTH) {
+ input_report_abs(dev, ABS_X,
+ (tr->data[1] << 5) | (tr->data[2] >> 1));
+ input_report_abs(dev, ABS_Y,
+ (tr->data[3] << 5) | (tr->data[4] >> 1));
+ input_report_key(dev, BTN_TOUCH,
+ tr->data[0] & TR_FORMAT_TOUCH_BIT);
+ input_sync(dev);
+ tr->idx = 0;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tr_disconnect() is the opposite of tr_connect()
+ */
+
+static void tr_disconnect(struct serio *serio)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+
+ input_get_device(tr->dev);
+ input_unregister_device(tr->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tr->dev);
+ kfree(tr);
+}
+
+/*
+ * tr_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int tr_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tr *tr;
+ struct input_dev *input_dev;
+ int err;
+
+ tr = kzalloc(sizeof(struct tr), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tr || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tr->serio = serio;
+ tr->dev = input_dev;
+ snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Touchright Serial TouchScreen";
+ input_dev->phys = tr->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHRIGHT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
+ input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tr);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tr->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tr);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tr_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHRIGHT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tr_serio_ids);
+
+static struct serio_driver tr_drv = {
+ .driver = {
+ .name = "touchright",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tr_serio_ids,
+ .interrupt = tr_interrupt,
+ .connect = tr_connect,
+ .disconnect = tr_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tr_init(void)
+{
+ return serio_register_driver(&tr_drv);
+}
+
+static void __exit tr_exit(void)
+{
+ serio_unregister_driver(&tr_drv);
+}
+
+module_init(tr_init);
+module_exit(tr_exit);
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
new file mode 100644
index 00000000..763a656a
--- /dev/null
+++ b/drivers/input/touchscreen/touchwin.c
@@ -0,0 +1,201 @@
+/*
+ * Touchwindow serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+/*
+ * 2005/02/19 Rick Koch:
+ * The Touchwindow I used is made by Edmark Corp. and
+ * constantly outputs a stream of 0's unless it is touched.
+ * It then outputs 3 bytes: X, Y, and a copy of Y.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Touchwindow serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TW_LENGTH 3
+
+#define TW_MIN_XC 0
+#define TW_MAX_XC 0xff
+#define TW_MIN_YC 0
+#define TW_MAX_YC 0xff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tw {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ int touched;
+ unsigned char data[TW_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tw_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+ struct input_dev *dev = tw->dev;
+
+ if (data) { /* touch */
+ tw->touched = 1;
+ tw->data[tw->idx++] = data;
+ /* verify length and that the two Y's are the same */
+ if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
+ input_report_abs(dev, ABS_X, tw->data[0]);
+ input_report_abs(dev, ABS_Y, tw->data[1]);
+ input_report_key(dev, BTN_TOUCH, 1);
+ input_sync(dev);
+ tw->idx = 0;
+ }
+ } else if (tw->touched) { /* untouch */
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
+ tw->idx = 0;
+ tw->touched = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tw_disconnect() is the opposite of tw_connect()
+ */
+
+static void tw_disconnect(struct serio *serio)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+
+ input_get_device(tw->dev);
+ input_unregister_device(tw->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tw->dev);
+ kfree(tw);
+}
+
+/*
+ * tw_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchwin protocol and registers it as
+ * an input device.
+ */
+
+static int tw_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tw *tw;
+ struct input_dev *input_dev;
+ int err;
+
+ tw = kzalloc(sizeof(struct tw), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tw || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tw->serio = serio;
+ tw->dev = input_dev;
+ snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Touchwindow Serial TouchScreen";
+ input_dev->phys = tw->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHWIN;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
+ input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tw);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tw->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tw);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tw_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHWIN,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tw_serio_ids);
+
+static struct serio_driver tw_drv = {
+ .driver = {
+ .name = "touchwin",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tw_serio_ids,
+ .interrupt = tw_interrupt,
+ .connect = tw_connect,
+ .disconnect = tw_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tw_init(void)
+{
+ return serio_register_driver(&tw_drv);
+}
+
+static void __exit tw_exit(void)
+{
+ serio_unregister_driver(&tw_drv);
+}
+
+module_init(tw_init);
+module_exit(tw_exit);
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
new file mode 100644
index 00000000..f7eda3d0
--- /dev/null
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -0,0 +1,377 @@
+/*
+ * Touchscreen driver for the tps6507x chip.
+ *
+ * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ * Credits:
+ *
+ * Using code from tsc2007, MtekVision Co., Ltd.
+ *
+ * For licencing details see kernel-base/COPYING
+ *
+ * TPS65070, TPS65073, TPS650731, and TPS650732 support
+ * 10 bit touch screen interface.
+ */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tps6507x.h>
+#include <linux/input/tps6507x-ts.h>
+#include <linux/delay.h>
+
+#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
+#define TPS_DEFAULT_MIN_PRESSURE 0x30
+#define MAX_10BIT ((1 << 10) - 1)
+
+#define TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \
+ TPS6507X_ADCONFIG_START_CONVERSION | \
+ TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+#define TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+
+struct ts_event {
+ u16 x;
+ u16 y;
+ u16 pressure;
+};
+
+struct tps6507x_ts {
+ struct input_dev *input_dev;
+ struct device *dev;
+ char phys[32];
+ struct delayed_work work;
+ unsigned polling; /* polling is active */
+ struct ts_event tc;
+ struct tps6507x_dev *mfd;
+ u16 model;
+ unsigned pendown;
+ int irq;
+ void (*clear_penirq)(void);
+ unsigned long poll_period; /* ms */
+ u16 min_pressure;
+ int vref; /* non-zero to leave vref on */
+};
+
+static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
+{
+ int err;
+
+ err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
+
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
+{
+ return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data);
+}
+
+static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc,
+ u8 tsc_mode, u16 *value)
+{
+ s32 ret;
+ u8 adc_status;
+ u8 result;
+
+ /* Route input signal to A/D converter */
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode);
+ if (ret) {
+ dev_err(tsc->dev, "TSC mode read failed\n");
+ goto err;
+ }
+
+ /* Start A/D conversion */
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
+ TPS6507X_ADCONFIG_CONVERT_TS);
+ if (ret) {
+ dev_err(tsc->dev, "ADC config write failed\n");
+ return ret;
+ }
+
+ do {
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG,
+ &adc_status);
+ if (ret) {
+ dev_err(tsc->dev, "ADC config read failed\n");
+ goto err;
+ }
+ } while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION);
+
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result);
+ if (ret) {
+ dev_err(tsc->dev, "ADC result 2 read failed\n");
+ goto err;
+ }
+
+ *value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8;
+
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result);
+ if (ret) {
+ dev_err(tsc->dev, "ADC result 1 read failed\n");
+ goto err;
+ }
+
+ *value |= result;
+
+ dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value);
+
+err:
+ return ret;
+}
+
+/* Need to call tps6507x_adc_standby() after using A/D converter for the
+ * touch screen interrupt to work properly.
+ */
+
+static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
+{
+ s32 ret;
+ s32 loops = 0;
+ u8 val;
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
+ TPS6507X_ADCONFIG_INPUT_TSC);
+ if (ret)
+ return ret;
+
+ ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE,
+ TPS6507X_TSCMODE_STANDBY);
+ if (ret)
+ return ret;
+
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+ if (ret)
+ return ret;
+
+ while (val & TPS6507X_REG_TSC_INT) {
+ mdelay(10);
+ ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+ if (ret)
+ return ret;
+ loops++;
+ }
+
+ return ret;
+}
+
+static void tps6507x_ts_handler(struct work_struct *work)
+{
+ struct tps6507x_ts *tsc = container_of(work,
+ struct tps6507x_ts, work.work);
+ struct input_dev *input_dev = tsc->input_dev;
+ int pendown;
+ int schd;
+ int poll = 0;
+ s32 ret;
+
+ ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
+ &tsc->tc.pressure);
+ if (ret)
+ goto done;
+
+ pendown = tsc->tc.pressure > tsc->min_pressure;
+
+ if (unlikely(!pendown && tsc->pendown)) {
+ dev_dbg(tsc->dev, "UP\n");
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_sync(input_dev);
+ tsc->pendown = 0;
+ }
+
+ if (pendown) {
+
+ if (!tsc->pendown) {
+ dev_dbg(tsc->dev, "DOWN\n");
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ } else
+ dev_dbg(tsc->dev, "still down\n");
+
+ ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION,
+ &tsc->tc.x);
+ if (ret)
+ goto done;
+
+ ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION,
+ &tsc->tc.y);
+ if (ret)
+ goto done;
+
+ input_report_abs(input_dev, ABS_X, tsc->tc.x);
+ input_report_abs(input_dev, ABS_Y, tsc->tc.y);
+ input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
+ input_sync(input_dev);
+ tsc->pendown = 1;
+ poll = 1;
+ }
+
+done:
+ /* always poll if not using interrupts */
+ poll = 1;
+
+ if (poll) {
+ schd = schedule_delayed_work(&tsc->work,
+ msecs_to_jiffies(tsc->poll_period));
+ if (schd)
+ tsc->polling = 1;
+ else {
+ tsc->polling = 0;
+ dev_err(tsc->dev, "re-schedule failed");
+ }
+ } else
+ tsc->polling = 0;
+
+ ret = tps6507x_adc_standby(tsc);
+}
+
+static int tps6507x_ts_probe(struct platform_device *pdev)
+{
+ int error;
+ struct tps6507x_ts *tsc;
+ struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
+ struct touchscreen_init_data *init_data;
+ struct input_dev *input_dev;
+ struct tps6507x_board *tps_board;
+ int schd;
+
+ /**
+ * tps_board points to pmic related constants
+ * coming from the board-evm file.
+ */
+
+ tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data;
+
+ if (!tps_board) {
+ dev_err(tps6507x_dev->dev,
+ "Could not find tps6507x platform data\n");
+ return -EIO;
+ }
+
+ /**
+ * init_data points to array of regulator_init structures
+ * coming from the board-evm file.
+ */
+
+ init_data = tps_board->tps6507x_ts_init_data;
+
+ tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
+ if (!tsc) {
+ dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
+ error = -ENOMEM;
+ goto err0;
+ }
+
+ tps6507x_dev->ts = tsc;
+ tsc->mfd = tps6507x_dev;
+ tsc->dev = tps6507x_dev->dev;
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(tsc->dev, "Failed to allocate input device.\n");
+ error = -ENOMEM;
+ goto err1;
+ }
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
+
+ input_dev->name = "TPS6507x Touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = tsc->dev;
+
+ snprintf(tsc->phys, sizeof(tsc->phys),
+ "%s/input0", dev_name(tsc->dev));
+ input_dev->phys = tsc->phys;
+
+ dev_dbg(tsc->dev, "device: %s\n", input_dev->phys);
+
+ input_set_drvdata(input_dev, tsc);
+
+ tsc->input_dev = input_dev;
+
+ INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler);
+
+ if (init_data) {
+ tsc->poll_period = init_data->poll_period;
+ tsc->vref = init_data->vref;
+ tsc->min_pressure = init_data->min_pressure;
+ input_dev->id.vendor = init_data->vendor;
+ input_dev->id.product = init_data->product;
+ input_dev->id.version = init_data->version;
+ } else {
+ tsc->poll_period = TSC_DEFAULT_POLL_PERIOD;
+ tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE;
+ }
+
+ error = tps6507x_adc_standby(tsc);
+ if (error)
+ goto err2;
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err2;
+
+ schd = schedule_delayed_work(&tsc->work,
+ msecs_to_jiffies(tsc->poll_period));
+
+ if (schd)
+ tsc->polling = 1;
+ else {
+ tsc->polling = 0;
+ dev_err(tsc->dev, "schedule failed");
+ goto err2;
+ }
+ platform_set_drvdata(pdev, tps6507x_dev);
+
+ return 0;
+
+err2:
+ cancel_delayed_work_sync(&tsc->work);
+ input_free_device(input_dev);
+err1:
+ kfree(tsc);
+ tps6507x_dev->ts = NULL;
+err0:
+ return error;
+}
+
+static int __devexit tps6507x_ts_remove(struct platform_device *pdev)
+{
+ struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
+ struct tps6507x_ts *tsc = tps6507x_dev->ts;
+ struct input_dev *input_dev = tsc->input_dev;
+
+ cancel_delayed_work_sync(&tsc->work);
+
+ input_unregister_device(input_dev);
+
+ tps6507x_dev->ts = NULL;
+ kfree(tsc);
+
+ return 0;
+}
+
+static struct platform_driver tps6507x_ts_driver = {
+ .driver = {
+ .name = "tps6507x-ts",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps6507x_ts_probe,
+ .remove = __devexit_p(tps6507x_ts_remove),
+};
+module_platform_driver(tps6507x_ts_driver);
+
+MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
+MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps6507x-ts");
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
new file mode 100644
index 00000000..b6adeaee
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2005.c
@@ -0,0 +1,754 @@
+/*
+ * TSC2005 touchscreen driver
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ *
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2005.h>
+
+/*
+ * The touchscreen interface operates as follows:
+ *
+ * 1) Pen is pressed against the touchscreen.
+ * 2) TSC2005 performs AD conversion.
+ * 3) After the conversion is done TSC2005 drives DAV line down.
+ * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled.
+ * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2
+ * values.
+ * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up
+ * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms).
+ * 7) When the penup timer expires, there have not been touch or DAV interrupts
+ * during the last 40ms which means the pen has been lifted.
+ *
+ * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond
+ * after a configurable period (in ms) of activity. If esd_timeout is 0, the
+ * watchdog is disabled.
+ */
+
+/* control byte 1 */
+#define TSC2005_CMD 0x80
+#define TSC2005_CMD_NORMAL 0x00
+#define TSC2005_CMD_STOP 0x01
+#define TSC2005_CMD_12BIT 0x04
+
+/* control byte 0 */
+#define TSC2005_REG_READ 0x0001
+#define TSC2005_REG_PND0 0x0002
+#define TSC2005_REG_X 0x0000
+#define TSC2005_REG_Y 0x0008
+#define TSC2005_REG_Z1 0x0010
+#define TSC2005_REG_Z2 0x0018
+#define TSC2005_REG_TEMP_HIGH 0x0050
+#define TSC2005_REG_CFR0 0x0060
+#define TSC2005_REG_CFR1 0x0068
+#define TSC2005_REG_CFR2 0x0070
+
+/* configuration register 0 */
+#define TSC2005_CFR0_PRECHARGE_276US 0x0040
+#define TSC2005_CFR0_STABTIME_1MS 0x0300
+#define TSC2005_CFR0_CLOCK_1MHZ 0x1000
+#define TSC2005_CFR0_RESOLUTION12 0x2000
+#define TSC2005_CFR0_PENMODE 0x8000
+#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \
+ TSC2005_CFR0_CLOCK_1MHZ | \
+ TSC2005_CFR0_RESOLUTION12 | \
+ TSC2005_CFR0_PRECHARGE_276US | \
+ TSC2005_CFR0_PENMODE)
+
+/* bits common to both read and write of configuration register 0 */
+#define TSC2005_CFR0_RW_MASK 0x3fff
+
+/* configuration register 1 */
+#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003
+#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS
+
+/* configuration register 2 */
+#define TSC2005_CFR2_MAVE_Z 0x0004
+#define TSC2005_CFR2_MAVE_Y 0x0008
+#define TSC2005_CFR2_MAVE_X 0x0010
+#define TSC2005_CFR2_AVG_7 0x0800
+#define TSC2005_CFR2_MEDIUM_15 0x3000
+#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \
+ TSC2005_CFR2_MAVE_Y | \
+ TSC2005_CFR2_MAVE_Z | \
+ TSC2005_CFR2_MEDIUM_15 | \
+ TSC2005_CFR2_AVG_7)
+
+#define MAX_12BIT 0xfff
+#define TSC2005_SPI_MAX_SPEED_HZ 10000000
+#define TSC2005_PENUP_TIME_MS 40
+
+struct tsc2005_spi_rd {
+ struct spi_transfer spi_xfer;
+ u32 spi_tx;
+ u32 spi_rx;
+};
+
+struct tsc2005 {
+ struct spi_device *spi;
+
+ struct spi_message spi_read_msg;
+ struct tsc2005_spi_rd spi_x;
+ struct tsc2005_spi_rd spi_y;
+ struct tsc2005_spi_rd spi_z1;
+ struct tsc2005_spi_rd spi_z2;
+
+ struct input_dev *idev;
+ char phys[32];
+
+ struct mutex mutex;
+
+ /* raw copy of previous x,y,z */
+ int in_x;
+ int in_y;
+ int in_z1;
+ int in_z2;
+
+ spinlock_t lock;
+ struct timer_list penup_timer;
+
+ unsigned int esd_timeout;
+ struct delayed_work esd_work;
+ unsigned long last_valid_interrupt;
+
+ unsigned int x_plate_ohm;
+
+ bool opened;
+ bool suspended;
+
+ bool pen_down;
+
+ void (*set_reset)(bool enable);
+};
+
+static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
+{
+ u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
+ struct spi_transfer xfer = {
+ .tx_buf = &tx,
+ .len = 1,
+ .bits_per_word = 8,
+ };
+ struct spi_message msg;
+ int error;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error) {
+ dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n",
+ __func__, cmd, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
+{
+ u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value;
+ struct spi_transfer xfer = {
+ .tx_buf = &tx,
+ .len = 4,
+ .bits_per_word = 24,
+ };
+ struct spi_message msg;
+ int error;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error) {
+ dev_err(&ts->spi->dev,
+ "%s: failed, register: %x, value: %x, error: %d\n",
+ __func__, reg, value, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last)
+{
+ memset(rd, 0, sizeof(*rd));
+
+ rd->spi_tx = (reg | TSC2005_REG_READ) << 16;
+ rd->spi_xfer.tx_buf = &rd->spi_tx;
+ rd->spi_xfer.rx_buf = &rd->spi_rx;
+ rd->spi_xfer.len = 4;
+ rd->spi_xfer.bits_per_word = 24;
+ rd->spi_xfer.cs_change = !last;
+}
+
+static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value)
+{
+ struct tsc2005_spi_rd spi_rd;
+ struct spi_message msg;
+ int error;
+
+ tsc2005_setup_read(&spi_rd, reg, true);
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&spi_rd.spi_xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error)
+ return error;
+
+ *value = spi_rd.spi_rx;
+ return 0;
+}
+
+static void tsc2005_update_pen_state(struct tsc2005 *ts,
+ int x, int y, int pressure)
+{
+ if (pressure) {
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, pressure);
+ if (!ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, !!pressure);
+ ts->pen_down = true;
+ }
+ } else {
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ if (ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, 0);
+ ts->pen_down = false;
+ }
+ }
+ input_sync(ts->idev);
+ dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y,
+ pressure);
+}
+
+static irqreturn_t tsc2005_irq_thread(int irq, void *_ts)
+{
+ struct tsc2005 *ts = _ts;
+ unsigned long flags;
+ unsigned int pressure;
+ u32 x, y;
+ u32 z1, z2;
+ int error;
+
+ /* read the coordinates */
+ error = spi_sync(ts->spi, &ts->spi_read_msg);
+ if (unlikely(error))
+ goto out;
+
+ x = ts->spi_x.spi_rx;
+ y = ts->spi_y.spi_rx;
+ z1 = ts->spi_z1.spi_rx;
+ z2 = ts->spi_z2.spi_rx;
+
+ /* validate position */
+ if (unlikely(x > MAX_12BIT || y > MAX_12BIT))
+ goto out;
+
+ /* Skip reading if the pressure components are out of range */
+ if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2))
+ goto out;
+
+ /*
+ * Skip point if this is a pen down with the exact same values as
+ * the value before pen-up - that implies SPI fed us stale data
+ */
+ if (!ts->pen_down &&
+ ts->in_x == x && ts->in_y == y &&
+ ts->in_z1 == z1 && ts->in_z2 == z2) {
+ goto out;
+ }
+
+ /*
+ * At this point we are happy we have a valid and useful reading.
+ * Remember it for later comparisons. We may now begin downsampling.
+ */
+ ts->in_x = x;
+ ts->in_y = y;
+ ts->in_z1 = z1;
+ ts->in_z2 = z2;
+
+ /* Compute touch pressure resistance using equation #1 */
+ pressure = x * (z2 - z1) / z1;
+ pressure = pressure * ts->x_plate_ohm / 4096;
+ if (unlikely(pressure > MAX_12BIT))
+ goto out;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ tsc2005_update_pen_state(ts, x, y, pressure);
+ mod_timer(&ts->penup_timer,
+ jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS));
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+ ts->last_valid_interrupt = jiffies;
+out:
+ return IRQ_HANDLED;
+}
+
+static void tsc2005_penup_timer(unsigned long data)
+{
+ struct tsc2005 *ts = (struct tsc2005 *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+ tsc2005_update_pen_state(ts, 0, 0, 0);
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static void tsc2005_start_scan(struct tsc2005 *ts)
+{
+ tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
+ tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
+ tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
+ tsc2005_cmd(ts, TSC2005_CMD_NORMAL);
+}
+
+static void tsc2005_stop_scan(struct tsc2005 *ts)
+{
+ tsc2005_cmd(ts, TSC2005_CMD_STOP);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc2005_disable(struct tsc2005 *ts)
+{
+ tsc2005_stop_scan(ts);
+
+ disable_irq(ts->spi->irq);
+ del_timer_sync(&ts->penup_timer);
+
+ cancel_delayed_work_sync(&ts->esd_work);
+
+ enable_irq(ts->spi->irq);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc2005_enable(struct tsc2005 *ts)
+{
+ tsc2005_start_scan(ts);
+
+ if (ts->esd_timeout && ts->set_reset) {
+ ts->last_valid_interrupt = jiffies;
+ schedule_delayed_work(&ts->esd_work,
+ round_jiffies_relative(
+ msecs_to_jiffies(ts->esd_timeout)));
+ }
+
+}
+
+static ssize_t tsc2005_selftest_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+ u16 temp_high;
+ u16 temp_high_orig;
+ u16 temp_high_test;
+ bool success = true;
+ int error;
+
+ mutex_lock(&ts->mutex);
+
+ /*
+ * Test TSC2005 communications via temp high register.
+ */
+ __tsc2005_disable(ts);
+
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d\n", error);
+ success = false;
+ goto out;
+ }
+
+ temp_high_test = (temp_high_orig - 1) & MAX_12BIT;
+
+ error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test);
+ if (error) {
+ dev_warn(dev, "selftest failed: write error %d\n", error);
+ success = false;
+ goto out;
+ }
+
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d after write\n",
+ error);
+ success = false;
+ goto out;
+ }
+
+ if (temp_high != temp_high_test) {
+ dev_warn(dev, "selftest failed: %d != %d\n",
+ temp_high, temp_high_test);
+ success = false;
+ }
+
+ /* hardware reset */
+ ts->set_reset(false);
+ usleep_range(100, 500); /* only 10us required */
+ ts->set_reset(true);
+
+ if (!success)
+ goto out;
+
+ /* test that the reset really happened */
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d after reset\n",
+ error);
+ success = false;
+ goto out;
+ }
+
+ if (temp_high != temp_high_orig) {
+ dev_warn(dev, "selftest failed after reset: %d != %d\n",
+ temp_high, temp_high_orig);
+ success = false;
+ }
+
+out:
+ __tsc2005_enable(ts);
+ mutex_unlock(&ts->mutex);
+
+ return sprintf(buf, "%d\n", success);
+}
+
+static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL);
+
+static struct attribute *tsc2005_attrs[] = {
+ &dev_attr_selftest.attr,
+ NULL
+};
+
+static umode_t tsc2005_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_selftest.attr) {
+ if (!ts->set_reset)
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static const struct attribute_group tsc2005_attr_group = {
+ .is_visible = tsc2005_attr_is_visible,
+ .attrs = tsc2005_attrs,
+};
+
+static void tsc2005_esd_work(struct work_struct *work)
+{
+ struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work);
+ int error;
+ u16 r;
+
+ if (!mutex_trylock(&ts->mutex)) {
+ /*
+ * If the mutex is taken, it means that disable or enable is in
+ * progress. In that case just reschedule the work. If the work
+ * is not needed, it will be canceled by disable.
+ */
+ goto reschedule;
+ }
+
+ if (time_is_after_jiffies(ts->last_valid_interrupt +
+ msecs_to_jiffies(ts->esd_timeout)))
+ goto out;
+
+ /* We should be able to read register without disabling interrupts. */
+ error = tsc2005_read(ts, TSC2005_REG_CFR0, &r);
+ if (!error &&
+ !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) {
+ goto out;
+ }
+
+ /*
+ * If we could not read our known value from configuration register 0
+ * then we should reset the controller as if from power-up and start
+ * scanning again.
+ */
+ dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n");
+
+ disable_irq(ts->spi->irq);
+ del_timer_sync(&ts->penup_timer);
+
+ tsc2005_update_pen_state(ts, 0, 0, 0);
+
+ ts->set_reset(false);
+ usleep_range(100, 500); /* only 10us required */
+ ts->set_reset(true);
+
+ enable_irq(ts->spi->irq);
+ tsc2005_start_scan(ts);
+
+out:
+ mutex_unlock(&ts->mutex);
+reschedule:
+ /* re-arm the watchdog */
+ schedule_delayed_work(&ts->esd_work,
+ round_jiffies_relative(
+ msecs_to_jiffies(ts->esd_timeout)));
+}
+
+static int tsc2005_open(struct input_dev *input)
+{
+ struct tsc2005 *ts = input_get_drvdata(input);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended)
+ __tsc2005_enable(ts);
+
+ ts->opened = true;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static void tsc2005_close(struct input_dev *input)
+{
+ struct tsc2005 *ts = input_get_drvdata(input);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended)
+ __tsc2005_disable(ts);
+
+ ts->opened = false;
+
+ mutex_unlock(&ts->mutex);
+}
+
+static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts)
+{
+ tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false);
+ tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false);
+ tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false);
+ tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true);
+
+ spi_message_init(&ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg);
+}
+
+static int __devinit tsc2005_probe(struct spi_device *spi)
+{
+ const struct tsc2005_platform_data *pdata = spi->dev.platform_data;
+ struct tsc2005 *ts;
+ struct input_dev *input_dev;
+ unsigned int max_x, max_y, max_p;
+ unsigned int fudge_x, fudge_y, fudge_p;
+ int error;
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ fudge_x = pdata->ts_x_fudge ? : 4;
+ fudge_y = pdata->ts_y_fudge ? : 8;
+ fudge_p = pdata->ts_pressure_fudge ? : 2;
+ max_x = pdata->ts_x_max ? : MAX_12BIT;
+ max_y = pdata->ts_y_max ? : MAX_12BIT;
+ max_p = pdata->ts_pressure_max ? : MAX_12BIT;
+
+ if (spi->irq <= 0) {
+ dev_dbg(&spi->dev, "no irq\n");
+ return -ENODEV;
+ }
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ if (!spi->max_speed_hz)
+ spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ;
+
+ error = spi_setup(spi);
+ if (error)
+ return error;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->spi = spi;
+ ts->idev = input_dev;
+
+ ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
+ ts->esd_timeout = pdata->esd_timeout_ms;
+ ts->set_reset = pdata->set_reset;
+
+ mutex_init(&ts->mutex);
+
+ spin_lock_init(&ts->lock);
+ setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts);
+
+ INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work);
+
+ tsc2005_setup_spi_xfer(ts);
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input-ts", dev_name(&spi->dev));
+
+ input_dev->name = "TSC2005 touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_SPI;
+ input_dev->dev.parent = &spi->dev;
+ input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
+
+ input_dev->open = tsc2005_open;
+ input_dev->close = tsc2005_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ /* Ensure the touchscreen is off */
+ tsc2005_stop_scan(ts);
+
+ error = request_threaded_irq(spi->irq, NULL, tsc2005_irq_thread,
+ IRQF_TRIGGER_RISING, "tsc2005", ts);
+ if (error) {
+ dev_err(&spi->dev, "Failed to request irq, err: %d\n", error);
+ goto err_free_mem;
+ }
+
+ spi_set_drvdata(spi, ts);
+ error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group);
+ if (error) {
+ dev_err(&spi->dev,
+ "Failed to create sysfs attributes, err: %d\n", error);
+ goto err_clear_drvdata;
+ }
+
+ error = input_register_device(ts->idev);
+ if (error) {
+ dev_err(&spi->dev,
+ "Failed to register input device, err: %d\n", error);
+ goto err_remove_sysfs;
+ }
+
+ irq_set_irq_wake(spi->irq, 1);
+ return 0;
+
+err_remove_sysfs:
+ sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
+err_clear_drvdata:
+ spi_set_drvdata(spi, NULL);
+ free_irq(spi->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ return error;
+}
+
+static int __devexit tsc2005_remove(struct spi_device *spi)
+{
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group);
+
+ free_irq(ts->spi->irq, ts);
+ input_unregister_device(ts->idev);
+ kfree(ts);
+
+ spi_set_drvdata(spi, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tsc2005_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended && ts->opened)
+ __tsc2005_disable(ts);
+
+ ts->suspended = true;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static int tsc2005_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ mutex_lock(&ts->mutex);
+
+ if (ts->suspended && ts->opened)
+ __tsc2005_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume);
+
+static struct spi_driver tsc2005_driver = {
+ .driver = {
+ .name = "tsc2005",
+ .owner = THIS_MODULE,
+ .pm = &tsc2005_pm_ops,
+ },
+ .probe = tsc2005_probe,
+ .remove = __devexit_p(tsc2005_remove),
+};
+
+module_spi_driver(tsc2005_driver);
+
+MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
+MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
new file mode 100644
index 00000000..1473d238
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -0,0 +1,406 @@
+/*
+ * drivers/input/touchscreen/tsc2007.c
+ *
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ * Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ * - ads7846.c
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c/tsc2007.h>
+
+#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
+#define TSC2007_MEASURE_AUX (0x2 << 4)
+#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
+#define TSC2007_ACTIVATE_XN (0x8 << 4)
+#define TSC2007_ACTIVATE_YN (0x9 << 4)
+#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
+#define TSC2007_SETUP (0xb << 4)
+#define TSC2007_MEASURE_X (0xc << 4)
+#define TSC2007_MEASURE_Y (0xd << 4)
+#define TSC2007_MEASURE_Z1 (0xe << 4)
+#define TSC2007_MEASURE_Z2 (0xf << 4)
+
+#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
+#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
+
+#define TSC2007_12BIT (0x0 << 1)
+#define TSC2007_8BIT (0x1 << 1)
+
+#define MAX_12BIT ((1 << 12) - 1)
+
+#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
+
+#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
+#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
+#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
+#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
+#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
+
+struct ts_event {
+ u16 x;
+ u16 y;
+ u16 z1, z2;
+};
+
+struct tsc2007 {
+ struct input_dev *input;
+ char phys[32];
+
+ struct i2c_client *client;
+
+ u16 model;
+ u16 x_plate_ohms;
+ u16 max_rt;
+ unsigned long poll_delay;
+ unsigned long poll_period;
+
+ int irq;
+
+ wait_queue_head_t wait;
+ bool stopped;
+
+ int (*get_pendown_state)(void);
+ void (*clear_penirq)(void);
+};
+
+static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
+{
+ s32 data;
+ u16 val;
+
+ data = i2c_smbus_read_word_data(tsc->client, cmd);
+ if (data < 0) {
+ dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
+ return data;
+ }
+
+ /* The protocol and raw data format from i2c interface:
+ * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
+ * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
+ */
+ val = swab16(data) >> 4;
+
+ dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
+
+ return val;
+}
+
+static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
+{
+ /* y- still on; turn on only y+ (and ADC) */
+ tc->y = tsc2007_xfer(tsc, READ_Y);
+
+ /* turn y- off, x+ on, then leave in lowpower */
+ tc->x = tsc2007_xfer(tsc, READ_X);
+
+ /* turn y+ off, x- on; we'll use formula #1 */
+ tc->z1 = tsc2007_xfer(tsc, READ_Z1);
+ tc->z2 = tsc2007_xfer(tsc, READ_Z2);
+
+ /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+ tsc2007_xfer(tsc, PWRDOWN);
+}
+
+static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
+{
+ u32 rt = 0;
+
+ /* range filtering */
+ if (tc->x == MAX_12BIT)
+ tc->x = 0;
+
+ if (likely(tc->x && tc->z1)) {
+ /* compute touch pressure resistance using equation #1 */
+ rt = tc->z2 - tc->z1;
+ rt *= tc->x;
+ rt *= tsc->x_plate_ohms;
+ rt /= tc->z1;
+ rt = (rt + 2047) >> 12;
+ }
+
+ return rt;
+}
+
+static bool tsc2007_is_pen_down(struct tsc2007 *ts)
+{
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
+ * state, even though this controller has a pressure sensor.
+ * The pressure value can fluctuate for quite a while after
+ * lifting the pen and in some cases may not even settle at the
+ * expected value.
+ *
+ * The only safe way to check for the pen up condition is in the
+ * work function by reading the pen signal state (it's a GPIO
+ * and IRQ). Unfortunately such callback is not always available,
+ * in that case we assume that the pen is down and expect caller
+ * to fall back on the pressure reading.
+ */
+
+ if (!ts->get_pendown_state)
+ return true;
+
+ return ts->get_pendown_state();
+}
+
+static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
+{
+ struct tsc2007 *ts = handle;
+ struct input_dev *input = ts->input;
+ struct ts_event tc;
+ u32 rt;
+
+ while (!ts->stopped && tsc2007_is_pen_down(ts)) {
+
+ /* pen is down, continue with the measurement */
+ tsc2007_read_values(ts, &tc);
+
+ rt = tsc2007_calculate_pressure(ts, &tc);
+
+ if (rt == 0 && !ts->get_pendown_state) {
+ /*
+ * If pressure reported is 0 and we don't have
+ * callback to check pendown state, we have to
+ * assume that pen was lifted up.
+ */
+ break;
+ }
+
+ if (rt <= ts->max_rt) {
+ dev_dbg(&ts->client->dev,
+ "DOWN point(%4d,%4d), pressure (%4u)\n",
+ tc.x, tc.y, rt);
+
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, tc.x);
+ input_report_abs(input, ABS_Y, tc.y);
+ input_report_abs(input, ABS_PRESSURE, rt);
+
+ input_sync(input);
+
+ } else {
+ /*
+ * Sample found inconsistent by debouncing or pressure is
+ * beyond the maximum. Don't report it to user space,
+ * repeat at least once more the measurement.
+ */
+ dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
+ }
+
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(ts->poll_period));
+ }
+
+ dev_dbg(&ts->client->dev, "UP\n");
+
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+
+ if (ts->clear_penirq)
+ ts->clear_penirq();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
+{
+ struct tsc2007 *ts = handle;
+
+ if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
+ return IRQ_WAKE_THREAD;
+
+ if (ts->clear_penirq)
+ ts->clear_penirq();
+
+ return IRQ_HANDLED;
+}
+
+static void tsc2007_stop(struct tsc2007 *ts)
+{
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+
+ disable_irq(ts->irq);
+}
+
+static int tsc2007_open(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+ int err;
+
+ ts->stopped = false;
+ mb();
+
+ enable_irq(ts->irq);
+
+ /* Prepare for touch readings - power down ADC and enable PENIRQ */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ tsc2007_stop(ts);
+ return err;
+ }
+
+ return 0;
+}
+
+static void tsc2007_close(struct input_dev *input_dev)
+{
+ struct tsc2007 *ts = input_get_drvdata(input_dev);
+
+ tsc2007_stop(ts);
+}
+
+static int __devinit tsc2007_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tsc2007 *ts;
+ struct tsc2007_platform_data *pdata = client->dev.platform_data;
+ struct input_dev *input_dev;
+ int err;
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data is required!\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EIO;
+
+ ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->client = client;
+ ts->irq = client->irq;
+ ts->input = input_dev;
+ init_waitqueue_head(&ts->wait);
+
+ ts->model = pdata->model;
+ ts->x_plate_ohms = pdata->x_plate_ohms;
+ ts->max_rt = pdata->max_rt ? : MAX_12BIT;
+ ts->poll_delay = pdata->poll_delay ? : 1;
+ ts->poll_period = pdata->poll_period ? : 1;
+ ts->get_pendown_state = pdata->get_pendown_state;
+ ts->clear_penirq = pdata->clear_penirq;
+
+ if (pdata->x_plate_ohms == 0) {
+ dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
+ err = -EINVAL;
+ goto err_free_mem;
+ }
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ input_dev->name = "TSC2007 Touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_I2C;
+
+ input_dev->open = tsc2007_open;
+ input_dev->close = tsc2007_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
+ pdata->fuzzz, 0);
+
+ if (pdata->init_platform_hw)
+ pdata->init_platform_hw();
+
+ err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
+ IRQF_ONESHOT, client->dev.driver->name, ts);
+ if (err < 0) {
+ dev_err(&client->dev, "irq %d busy?\n", ts->irq);
+ goto err_free_mem;
+ }
+
+ tsc2007_stop(ts);
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, ts);
+
+ return 0;
+
+ err_free_irq:
+ free_irq(ts->irq, ts);
+ if (pdata->exit_platform_hw)
+ pdata->exit_platform_hw();
+ err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ return err;
+}
+
+static int __devexit tsc2007_remove(struct i2c_client *client)
+{
+ struct tsc2007 *ts = i2c_get_clientdata(client);
+ struct tsc2007_platform_data *pdata = client->dev.platform_data;
+
+ free_irq(ts->irq, ts);
+
+ if (pdata->exit_platform_hw)
+ pdata->exit_platform_hw();
+
+ input_unregister_device(ts->input);
+ kfree(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id tsc2007_idtable[] = {
+ { "tsc2007", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
+
+static struct i2c_driver tsc2007_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tsc2007"
+ },
+ .id_table = tsc2007_idtable,
+ .probe = tsc2007_probe,
+ .remove = __devexit_p(tsc2007_remove),
+};
+
+module_i2c_driver(tsc2007_driver);
+
+MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
+MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc40.c b/drivers/input/touchscreen/tsc40.c
new file mode 100644
index 00000000..29d5ed4d
--- /dev/null
+++ b/drivers/input/touchscreen/tsc40.c
@@ -0,0 +1,184 @@
+/*
+ * TSC-40 serial touchscreen driver. It should be compatible with
+ * TSC-10 and 25.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ * License: GPLv2 as published by the FSF.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define PACKET_LENGTH 5
+struct tsc_ser {
+ struct input_dev *dev;
+ struct serio *serio;
+ u32 idx;
+ unsigned char data[PACKET_LENGTH];
+ char phys[32];
+};
+
+static void tsc_process_data(struct tsc_ser *ptsc)
+{
+ struct input_dev *dev = ptsc->dev;
+ u8 *data = ptsc->data;
+ u32 x;
+ u32 y;
+
+ x = ((data[1] & 0x03) << 8) | data[2];
+ y = ((data[3] & 0x03) << 8) | data[4];
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_key(dev, BTN_TOUCH, 1);
+
+ input_sync(dev);
+}
+
+static irqreturn_t tsc_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct tsc_ser *ptsc = serio_get_drvdata(serio);
+ struct input_dev *dev = ptsc->dev;
+
+ ptsc->data[ptsc->idx] = data;
+ switch (ptsc->idx++) {
+ case 0:
+ if (unlikely((data & 0x3e) != 0x10)) {
+ dev_dbg(&serio->dev,
+ "unsynchronized packet start (0x%02x)\n", data);
+ ptsc->idx = 0;
+ } else if (!(data & 0x01)) {
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
+ ptsc->idx = 0;
+ }
+ break;
+
+ case 1:
+ case 3:
+ if (unlikely(data & 0xfc)) {
+ dev_dbg(&serio->dev,
+ "unsynchronized data 0x%02x at offset %d\n",
+ data, ptsc->idx - 1);
+ ptsc->idx = 0;
+ }
+ break;
+
+ case 4:
+ tsc_process_data(ptsc);
+ ptsc->idx = 0;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tsc_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tsc_ser *ptsc;
+ struct input_dev *input_dev;
+ int error;
+
+ ptsc = kzalloc(sizeof(struct tsc_ser), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ptsc || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ ptsc->serio = serio;
+ ptsc->dev = input_dev;
+ snprintf(ptsc->phys, sizeof(ptsc->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "TSC-10/25/40 Serial TouchScreen";
+ input_dev->phys = ptsc->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TSC40;
+ input_dev->id.product = 40;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(ptsc->dev, ABS_X, 0, 0x3ff, 0, 0);
+ input_set_abs_params(ptsc->dev, ABS_Y, 0, 0x3ff, 0, 0);
+ input_set_abs_params(ptsc->dev, ABS_PRESSURE, 0, 0, 0, 0);
+
+ serio_set_drvdata(serio, ptsc);
+
+ error = serio_open(serio, drv);
+ if (error)
+ goto fail2;
+
+ error = input_register_device(ptsc->dev);
+ if (error)
+ goto fail3;
+
+ return 0;
+
+fail3:
+ serio_close(serio);
+fail2:
+ serio_set_drvdata(serio, NULL);
+fail1:
+ input_free_device(input_dev);
+ kfree(ptsc);
+ return error;
+}
+
+static void tsc_disconnect(struct serio *serio)
+{
+ struct tsc_ser *ptsc = serio_get_drvdata(serio);
+
+ serio_close(serio);
+
+ input_unregister_device(ptsc->dev);
+ kfree(ptsc);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+static struct serio_device_id tsc_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TSC40,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(serio, tsc_serio_ids);
+
+#define DRIVER_DESC "TSC-10/25/40 serial touchscreen driver"
+
+static struct serio_driver tsc_drv = {
+ .driver = {
+ .name = "tsc40",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tsc_serio_ids,
+ .interrupt = tsc_interrupt,
+ .connect = tsc_connect,
+ .disconnect = tsc_disconnect,
+};
+
+static int __init tsc_ser_init(void)
+{
+ return serio_register_driver(&tsc_drv);
+}
+module_init(tsc_ser_init);
+
+static void __exit tsc_exit(void)
+{
+ serio_unregister_driver(&tsc_drv);
+}
+module_exit(tsc_exit);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
new file mode 100644
index 00000000..46e83ad5
--- /dev/null
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -0,0 +1,467 @@
+/*
+ * Philips UCB1400 touchscreen driver
+ *
+ * Author: Nicolas Pitre
+ * Created: September 25, 2006
+ * Copyright: MontaVista Software, Inc.
+ *
+ * Spliting done by: Marek Vasut <marek.vasut@gmail.com>
+ * If something doesn't work and it worked before spliting, e-mail me,
+ * dont bother Nicolas please ;-)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This code is heavily based on ucb1x00-*.c copyrighted by Russell King
+ * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has
+ * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ucb1400.h>
+
+#define UCB1400_TS_POLL_PERIOD 10 /* ms */
+
+static bool adcsync;
+static int ts_delay = 55; /* us */
+static int ts_delay_pressure; /* us */
+
+/* Switch to interrupt mode. */
+static void ucb1400_ts_mode_int(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+ UCB_TS_CR_MODE_INT);
+}
+
+/*
+ * Switch to pressure mode, and read pressure. We don't need to wait
+ * here, since both plates are being driven.
+ */
+static unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
+ udelay(ts_delay_pressure);
+
+ return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
+}
+
+/*
+ * Switch to X position mode and measure Y plate. We switch the plate
+ * configuration in pressure mode, then switch to position mode. This
+ * gives a faster response time. Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+ udelay(ts_delay);
+
+ return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
+}
+
+/*
+ * Switch to Y position mode and measure X plate. We switch the plate
+ * configuration in pressure mode, then switch to position mode. This
+ * gives a faster response time. Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+ udelay(ts_delay);
+
+ return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync);
+}
+
+/*
+ * Switch to X plate resistance mode. Set MX to ground, PX to
+ * supply. Measure current.
+ */
+static unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ return ucb1400_adc_read(ucb->ac97, 0, adcsync);
+}
+
+/*
+ * Switch to Y plate resistance mode. Set MY to ground, PY to
+ * supply. Measure current.
+ */
+static unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ return ucb1400_adc_read(ucb->ac97, 0, adcsync);
+}
+
+static int ucb1400_ts_pen_up(struct ucb1400_ts *ucb)
+{
+ unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR);
+
+ return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW);
+}
+
+static void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX);
+}
+
+static void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb)
+{
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
+}
+
+static void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y)
+{
+ input_report_abs(idev, ABS_X, x);
+ input_report_abs(idev, ABS_Y, y);
+ input_report_abs(idev, ABS_PRESSURE, pressure);
+ input_report_key(idev, BTN_TOUCH, 1);
+ input_sync(idev);
+}
+
+static void ucb1400_ts_event_release(struct input_dev *idev)
+{
+ input_report_abs(idev, ABS_PRESSURE, 0);
+ input_report_key(idev, BTN_TOUCH, 0);
+ input_sync(idev);
+}
+
+static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb)
+{
+ unsigned int isr;
+
+ isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+ if (isr & UCB_IE_TSPX)
+ ucb1400_ts_irq_disable(ucb);
+ else
+ dev_dbg(&ucb->ts_idev->dev,
+ "ucb1400: unexpected IE_STATUS = %#x\n", isr);
+}
+
+/*
+ * A restriction with interrupts exists when using the ucb1400, as
+ * the codec read/write routines may sleep while waiting for codec
+ * access completion and uses semaphores for access control to the
+ * AC97 bus. Therefore the driver is forced to use threaded interrupt
+ * handler.
+ */
+static irqreturn_t ucb1400_irq(int irqnr, void *devid)
+{
+ struct ucb1400_ts *ucb = devid;
+ unsigned int x, y, p;
+ bool penup;
+
+ if (unlikely(irqnr != ucb->irq))
+ return IRQ_NONE;
+
+ ucb1400_clear_pending_irq(ucb);
+
+ /* Start with a small delay before checking pendown state */
+ msleep(UCB1400_TS_POLL_PERIOD);
+
+ while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) {
+
+ ucb1400_adc_enable(ucb->ac97);
+ x = ucb1400_ts_read_xpos(ucb);
+ y = ucb1400_ts_read_ypos(ucb);
+ p = ucb1400_ts_read_pressure(ucb);
+ ucb1400_adc_disable(ucb->ac97);
+
+ ucb1400_ts_report_event(ucb->ts_idev, p, x, y);
+
+ wait_event_timeout(ucb->ts_wait, ucb->stopped,
+ msecs_to_jiffies(UCB1400_TS_POLL_PERIOD));
+ }
+
+ ucb1400_ts_event_release(ucb->ts_idev);
+
+ if (!ucb->stopped) {
+ /* Switch back to interrupt mode. */
+ ucb1400_ts_mode_int(ucb);
+ ucb1400_ts_irq_enable(ucb);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void ucb1400_ts_stop(struct ucb1400_ts *ucb)
+{
+ /* Signal IRQ thread to stop polling and disable the handler. */
+ ucb->stopped = true;
+ mb();
+ wake_up(&ucb->ts_wait);
+ disable_irq(ucb->irq);
+
+ ucb1400_ts_irq_disable(ucb);
+ ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0);
+}
+
+/* Must be called with ts->lock held */
+static void ucb1400_ts_start(struct ucb1400_ts *ucb)
+{
+ /* Tell IRQ thread that it may poll the device. */
+ ucb->stopped = false;
+ mb();
+
+ ucb1400_ts_mode_int(ucb);
+ ucb1400_ts_irq_enable(ucb);
+
+ enable_irq(ucb->irq);
+}
+
+static int ucb1400_ts_open(struct input_dev *idev)
+{
+ struct ucb1400_ts *ucb = input_get_drvdata(idev);
+
+ ucb1400_ts_start(ucb);
+
+ return 0;
+}
+
+static void ucb1400_ts_close(struct input_dev *idev)
+{
+ struct ucb1400_ts *ucb = input_get_drvdata(idev);
+
+ ucb1400_ts_stop(ucb);
+}
+
+#ifndef NO_IRQ
+#define NO_IRQ 0
+#endif
+
+/*
+ * Try to probe our interrupt, rather than relying on lots of
+ * hard-coded machine dependencies.
+ */
+static int __devinit ucb1400_ts_detect_irq(struct ucb1400_ts *ucb,
+ struct platform_device *pdev)
+{
+ unsigned long mask, timeout;
+
+ mask = probe_irq_on();
+
+ /* Enable the ADC interrupt. */
+ ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+ /* Cause an ADC interrupt. */
+ ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA);
+ ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
+
+ /* Wait for the conversion to complete. */
+ timeout = jiffies + HZ/2;
+ while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) &
+ UCB_ADC_DAT_VALID)) {
+ cpu_relax();
+ if (time_after(jiffies, timeout)) {
+ dev_err(&pdev->dev, "timed out in IRQ probe\n");
+ probe_irq_off(mask);
+ return -ENODEV;
+ }
+ }
+ ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0);
+
+ /* Disable and clear interrupt. */
+ ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
+ ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+ /* Read triggered interrupt. */
+ ucb->irq = probe_irq_off(mask);
+ if (ucb->irq < 0 || ucb->irq == NO_IRQ)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int __devinit ucb1400_ts_probe(struct platform_device *pdev)
+{
+ struct ucb1400_ts *ucb = pdev->dev.platform_data;
+ int error, x_res, y_res;
+ u16 fcsr;
+
+ ucb->ts_idev = input_allocate_device();
+ if (!ucb->ts_idev) {
+ error = -ENOMEM;
+ goto err;
+ }
+
+ /* Only in case the IRQ line wasn't supplied, try detecting it */
+ if (ucb->irq < 0) {
+ error = ucb1400_ts_detect_irq(ucb, pdev);
+ if (error) {
+ dev_err(&pdev->dev, "IRQ probe failed\n");
+ goto err_free_devs;
+ }
+ }
+ dev_dbg(&pdev->dev, "found IRQ %d\n", ucb->irq);
+
+ init_waitqueue_head(&ucb->ts_wait);
+
+ input_set_drvdata(ucb->ts_idev, ucb);
+
+ ucb->ts_idev->dev.parent = &pdev->dev;
+ ucb->ts_idev->name = "UCB1400 touchscreen interface";
+ ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97,
+ AC97_VENDOR_ID1);
+ ucb->ts_idev->id.product = ucb->id;
+ ucb->ts_idev->open = ucb1400_ts_open;
+ ucb->ts_idev->close = ucb1400_ts_close;
+ ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+ ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ /*
+ * Enable ADC filter to prevent horrible jitter on Colibri.
+ * This also further reduces jitter on boards where ADCSYNC
+ * pin is connected.
+ */
+ fcsr = ucb1400_reg_read(ucb->ac97, UCB_FCSR);
+ ucb1400_reg_write(ucb->ac97, UCB_FCSR, fcsr | UCB_FCSR_AVE);
+
+ ucb1400_adc_enable(ucb->ac97);
+ x_res = ucb1400_ts_read_xres(ucb);
+ y_res = ucb1400_ts_read_yres(ucb);
+ ucb1400_adc_disable(ucb->ac97);
+ dev_dbg(&pdev->dev, "x/y = %d/%d\n", x_res, y_res);
+
+ input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0);
+ input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0);
+ input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0);
+
+ ucb1400_ts_stop(ucb);
+
+ error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "UCB1400", ucb);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to grab irq%d: %d\n", ucb->irq, error);
+ goto err_free_devs;
+ }
+
+ error = input_register_device(ucb->ts_idev);
+ if (error)
+ goto err_free_irq;
+
+ return 0;
+
+err_free_irq:
+ free_irq(ucb->irq, ucb);
+err_free_devs:
+ input_free_device(ucb->ts_idev);
+err:
+ return error;
+}
+
+static int __devexit ucb1400_ts_remove(struct platform_device *pdev)
+{
+ struct ucb1400_ts *ucb = pdev->dev.platform_data;
+
+ free_irq(ucb->irq, ucb);
+ input_unregister_device(ucb->ts_idev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ucb1400_ts_suspend(struct device *dev)
+{
+ struct ucb1400_ts *ucb = dev->platform_data;
+ struct input_dev *idev = ucb->ts_idev;
+
+ mutex_lock(&idev->mutex);
+
+ if (idev->users)
+ ucb1400_ts_start(ucb);
+
+ mutex_unlock(&idev->mutex);
+ return 0;
+}
+
+static int ucb1400_ts_resume(struct device *dev)
+{
+ struct ucb1400_ts *ucb = dev->platform_data;
+ struct input_dev *idev = ucb->ts_idev;
+
+ mutex_lock(&idev->mutex);
+
+ if (idev->users)
+ ucb1400_ts_stop(ucb);
+
+ mutex_unlock(&idev->mutex);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops,
+ ucb1400_ts_suspend, ucb1400_ts_resume);
+
+static struct platform_driver ucb1400_ts_driver = {
+ .probe = ucb1400_ts_probe,
+ .remove = __devexit_p(ucb1400_ts_remove),
+ .driver = {
+ .name = "ucb1400_ts",
+ .owner = THIS_MODULE,
+ .pm = &ucb1400_ts_pm_ops,
+ },
+};
+module_platform_driver(ucb1400_ts_driver);
+
+module_param(adcsync, bool, 0444);
+MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin.");
+
+module_param(ts_delay, int, 0444);
+MODULE_PARM_DESC(ts_delay, "Delay between panel setup and"
+ " position read. Default = 55us.");
+
+module_param(ts_delay_pressure, int, 0444);
+MODULE_PARM_DESC(ts_delay_pressure,
+ "delay between panel setup and pressure read."
+ " Default = 0us.");
+
+MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
new file mode 100644
index 00000000..22cd96f5
--- /dev/null
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -0,0 +1,1690 @@
+/******************************************************************************
+ * usbtouchscreen.c
+ * Driver for USB Touchscreens, supporting those devices:
+ * - eGalax Touchkit
+ * includes eTurboTouch CT-410/510/700
+ * - 3M/Microtouch EX II series
+ * - ITM
+ * - PanJit TouchSet
+ * - eTurboTouch
+ * - Gunze AHL61
+ * - DMC TSC-10/25
+ * - IRTOUCHSYSTEMS/UNITOP
+ * - IdealTEK URTC1000
+ * - General Touch
+ * - GoTop Super_Q2/GogoPen/PenPower tablets
+ * - JASTEC USB touch controller/DigiTech DTR-02U
+ * - Zytronic capacitive touchscreen
+ * - NEXIO/iNexio
+ * - Elo TouchSystems 2700 IntelliTouch
+ * - EasyTouch USB Dual/Multi touch controller from Data Modul
+ *
+ * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Driver is based on touchkitusb.c
+ * - ITM parts are from itmtouch.c
+ * - 3M parts are from mtouchusb.c
+ * - PanJit parts are from an unmerged driver by Lanslott Gish
+ * - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged
+ * driver from Marius Vollmer
+ *
+ *****************************************************************************/
+
+//#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/hid.h>
+
+
+#define DRIVER_VERSION "v0.6"
+#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
+#define DRIVER_DESC "USB Touchscreen Driver"
+
+static bool swap_xy;
+module_param(swap_xy, bool, 0644);
+MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
+
+static bool hwcalib_xy;
+module_param(hwcalib_xy, bool, 0644);
+MODULE_PARM_DESC(hwcalib_xy, "If set hw-calibrated X/Y are used if available");
+
+/* device specifc data/functions */
+struct usbtouch_usb;
+struct usbtouch_device_info {
+ int min_xc, max_xc;
+ int min_yc, max_yc;
+ int min_press, max_press;
+ int rept_size;
+
+ /*
+ * Always service the USB devices irq not just when the input device is
+ * open. This is useful when devices have a watchdog which prevents us
+ * from periodically polling the device. Leave this unset unless your
+ * touchscreen device requires it, as it does consume more of the USB
+ * bandwidth.
+ */
+ bool irq_always;
+
+ void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
+
+ /*
+ * used to get the packet len. possible return values:
+ * > 0: packet len
+ * = 0: skip one byte
+ * < 0: -return value more bytes needed
+ */
+ 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);
+};
+
+/* a usbtouch device */
+struct usbtouch_usb {
+ unsigned char *data;
+ dma_addr_t data_dma;
+ unsigned char *buffer;
+ int buf_len;
+ struct urb *irq;
+ struct usb_interface *interface;
+ struct input_dev *input;
+ struct usbtouch_device_info *type;
+ char name[128];
+ char phys[64];
+ void *priv;
+
+ int x, y;
+ int touch, press;
+};
+
+
+/* device types */
+enum {
+ DEVTYPE_IGNORE = -1,
+ DEVTYPE_EGALAX,
+ DEVTYPE_PANJIT,
+ DEVTYPE_3M,
+ DEVTYPE_ITM,
+ DEVTYPE_ETURBO,
+ DEVTYPE_GUNZE,
+ DEVTYPE_DMC_TSC10,
+ DEVTYPE_IRTOUCH,
+ DEVTYPE_IDEALTEK,
+ DEVTYPE_GENERAL_TOUCH,
+ DEVTYPE_GOTOP,
+ DEVTYPE_JASTEC,
+ DEVTYPE_E2I,
+ DEVTYPE_ZYTRONIC,
+ DEVTYPE_TC45USB,
+ DEVTYPE_NEXIO,
+ DEVTYPE_ELO,
+ DEVTYPE_ETOUCH,
+};
+
+#define USB_DEVICE_HID_CLASS(vend, prod) \
+ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS \
+ | USB_DEVICE_ID_MATCH_INT_PROTOCOL \
+ | USB_DEVICE_ID_MATCH_DEVICE, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = USB_INTERFACE_CLASS_HID, \
+ .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE
+
+static const struct usb_device_id usbtouch_devices[] = {
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+ /* ignore the HID capable devices, handled by usbhid */
+ {USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE},
+ {USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},
+
+ /* normal device IDs */
+ {USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+ {USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+ {USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+ {USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
+ {USB_DEVICE(0x16e3, 0xf9e9), .driver_info = DEVTYPE_ITM},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+ {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+ {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+ {USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+ {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+ {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+ {USB_DEVICE(0x1391, 0x1000), .driver_info = DEVTYPE_IDEALTEK},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+ {USB_DEVICE(0x0dfc, 0x0001), .driver_info = DEVTYPE_GENERAL_TOUCH},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+ {USB_DEVICE(0x08f2, 0x007f), .driver_info = DEVTYPE_GOTOP},
+ {USB_DEVICE(0x08f2, 0x00ce), .driver_info = DEVTYPE_GOTOP},
+ {USB_DEVICE(0x08f2, 0x00f4), .driver_info = DEVTYPE_GOTOP},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+ {USB_DEVICE(0x0f92, 0x0001), .driver_info = DEVTYPE_JASTEC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+ {USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+ {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},
+#endif
+
+#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
+ /* data interface only */
+ {USB_DEVICE_AND_INTERFACE_INFO(0x10f0, 0x2002, 0x0a, 0x00, 0x00),
+ .driver_info = DEVTYPE_NEXIO},
+ {USB_DEVICE_AND_INTERFACE_INFO(0x1870, 0x0001, 0x0a, 0x00, 0x00),
+ .driver_info = DEVTYPE_NEXIO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+ {USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+ {USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH},
+#endif
+
+ {}
+};
+
+
+/*****************************************************************************
+ * e2i Part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+static int e2i_init(struct usbtouch_usb *usbtouch)
+{
+ int ret;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x01, 0x02, 0x0000, 0x0081,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+ dbg("%s - usb_control_msg - E2I_RESET - bytes|err: %d",
+ __func__, ret);
+ return ret;
+}
+
+static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ int tmp = (pkt[0] << 8) | pkt[1];
+ dev->x = (pkt[2] << 8) | pkt[3];
+ dev->y = (pkt[4] << 8) | pkt[5];
+
+ tmp = tmp - 0xA000;
+ dev->touch = (tmp > 0);
+ dev->press = (tmp > 0 ? tmp : 0);
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * eGalax part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+
+#define EGALAX_PKT_TYPE_MASK 0xFE
+#define EGALAX_PKT_TYPE_REPT 0x80
+#define EGALAX_PKT_TYPE_DIAG 0x0A
+
+static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
+ return 0;
+
+ dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);
+ dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+
+static int egalax_get_pkt_len(unsigned char *buf, int len)
+{
+ switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
+ case EGALAX_PKT_TYPE_REPT:
+ return 5;
+
+ case EGALAX_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
+ return buf[1] + 2;
+ }
+
+ return 0;
+}
+#endif
+
+/*****************************************************************************
+ * EasyTouch part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+
+#define ETOUCH_PKT_TYPE_MASK 0xFE
+#define ETOUCH_PKT_TYPE_REPT 0x80
+#define ETOUCH_PKT_TYPE_REPT2 0xB0
+#define ETOUCH_PKT_TYPE_DIAG 0x0A
+
+static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT &&
+ (pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2)
+ return 0;
+
+ dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F);
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+
+static int etouch_get_pkt_len(unsigned char *buf, int len)
+{
+ switch (buf[0] & ETOUCH_PKT_TYPE_MASK) {
+ case ETOUCH_PKT_TYPE_REPT:
+ case ETOUCH_PKT_TYPE_REPT2:
+ return 5;
+
+ case ETOUCH_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
+ return buf[1] + 2;
+ }
+
+ return 0;
+}
+#endif
+
+/*****************************************************************************
+ * PanJit Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+static int panjit_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];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * 3M/Microtouch Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+
+#define MTOUCHUSB_ASYNC_REPORT 1
+#define MTOUCHUSB_RESET 7
+#define MTOUCHUSB_REQ_CTRLLR_ID 10
+
+static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if (hwcalib_xy) {
+ dev->x = (pkt[4] << 8) | pkt[3];
+ dev->y = 0xffff - ((pkt[6] << 8) | pkt[5]);
+ } else {
+ dev->x = (pkt[8] << 8) | pkt[7];
+ dev->y = (pkt[10] << 8) | pkt[9];
+ }
+ dev->touch = (pkt[2] & 0x40) ? 1 : 0;
+
+ return 1;
+}
+
+static int mtouch_init(struct usbtouch_usb *usbtouch)
+{
+ int ret, i;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ MTOUCHUSB_RESET,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d",
+ __func__, ret);
+ if (ret < 0)
+ return ret;
+ msleep(150);
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ MTOUCHUSB_ASYNC_REPORT,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
+ __func__, ret);
+ if (ret >= 0)
+ break;
+ if (ret != -EPIPE)
+ return ret;
+ }
+
+ /* Default min/max xy are the raw values, override if using hw-calib */
+ if (hwcalib_xy) {
+ input_set_abs_params(usbtouch->input, ABS_X, 0, 0xffff, 0, 0);
+ input_set_abs_params(usbtouch->input, ABS_Y, 0, 0xffff, 0, 0);
+ }
+
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * ITM Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ int touch;
+ /*
+ * ITM devices report invalid x/y data if not touched.
+ * if the screen was touched before but is not touched any more
+ * report touch as 0 with the last valid x/y data once. then stop
+ * reporting data until touched again.
+ */
+ dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F);
+
+ touch = ~pkt[7] & 0x20;
+ if (!touch) {
+ if (dev->touch) {
+ dev->touch = 0;
+ return 1;
+ }
+
+ return 0;
+ }
+
+ dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F);
+ dev->touch = touch;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * eTurboTouch part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ unsigned int shift;
+
+ /* packets should start with sync */
+ if (!(pkt[0] & 0x80))
+ return 0;
+
+ shift = (6 - (pkt[0] & 0x03));
+ dev->x = ((pkt[3] << 7) | pkt[4]) >> shift;
+ dev->y = ((pkt[1] << 7) | pkt[2]) >> shift;
+ dev->touch = (pkt[0] & 0x10) ? 1 : 0;
+
+ return 1;
+}
+
+static int eturbo_get_pkt_len(unsigned char *buf, int len)
+{
+ if (buf[0] & 0x80)
+ return 5;
+ if (buf[0] == 0x01)
+ return 3;
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * Gunze part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
+ return 0;
+
+ dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ dev->touch = pkt[0] & 0x20;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * DMC TSC-10/25 Part
+ *
+ * Documentation about the controller and it's protocol can be found at
+ * http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf
+ * http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+
+/* supported data rates. currently using 130 */
+#define TSC10_RATE_POINT 0x50
+#define TSC10_RATE_30 0x40
+#define TSC10_RATE_50 0x41
+#define TSC10_RATE_80 0x42
+#define TSC10_RATE_100 0x43
+#define TSC10_RATE_130 0x44
+#define TSC10_RATE_150 0x45
+
+/* commands */
+#define TSC10_CMD_RESET 0x55
+#define TSC10_CMD_RATE 0x05
+#define TSC10_CMD_DATA1 0x01
+
+static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
+{
+ struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
+ int ret = -ENOMEM;
+ unsigned char *buf;
+
+ buf = kmalloc(2, GFP_NOIO);
+ if (!buf)
+ goto err_nobuf;
+ /* reset */
+ buf[0] = buf[1] = 0xFF;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_RESET,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ goto err_out;
+ if (buf[0] != 0x06) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* set coordinate output rate */
+ buf[0] = buf[1] = 0xFF;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_RATE,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ goto err_out;
+ if ((buf[0] != 0x06) && (buf[0] != 0x15 || buf[1] != 0x01)) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ /* start sending data */
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_DATA1,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+err_out:
+ kfree(buf);
+err_nobuf:
+ return ret;
+}
+
+
+static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[2] & 0x03) << 8) | pkt[1];
+ dev->y = ((pkt[4] & 0x03) << 8) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * IRTOUCH Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[3] << 8) | pkt[2];
+ dev->y = (pkt[5] << 8) | pkt[4];
+ dev->touch = (pkt[1] & 0x03) ? 1 : 0;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * ET&T TC5UH/TC4UM part
+ */
+#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];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * IdealTEK URTC1000 Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+static int idealtek_get_pkt_len(unsigned char *buf, int len)
+{
+ if (buf[0] & 0x80)
+ return 5;
+ if (buf[0] == 0x01)
+ return len;
+ return 0;
+}
+
+static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ switch (pkt[0] & 0x98) {
+ case 0x88:
+ /* touch data in IdealTEK mode */
+ dev->x = (pkt[1] << 5) | (pkt[2] >> 2);
+ dev->y = (pkt[3] << 5) | (pkt[4] >> 2);
+ dev->touch = (pkt[0] & 0x40) ? 1 : 0;
+ return 1;
+
+ case 0x98:
+ /* touch data in MT emulation mode */
+ dev->x = (pkt[2] << 5) | (pkt[1] >> 2);
+ dev->y = (pkt[4] << 5) | (pkt[3] >> 2);
+ dev->touch = (pkt[0] & 0x40) ? 1 : 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+#endif
+
+/*****************************************************************************
+ * General Touch Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[2] << 8) | pkt[1];
+ dev->y = (pkt[4] << 8) | pkt[3];
+ dev->press = pkt[5] & 0xff;
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * GoTop Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+static int gotop_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[1] & 0x38) << 4) | pkt[2];
+ dev->y = ((pkt[1] & 0x07) << 7) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * JASTEC Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[0] & 0x3f) << 6) | (pkt[2] & 0x3f);
+ dev->y = ((pkt[1] & 0x3f) << 6) | (pkt[3] & 0x3f);
+ dev->touch = (pkt[0] & 0x40) >> 6;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * Zytronic Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ switch (pkt[0]) {
+ case 0x3A: /* command response */
+ dbg("%s: Command response %d", __func__, pkt[1]);
+ break;
+
+ case 0xC0: /* down */
+ dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+ dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+ dev->touch = 1;
+ dbg("%s: down %d,%d", __func__, dev->x, dev->y);
+ return 1;
+
+ case 0x80: /* up */
+ dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+ dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+ dev->touch = 0;
+ dbg("%s: up %d,%d", __func__, dev->x, dev->y);
+ return 1;
+
+ default:
+ dbg("%s: Unknown return %d", __func__, pkt[0]);
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+/*****************************************************************************
+ * NEXIO Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+
+#define NEXIO_TIMEOUT 5000
+#define NEXIO_BUFSIZE 1024
+#define NEXIO_THRESHOLD 50
+
+struct nexio_priv {
+ struct urb *ack;
+ unsigned char *ack_buf;
+};
+
+struct nexio_touch_packet {
+ u8 flags; /* 0xe1 = touch, 0xe1 = release */
+ __be16 data_len; /* total bytes of touch data */
+ __be16 x_len; /* bytes for X axis */
+ __be16 y_len; /* bytes for Y axis */
+ u8 data[];
+} __attribute__ ((packed));
+
+static unsigned char nexio_ack_pkt[2] = { 0xaa, 0x02 };
+static unsigned char nexio_init_pkt[4] = { 0x82, 0x04, 0x0a, 0x0f };
+
+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 = usbtouch->priv;
+ int ret = -ENOMEM;
+ int actual_len, i;
+ unsigned char *buf;
+ char *firmware_ver = NULL, *device_name = NULL;
+ int input_ep = 0, output_ep = 0;
+
+ /* find first input and output endpoint */
+ for (i = 0; i < interface->desc.bNumEndpoints; i++) {
+ if (!input_ep &&
+ usb_endpoint_dir_in(&interface->endpoint[i].desc))
+ input_ep = interface->endpoint[i].desc.bEndpointAddress;
+ if (!output_ep &&
+ usb_endpoint_dir_out(&interface->endpoint[i].desc))
+ output_ep = interface->endpoint[i].desc.bEndpointAddress;
+ }
+ if (!input_ep || !output_ep)
+ return -ENXIO;
+
+ buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO);
+ if (!buf)
+ goto out_buf;
+
+ /* two empty reads */
+ for (i = 0; i < 2; i++) {
+ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
+ buf, NEXIO_BUFSIZE, &actual_len,
+ NEXIO_TIMEOUT);
+ if (ret < 0)
+ goto out_buf;
+ }
+
+ /* send init command */
+ memcpy(buf, nexio_init_pkt, sizeof(nexio_init_pkt));
+ ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, output_ep),
+ buf, sizeof(nexio_init_pkt), &actual_len,
+ NEXIO_TIMEOUT);
+ if (ret < 0)
+ goto out_buf;
+
+ /* read replies */
+ for (i = 0; i < 3; i++) {
+ memset(buf, 0, NEXIO_BUFSIZE);
+ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
+ buf, NEXIO_BUFSIZE, &actual_len,
+ NEXIO_TIMEOUT);
+ if (ret < 0 || actual_len < 1 || buf[1] != actual_len)
+ continue;
+ switch (buf[0]) {
+ case 0x83: /* firmware version */
+ if (!firmware_ver)
+ firmware_ver = kstrdup(&buf[2], GFP_NOIO);
+ break;
+ case 0x84: /* device name */
+ if (!device_name)
+ device_name = kstrdup(&buf[2], GFP_NOIO);
+ break;
+ }
+ }
+
+ printk(KERN_INFO "Nexio device: %s, firmware version: %s\n",
+ device_name, firmware_ver);
+
+ kfree(firmware_ver);
+ kfree(device_name);
+
+ 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;
+
+out_buf:
+ kfree(buf);
+ return ret;
+}
+
+static void nexio_exit(struct usbtouch_usb *usbtouch)
+{
+ struct nexio_priv *priv = usbtouch->priv;
+
+ usb_kill_urb(priv->ack);
+ usb_free_urb(priv->ack);
+ kfree(priv->ack_buf);
+ kfree(priv);
+}
+
+static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
+{
+ 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 (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 * 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.
+ * Each byte represents "darkness" percentage (0-100) of one element.
+ * 17" touchscreen reports only 64 x 52 bytes so the resolution is low.
+ * This also means that there's a limited multi-touch capability but
+ * 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 < 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 = x_len; y < data_len; y++) {
+ if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) {
+ begin_y = y - x_len;
+ continue;
+ }
+ if (end_y == -1 &&
+ begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) {
+ end_y = y - 1 - x_len;
+ w = end_x - begin_x;
+ h = end_y - begin_y;
+#if 0
+ /* multi-touch */
+ input_report_abs(usbtouch->input,
+ ABS_MT_TOUCH_MAJOR, max(w,h));
+ input_report_abs(usbtouch->input,
+ ABS_MT_TOUCH_MINOR, min(x,h));
+ input_report_abs(usbtouch->input,
+ ABS_MT_POSITION_X, 2*begin_x+w);
+ input_report_abs(usbtouch->input,
+ ABS_MT_POSITION_Y, 2*begin_y+h);
+ input_report_abs(usbtouch->input,
+ ABS_MT_ORIENTATION, w > h);
+ input_mt_sync(usbtouch->input);
+#endif
+ /* single touch */
+ usbtouch->x = 2 * begin_x + w;
+ usbtouch->y = 2 * begin_y + h;
+ usbtouch->touch = packet->flags & 0x01;
+ begin_y = end_y = -1;
+ return 1;
+ }
+ }
+ begin_x = end_x = -1;
+ }
+
+ }
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * ELO part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+
+static int elo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[3] << 8) | pkt[2];
+ dev->y = (pkt[5] << 8) | pkt[4];
+ dev->touch = pkt[6] > 0;
+ dev->press = pkt[6];
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * the different device descriptors
+ */
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len);
+#endif
+
+static struct usbtouch_device_info usbtouch_dev_info[] = {
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+ [DEVTYPE_ELO] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .max_press = 0xff,
+ .rept_size = 8,
+ .read_data = elo_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+ [DEVTYPE_EGALAX] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 16,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = egalax_get_pkt_len,
+ .read_data = egalax_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+ [DEVTYPE_PANJIT] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .read_data = panjit_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+ [DEVTYPE_3M] = {
+ .min_xc = 0x0,
+ .max_xc = 0x4000,
+ .min_yc = 0x0,
+ .max_yc = 0x4000,
+ .rept_size = 11,
+ .read_data = mtouch_read_data,
+ .init = mtouch_init,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+ [DEVTYPE_ITM] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .max_press = 0xff,
+ .rept_size = 8,
+ .read_data = itm_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+ [DEVTYPE_ETURBO] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 8,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = eturbo_get_pkt_len,
+ .read_data = eturbo_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+ [DEVTYPE_GUNZE] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = gunze_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+ [DEVTYPE_DMC_TSC10] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 5,
+ .init = dmc_tsc10_init,
+ .read_data = dmc_tsc10_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+ [DEVTYPE_IRTOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .read_data = irtouch_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+ [DEVTYPE_IDEALTEK] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = idealtek_get_pkt_len,
+ .read_data = idealtek_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+ [DEVTYPE_GENERAL_TOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 7,
+ .read_data = general_touch_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+ [DEVTYPE_GOTOP] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 4,
+ .read_data = gotop_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+ [DEVTYPE_JASTEC] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = jastec_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+ [DEVTYPE_E2I] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 6,
+ .init = e2i_init,
+ .read_data = e2i_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+ [DEVTYPE_ZYTRONIC] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 5,
+ .read_data = zytronic_read_data,
+ .irq_always = true,
+ },
+#endif
+
+#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 = tc45usb_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+ [DEVTYPE_NEXIO] = {
+ .rept_size = 1024,
+ .irq_always = true,
+ .read_data = nexio_read_data,
+ .alloc = nexio_alloc,
+ .init = nexio_init,
+ .exit = nexio_exit,
+ },
+#endif
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+ [DEVTYPE_ETOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 16,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = etouch_get_pkt_len,
+ .read_data = etouch_read_data,
+ },
+#endif
+};
+
+
+/*****************************************************************************
+ * Generic Part
+ */
+static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len)
+{
+ struct usbtouch_device_info *type = usbtouch->type;
+
+ if (!type->read_data(usbtouch, pkt))
+ return;
+
+ input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
+
+ if (swap_xy) {
+ input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
+ input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
+ } else {
+ input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
+ input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
+ }
+ if (type->max_press)
+ input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
+ input_sync(usbtouch->input);
+}
+
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len)
+{
+ unsigned char *buffer;
+ int pkt_len, pos, buf_len, tmp;
+
+ /* process buffer */
+ if (unlikely(usbtouch->buf_len)) {
+ /* try to get size */
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+
+ /* drop? */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* need to append -pkt_len bytes before able to get size */
+ if (unlikely(pkt_len < 0)) {
+ int append = -pkt_len;
+ if (unlikely(append > len))
+ append = len;
+ if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
+ usbtouch->buf_len += append;
+
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+ if (pkt_len < 0)
+ return;
+ }
+
+ /* append */
+ tmp = pkt_len - usbtouch->buf_len;
+ if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
+ usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len);
+
+ buffer = pkt + tmp;
+ buf_len = len - tmp;
+ } else {
+ buffer = pkt;
+ buf_len = len;
+ }
+
+ /* loop over the received packet, process */
+ pos = 0;
+ while (pos < buf_len) {
+ /* get packet len */
+ pkt_len = usbtouch->type->get_pkt_len(buffer + pos,
+ buf_len - pos);
+
+ /* unknown packet: skip one byte */
+ if (unlikely(!pkt_len)) {
+ pos++;
+ continue;
+ }
+
+ /* full packet: process */
+ if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
+ usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len);
+ } else {
+ /* incomplete packet: save in buffer */
+ memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
+ usbtouch->buf_len = buf_len - pos;
+ return;
+ }
+ pos += pkt_len;
+ }
+
+out_flush_buf:
+ usbtouch->buf_len = 0;
+ return;
+}
+#endif
+
+
+static void usbtouch_irq(struct urb *urb)
+{
+ struct usbtouch_usb *usbtouch = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ETIME:
+ /* this urb is timing out */
+ dbg("%s - urb timed out - was the device unplugged?",
+ __func__);
+ return;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EPIPE:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ 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",
+ __func__, retval);
+}
+
+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)) {
+ r = -EIO;
+ goto out_put;
+ }
+ }
+
+ 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
+(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 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)
+{
+ usb_free_coherent(udev, usbtouch->type->rept_size,
+ usbtouch->data, usbtouch->data_dma);
+ kfree(usbtouch->buffer);
+}
+
+static struct usb_endpoint_descriptor *
+usbtouch_get_input_endpoint(struct usb_host_interface *interface)
+{
+ int i;
+
+ for (i = 0; i < interface->desc.bNumEndpoints; i++)
+ if (usb_endpoint_dir_in(&interface->endpoint[i].desc))
+ return &interface->endpoint[i].desc;
+
+ return NULL;
+}
+
+static int usbtouch_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usbtouch_usb *usbtouch;
+ struct input_dev *input_dev;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usbtouch_device_info *type;
+ int err = -ENOMEM;
+
+ /* some devices are ignored */
+ if (id->driver_info == DEVTYPE_IGNORE)
+ return -ENODEV;
+
+ endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting);
+ if (!endpoint)
+ return -ENXIO;
+
+ usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!usbtouch || !input_dev)
+ goto out_free;
+
+ type = &usbtouch_dev_info[id->driver_info];
+ usbtouch->type = type;
+ if (!type->process_pkt)
+ type->process_pkt = usbtouch_process_pkt;
+
+ usbtouch->data = usb_alloc_coherent(udev, type->rept_size,
+ GFP_KERNEL, &usbtouch->data_dma);
+ if (!usbtouch->data)
+ goto out_free;
+
+ if (type->get_pkt_len) {
+ usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);
+ if (!usbtouch->buffer)
+ goto out_free_buffers;
+ }
+
+ usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usbtouch->irq) {
+ dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
+ goto out_free_buffers;
+ }
+
+ usbtouch->interface = intf;
+ usbtouch->input = input_dev;
+
+ if (udev->manufacturer)
+ strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
+ strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
+ }
+
+ if (!strlen(usbtouch->name))
+ snprintf(usbtouch->name, sizeof(usbtouch->name),
+ "USB Touchscreen %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));
+ strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
+
+ input_dev->name = usbtouch->name;
+ input_dev->phys = usbtouch->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, usbtouch);
+
+ input_dev->open = usbtouch_open;
+ input_dev->close = usbtouch_close;
+
+ 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, type->min_xc, type->max_xc, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
+ if (type->max_press)
+ input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
+ type->max_press, 0, 0);
+
+ if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
+ usb_fill_int_urb(usbtouch->irq, udev,
+ usb_rcvintpipe(udev, endpoint->bEndpointAddress),
+ usbtouch->data, type->rept_size,
+ usbtouch_irq, usbtouch, endpoint->bInterval);
+ else
+ usb_fill_bulk_urb(usbtouch->irq, udev,
+ usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
+ usbtouch->data, type->rept_size,
+ usbtouch_irq, usbtouch);
+
+ usbtouch->irq->dev = udev;
+ usbtouch->irq->transfer_dma = usbtouch->data_dma;
+ usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* 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_do_exit;
+ }
+ }
+
+ err = input_register_device(usbtouch->input);
+ if (err) {
+ dbg("%s - input_register_device failed, err: %d", __func__, err);
+ goto out_do_exit;
+ }
+
+ 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;
+ }
+ }
+
+ return 0;
+
+out_unregister_input:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+out_do_exit:
+ if (type->exit)
+ type->exit(usbtouch);
+out_free_urb:
+ usb_free_urb(usbtouch->irq);
+out_free_buffers:
+ usbtouch_free_buffers(udev, usbtouch);
+out_free:
+ input_free_device(input_dev);
+ kfree(usbtouch);
+ return err;
+}
+
+static void usbtouch_disconnect(struct usb_interface *intf)
+{
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+
+ dbg("%s - called", __func__);
+
+ if (!usbtouch)
+ return;
+
+ dbg("%s - usbtouch is initialized, cleaning up", __func__);
+ usb_set_intfdata(intf, NULL);
+ /* this will stop IO via close */
+ input_unregister_device(usbtouch->input);
+ usb_free_urb(usbtouch->irq);
+ if (usbtouch->type->exit)
+ usbtouch->type->exit(usbtouch);
+ usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
+ kfree(usbtouch);
+}
+
+MODULE_DEVICE_TABLE(usb, usbtouch_devices);
+
+static struct usb_driver usbtouch_driver = {
+ .name = "usbtouchscreen",
+ .probe = usbtouch_probe,
+ .disconnect = usbtouch_disconnect,
+ .suspend = usbtouch_suspend,
+ .resume = usbtouch_resume,
+ .reset_resume = usbtouch_reset_resume,
+ .id_table = usbtouch_devices,
+ .supports_autosuspend = 1,
+};
+
+module_usb_driver(usbtouch_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("touchkitusb");
+MODULE_ALIAS("itmtouch");
+MODULE_ALIAS("mtouchusb");
diff --git a/drivers/input/touchscreen/vt1609_ts/Makefile b/drivers/input/touchscreen/vt1609_ts/Makefile
new file mode 100755
index 00000000..b3e5b2d3
--- /dev/null
+++ b/drivers/input/touchscreen/vt1609_ts/Makefile
@@ -0,0 +1,4 @@
+
+obj-$(CONFIG_TOUCHSCREEN_VT1609) := vt1609_dual.o vt1609_ts.o
+
+
diff --git a/drivers/input/touchscreen/vt1609_ts/vt1609_dual.c b/drivers/input/touchscreen/vt1609_ts/vt1609_dual.c
new file mode 100755
index 00000000..c88872e5
--- /dev/null
+++ b/drivers/input/touchscreen/vt1609_ts/vt1609_dual.c
@@ -0,0 +1,692 @@
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+//#include <linux/spinlock.h>
+#include <linux/input.h>
+#include <linux/cdev.h>
+#include <linux/time.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include "vt1609_ts.h"
+
+
+//#define _DEBUG_
+
+#undef dbg
+#ifdef _DEBUG_
+#define dbg(fmt, args...) printk(KERN_ERR "[%s][%d]: " fmt, __func__ , __LINE__, ## args)
+#else
+#define dbg(fmt, args...)
+#endif
+
+#undef dbg_err
+#define dbg_err(fmt, args...) printk(KERN_ERR "[%s][%d]##ERROR##: " fmt, __func__ , __LINE__, ## args)
+
+#define VT1609_DUAL_VERSION "1.1"
+
+#define DUAL_BUF_LENGTH 17
+#define SINGLE_BUF_LEN 5
+#define MAX_SAMPLE_NUM 5
+#define POLL_TOUT 100
+#define DX_DETLA 4
+#define DX_NUM 5
+
+#define VXY 17
+#define SCALE_X 4
+#define SCALE_Y 2
+#define FILTER_COUNT_T1 2
+#define FILTER_COUNT_T2 5
+#define FILTER_COUNT_2T1 5
+#define SAMPLE_COUNT 4
+#define THRESHOLD_DX 256
+#define THRESHOLD_XY 1300
+#define THRESHOLD_DUAL_CNT 1
+
+#define CHL_X1 0x01
+#define CHL_X2 0x02
+#define CHL_X3 0x03
+#define CHL_Y1 0x04
+#define CHL_Y2 0x05
+#define CHL_Y3 0x06
+
+static int vxy_max_updated = 0;
+static int vx_max = 0;
+static int vy_max = 0;
+
+static int dual_cnt = 0;
+static int prv_dx = 0;
+static int dx1 = 0, dx2 = 0;
+
+static int TwoCT = 0;
+static int FirstCT = 0;
+static int TouchCT = 0 ;
+static int OneTCAfter2 = 0;
+static int TwoTouchFlag = 0;
+static struct vt1603_ts_pos pre,fixpos;
+
+//extern struct vt1603_ts_drvdata *pContext;
+
+struct dual_avg_buf {
+ int num;
+ u32 data[DUAL_BUF_LENGTH];
+};
+
+static struct dual_avg_buf x_buf;
+static struct dual_avg_buf y_buf;
+static struct dual_avg_buf avg_buf;
+
+static int vt1603_ts_get_ux(int *para);
+static int vt1603_ts_get_uy(int *para);
+static int vt1603_ts_get_vx(int *para);
+static int vt1603_ts_get_vy(int *para);
+
+static inline void dual_buf_fill(struct dual_avg_buf *dual_buf, u32 data, int len)
+{
+ dual_buf->data[dual_buf->num % len] = data;
+ dual_buf->num++;
+
+ return ;
+}
+
+static inline u32 dual_buf_avg(struct dual_avg_buf *dual_buf, int len)
+{
+ int i, num;
+ u32 avg = 0;
+ int max, min;
+
+ num = (dual_buf->num < len)? dual_buf->num : len;
+
+ if(num == 1)
+ return dual_buf->data[0];
+ if(num == 2)
+ return (dual_buf->data[0]+dual_buf->data[1])/2;
+
+ max = dual_buf->data[0];
+ min = dual_buf->data[0];
+ for (i = 0; i < num; i++){
+ avg += dual_buf->data[i];
+
+ if(dual_buf->data[i] > max)
+ max = dual_buf->data[i];
+
+ if(dual_buf->data[i] < min)
+ min = dual_buf->data[i];
+ }
+
+ return (avg-max-min )/ (num-2);
+}
+
+static void dual_buf_init(struct dual_avg_buf *dual_buf)
+{
+ memset(dual_buf, 0x00, sizeof(struct dual_avg_buf));
+ return ;
+}
+
+static inline void vt1603_ts_report_dual_pos(struct vt1603_ts_drvdata *ts_drv,
+ struct vt1603_ts_pos *fst,struct vt1603_ts_pos *lst)
+{
+ struct vt1603_ts_pos p1 = *fst;
+ struct vt1603_ts_pos p2 = *lst;
+
+ vt1603_ts_pos_calibration(ts_drv,&p1);
+ vt1603_ts_pos_calibration(ts_drv,&p2);
+ //dbg("Caled pos1 (%d, %d), pos2 (%d, %d)\n", p1.x, p1.y, p2.x, p2.y);
+
+ input_report_abs(ts_drv->input, ABS_MT_POSITION_X, p1.x);
+ input_report_abs(ts_drv->input, ABS_MT_POSITION_Y, p1.y);
+ input_mt_sync(ts_drv->input);
+
+ input_report_abs(ts_drv->input, ABS_MT_POSITION_X, p2.x);
+ input_report_abs(ts_drv->input, ABS_MT_POSITION_Y, p2.y);
+ input_mt_sync(ts_drv->input);
+
+ input_sync(ts_drv->input);
+
+ return;
+}
+
+static inline void vt1603_ts_auto_mode(struct vt1603_ts_drvdata *ts_drv)
+{
+ vt1603_set_reg8(ts_drv, VT1603_PWC_REG, 0x08);
+ /* auto position conversion mode and panel type config */
+ vt1603_set_reg8(ts_drv, VT1603_CR_REG, BIT1);
+ /* disable pen up/down detection, it is a MUST opearetion */
+ vt1603_set_reg8(ts_drv, 0xC8, 0x7F);
+
+ return;
+}
+
+static inline int select_channel(struct vt1603_ts_drvdata *ts_drv,int chl)
+{
+ switch(chl){
+ case CHL_X1://select x1
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x05);
+ break;
+ case CHL_X2://select x2
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x04);
+ break;
+ case CHL_X3://select x3
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x00);
+ break;
+ case CHL_Y1://select y1
+ vt1603_set_reg8(ts_drv, 0xc6, 0x21);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x07);
+ break;
+ case CHL_Y2://select y2
+ vt1603_set_reg8(ts_drv, 0xc6, 0x21);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x06);
+ break;
+ case CHL_Y3://select y3
+ vt1603_set_reg8(ts_drv, 0xc6, 0x21);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x03);
+ break;
+ default://default select x1
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x05);
+ break;
+ }
+
+ return 0;
+}
+
+static inline int select_x_channel(struct vt1603_ts_drvdata *ts_drv,int chl)
+{
+ switch(chl){
+ case CHL_X1://select x1
+ vt1603_set_reg8(ts_drv, 0xc6, 0x21);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x07);
+ break;
+ case CHL_X2://select x2
+ vt1603_set_reg8(ts_drv, 0xc6, 0x21);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x06);
+ break;
+ case CHL_X3://select x3
+ vt1603_set_reg8(ts_drv, 0xc6, 0x21);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x03);
+ break;
+ case CHL_Y1://select y1
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x05);
+
+ break;
+ case CHL_Y2://select y2
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x04);
+ break;
+ case CHL_Y3://select y3
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x00);
+ break;
+ default://default select x1
+ vt1603_set_reg8(ts_drv, 0xc6, 0x12);
+ vt1603_set_reg8(ts_drv, 0xc7, 0x05);
+ break;
+ }
+
+ return 0;
+}
+
+static inline int get_channel_data(struct vt1603_ts_drvdata *ts_drv,int chl)
+{
+ int i = 0;
+ int sum = 0;
+ int buf[MAX_SAMPLE_NUM] = {0};
+ u32 now = 0;
+ u8 tmp = 0;
+
+ if(ts_drv->dual_dev.exch)
+ select_x_channel(ts_drv,chl);
+ else
+ select_channel(ts_drv,chl);
+
+ for (i = 0; i < ts_drv->dual_dev.SAMPLE_CNT; i++) {
+ vt1603_clrbits(ts_drv, VT1603_INTS_REG, 0x0f);
+ vt1603_set_reg8(ts_drv, VT1603_CR_REG, 0x10);
+ udelay(100);
+ now = jiffies;
+ while (time_before(jiffies, now + msecs_to_jiffies(POLL_TOUT))) {
+ tmp = vt1603_get_reg8(ts_drv, VT1603_INTS_REG);
+ if (tmp & BIT0) {
+ buf[i] = vt1603_get_reg8(ts_drv, 0xce);
+ tmp = (vt1603_get_reg8(ts_drv, 0xcf) & 0x0f);
+ buf[i] |= (tmp << 8);
+ sum += buf[i];
+ goto next;
+ }
+ }
+ printk("VT1609 %s timeout!\n", __func__);
+ return 0;
+
+ next:
+ ;//printk("CHL %d buf[%d] is %d\n", chl, i, buf[i]);
+ }
+
+ return sum/ts_drv->dual_dev.SAMPLE_CNT;
+
+}
+
+
+static inline int vt1603_get_paramters(struct vt1603_ts_drvdata *ts_drv, int *para)
+{
+ /* change to manual mode now */
+ vt1603_set_reg8(ts_drv, VT1603_CR_REG, 0x00);
+ /* set prechare to 0x10 */
+ vt1603_set_reg8(ts_drv, VT1603_TSPC_REG, 0x10);
+ /* get parameters now */
+ para[0] = get_channel_data(ts_drv, CHL_X1);
+ para[1] = get_channel_data(ts_drv, CHL_X2);
+ para[2] = get_channel_data(ts_drv, CHL_Y1);
+ para[3] = get_channel_data(ts_drv, CHL_Y2);
+
+ para[4] = get_channel_data(ts_drv, CHL_X3);
+ para[5] = get_channel_data(ts_drv, CHL_Y3);
+
+ /* reset adc, this is a MUST operation */
+ vt1603_set_reg8(ts_drv, 0xc8, 0x8f);
+ vt1603_set_reg8(ts_drv, 0xc0, BIT6 | BIT5 | BIT0);
+
+ return 0;
+}
+
+static int vt1603_get_vxy(struct vt1603_ts_drvdata *ts_drv, int *vx, int *vy)
+{
+ int i;
+ int xbuf[5] ={0}, ybuf[5] ={0};
+ int sum_vx = 0,sum_vy = 0;
+ int max_vx = 0,min_vx = 0;
+ int max_vy = 0,min_vy = 0;
+
+ /* change to manual mode now */
+ vt1603_set_reg8(ts_drv, VT1603_CR_REG, 0x00);
+ /* set prechare to 0x10 */
+ vt1603_set_reg8(ts_drv, VT1603_TSPC_REG, 0x10);
+ for(i=0; i<5; i++){
+ xbuf[i] = get_channel_data(ts_drv,CHL_X3);
+ ybuf[i] = get_channel_data(ts_drv,CHL_Y3);
+ sum_vx += xbuf[i];
+ sum_vy += ybuf[i];
+ }
+
+ max_vx = min_vx = xbuf[0];
+ max_vy = min_vy = ybuf[0];
+
+ for(i=0; i<5; i++){
+ if(xbuf[i] > max_vx)
+ max_vx = xbuf[i];
+
+ if(xbuf[i] < min_vx)
+ min_vx = xbuf[i];
+
+ if(ybuf[i] > max_vy)
+ max_vy = ybuf[i];
+
+ if(ybuf[i] < min_vy)
+ min_vy = ybuf[i];
+ }
+ *vx = (sum_vx - max_vx - min_vx)/3;
+ *vy = (sum_vy - max_vy - min_vy)/3;
+ dbg("updated vx_max=%d; vy_max=%d\n",*vx, *vy);
+ /* reset adc, this is a MUST operation */
+ vt1603_set_reg8(ts_drv, 0xc8, 0x8f);
+ vt1603_set_reg8(ts_drv, 0xc0, BIT6 | BIT5 | BIT0);
+
+ return 0;
+}
+
+static inline int vt1603_ts_get_ux(int *para)
+{
+ return abs(para[1] - para[0]);
+}
+
+static inline int vt1603_ts_get_uy(int *para)
+{
+ return abs(para[3] - para[2]);
+}
+
+static inline int vt1603_ts_get_vx(int *para)
+{
+ return abs(vx_max - para[4]);
+}
+
+static inline int vt1603_ts_get_vy(int *para)
+{
+ return abs(vy_max - para[5]);
+}
+
+static inline int vt1603_ts_nTouch(struct vt1603_ts_drvdata *ts_drv, int *para)
+{
+ int ux, uy, vx, vy;
+
+ ux = vt1603_ts_get_ux(para);
+ uy = vt1603_ts_get_uy(para);
+ vx = vt1603_ts_get_vx(para);
+ vy = vt1603_ts_get_vy(para);
+ //printk("ux:%-3d, uy:%-3d, vx:%-3d, vy:%-3d\n", ux, uy, vx, vy);
+
+ if ((vx <= 5) && (vy <= 5)){
+ dual_cnt = 0;
+ return Single_TOUCH;
+ }else if ((vx >= ts_drv->dual_dev.vxy) || (vy >= ts_drv->dual_dev.vxy)){
+ dual_cnt++;
+ //printk("ux:%-3d, uy:%-3d, vx:%-3d, vy:%-3d\n", ux, uy, vx, vy);
+ return (dual_cnt > THRESHOLD_DUAL_CNT)? Multi_TOUCH : Single_TOUCH;
+ }else if (((vx > 5) || (vy > 5)) && ((ux >= 2 * ts_drv->dual_dev.vxy) || (uy >= 2 * ts_drv->dual_dev.vxy))){
+ dual_cnt++;
+ //printk("ux:%-3d, uy:%-3d, vx:%-3d, vy:%-3d\n", ux, uy, vx, vy);
+ return (dual_cnt > THRESHOLD_DUAL_CNT)? Multi_TOUCH : Single_TOUCH;
+ }else{
+ dual_cnt = 0;
+ return Single_TOUCH;
+ }
+
+}
+
+static int pos_fix(const int limit, int p)
+{
+ if (p > limit) p = limit;
+ if (p < 0) p = 0;
+
+ return (u16)(p & 0xffff);
+}
+
+static int vt1603_ts_update_vxy(struct vt1603_ts_drvdata *ts_drv, int *vx, int *vy)
+{
+ u8 val;
+ int timeout = 100;
+
+ val = vt1603_get_reg8(ts_drv, VT1603_CR_REG);
+ while (timeout-- && val != 0x02) {
+ msleep(20);
+ val = vt1603_get_reg8(ts_drv, VT1603_CR_REG);
+ }
+
+ if(!timeout){
+ dbg_err("get vx_max/vy_max failed!\n");
+ goto out;
+ }
+
+ vt1603_get_vxy(ts_drv, vx,vy);
+ dbg("update vx_max:%d, vy_max:%d\n", vx_max, vy_max);
+
+out:
+ vt1603_set_reg8(ts_drv, VT1603_INTS_REG, 0x0F);
+
+ return 0;
+}
+
+void vt1603_ts_dual_support(struct work_struct* dwork)
+{
+ int nTouch = 0;
+ int para[6] = { 0 };
+ //unsigned long flags = 0;
+ int vx = 0, vy = 0, dx = 0;
+ struct vt1603_ts_pos p,pos1, pos2;
+ struct vt1603_ts_drvdata *ts_drv = NULL;
+ u8 int_sts = 0;
+
+ ts_drv = container_of(dwork, struct vt1603_ts_drvdata, dual_work.work);
+
+ //spin_lock_irqsave(&ts_drv->spinlock, flags);
+ mutex_lock(&ts_drv->ts_mutex);
+
+ int_sts = vt1603_get_reg8(ts_drv, VT1603_INTS_REG);
+ if (int_sts & BIT4 || ts_drv->earlysus) {
+ if (jiffies_to_msecs(jiffies - ts_drv->ts_stamp) < TS_DEBOUNCE && !ts_drv->earlysus) {
+ dbg("vt1603 ts debouncing?...\n");
+ //vt1603_clr_ts_irq(ts_drv, int_sts & 0x0F);
+ goto next_loop;
+ }
+ dbg("======= penup ======\n");
+
+ /* update vx_max/vy_max only when first penup */
+ if(!vxy_max_updated){
+ vxy_max_updated ++;
+ vt1603_ts_update_vxy(ts_drv, &vx_max, &vy_max);
+ }
+ vt1603_ts_auto_mode(ts_drv);
+ /* vt1603 gpio1 as IRQ output */
+ vt1603_set_reg8(ts_drv, VT1603_ISEL_REG36, 0x04);
+ input_mt_sync(ts_drv->input);
+ input_sync(ts_drv->input);
+ ts_drv->pen_state = TS_PENUP_STATE;
+ #ifdef TOUCH_KEY
+ vt1603_ts_report_key(ts_drv);
+ #endif
+ dual_buf_init(&avg_buf);
+ dual_buf_init(&x_buf);
+ dual_buf_init(&y_buf);
+
+ dx2 = 0;
+ dx1 = 0;
+ pre.x = 0;
+ pre.y = 0;
+ FirstCT = 0;
+ TwoCT = 0;
+ TouchCT = None_TOUCH;
+ OneTCAfter2 = 0;
+ TwoTouchFlag = 0;
+ vt1603_clr_ts_irq(ts_drv, int_sts & 0x0F);
+
+ if(!ts_drv->earlysus)
+ wmt_gpio_unmask_irq(ts_drv->intgpio);
+
+ //spin_unlock_irqrestore(&ts_drv->spinlock, flags);
+ mutex_unlock(&ts_drv->ts_mutex);
+
+ return;
+ }
+
+ ts_drv->ts_stamp = jiffies;
+ ts_drv->pen_state = TS_PENDOWN_STATE;
+ vt1603_get_paramters(ts_drv, para);
+ vt1603_ts_auto_mode(ts_drv);
+ //vt1603_clr_ts_irq(ts_drv, 0x0F & int_sts);
+
+ nTouch = vt1603_ts_nTouch(ts_drv, para);
+ if(nTouch == Single_TOUCH){
+ p.x = (para[0] + para[1]) / 2;
+ p.y = (para[2] + para[3]) / 2;
+
+ if(TwoTouchFlag ==0 && FirstCT < ts_drv->dual_dev.F1_CNT){
+ FirstCT ++;
+ dbg("Filter First %d Single Touch\n",FirstCT);
+ goto next_loop;
+
+ }else if(TwoTouchFlag == 1 && OneTCAfter2 < ts_drv->dual_dev.F2T1_CNT){
+ dbg("Filter First %d pointer when back to single touch from dual touch\n",OneTCAfter2);
+ dx1 = 0;
+ dx2 = 0;
+ TwoCT = 0;
+ OneTCAfter2 ++;
+ dual_buf_init(&x_buf);
+ dual_buf_init(&y_buf);
+ dual_buf_init(&avg_buf);
+ goto next_loop;
+
+ }else if(p.x > vx_max || p.y > vy_max){
+ dbg("Pos (%d,%d) beyond vx_max or vy_max\n",p.x,p.y);
+ goto next_loop;
+
+ }else if((pre.x!=0 && pre.y!=0) && (abs(pre.x-p.x) > THRESHOLD_XY||abs(pre.y-p.y) > THRESHOLD_XY )){
+ dbg("Threhold Filter Pos (%-4d,%-4d) ,dx=%-4d,dy=%-4d\n",p.x,p.y,abs(pre.x-p.x),abs(pre.y-p.y));
+ pre.x = p.x;
+ pre.y = p.y;
+ goto next_loop;
+
+ }else{
+ dual_buf_fill(&x_buf, p.x, SINGLE_BUF_LEN);
+ dual_buf_fill(&y_buf, p.y, SINGLE_BUF_LEN);
+ p.x = dual_buf_avg(&x_buf, SINGLE_BUF_LEN);
+ p.y = dual_buf_avg(&y_buf, SINGLE_BUF_LEN);
+ dbg("Report PHY Pos (%-4d,%-4d)\n",p.x,p.y);
+ pre.x = p.x;
+ pre.y = p.y;
+ #ifdef TOUCH_KEY
+ if(vt1603_ts_get_key(ts_drv, p))
+ goto next_loop;
+ #endif
+
+ vt1603_ts_report_pos(ts_drv, &p);
+
+ TwoCT = 0;
+ TouchCT = Single_TOUCH;
+ OneTCAfter2 = 0;
+ TwoTouchFlag = 0;
+ goto next_loop;
+ }
+ }
+ else if(nTouch == Multi_TOUCH){
+ vx = vt1603_ts_get_vx(para);
+ vy = vt1603_ts_get_vy(para);
+ dx = ts_drv->dual_dev.scale_y * vy + ts_drv->dual_dev.scale_x * vx;
+
+ if(dx1 && dx2)
+ dx = (dx+dx1+dx2)/3;
+ dx2 = dx1;
+ dx1 = dx;
+ dbg("vx=%-3d, vy=%-3d, dx=%-d, Ddx=%-3d\n",vx,vy,dx,abs(prv_dx-dx));
+
+ if(TwoCT < ts_drv->dual_dev.F2_CNT){
+ TwoCT ++;
+ dual_buf_init(&avg_buf);
+ dbg("Filter The First %d Dual Touch\n",TwoCT);
+ goto next_loop;
+
+ }else if (prv_dx!=0 && (abs(prv_dx - dx) > ts_drv->dual_dev.THR_MAX_DX)){
+ dbg("Threhold Filter Dual Touch dx=%d\n",abs(prv_dx - dx) );
+ prv_dx = dx;
+ goto next_loop;
+
+ }else{
+ //process and report dual touch data
+ dual_buf_fill(&avg_buf, dx, DUAL_BUF_LENGTH);
+ dx = dual_buf_avg(&avg_buf, DUAL_BUF_LENGTH);
+
+ if(abs(prv_dx - dx) < ts_drv->dual_dev.THR_MIN_DX){
+ //printk("Replace with last dx Ddx=%d\n",abs(prv_dx - dx));
+ dx = prv_dx;
+ }
+
+ if(TwoTouchFlag==0 && TouchCT==Single_TOUCH){//Single Touch ->Multi Touch
+ fixpos = pre;
+ //printk("Touch(%-4d,%-4d) 1--->2\n",pre.x,pre.y);
+ }else if(TwoTouchFlag==0 && TouchCT==None_TOUCH){//Multi Touch from the beginning
+ fixpos.x = vx_max/2;
+ fixpos.y = vy_max/2;
+ //printk("Touch(%-4d,%-4d) 2--->2\n",pos1.x, pos1.y);
+ }
+
+ pos1 = fixpos;
+ pos2.x = fixpos.x;
+ if(fixpos.y > vy_max/2)
+ pos2.y = pos_fix(vy_max, (fixpos.y - 150 - dx*DX_DETLA/DX_NUM));
+ else
+ pos2.y = pos_fix(vy_max, (fixpos.y + 150 + dx*DX_DETLA/DX_NUM));
+
+ dbg("PHY dx=%d, pos1.y=%d, pos2.y=%d\n", dx, pos1.y, pos2.y);
+ vt1603_ts_report_dual_pos(ts_drv, &pos1, &pos2);
+
+ prv_dx = dx;
+ TouchCT = Multi_TOUCH;
+ TwoTouchFlag = 1;
+ OneTCAfter2 = 0;
+ #ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0)
+ set_key_led_gpio(ts_drv,HIGH);
+ #endif
+ goto next_loop;
+ }
+ }
+ else{
+ dbg_err("Main Loop Error!\n");
+ }
+
+next_loop:
+ queue_delayed_work(ts_drv->workqueue, &ts_drv->dual_work, msecs_to_jiffies(20));
+ vt1603_clr_ts_irq(ts_drv, 0x0f);
+ //spin_unlock_irqrestore(&ts_drv->spinlock, flags);
+ mutex_unlock(&ts_drv->ts_mutex);
+
+ return ;
+}
+
+int vt1603_dual_init(struct vt1603_ts_drvdata *ts_drv)
+{
+ int ret = 0;
+ //unsigned long flags = 0;
+ int retries = 20;
+ u8 val = 0;
+
+ if (ts_drv == NULL) {
+ printk(KERN_ERR "VT1609 TouchScreen Driver Does Not Exsit!\n");
+ ret = -1;
+ goto out;
+ }
+
+ //spin_lock_irqsave(&ts_drv->spinlock, flags);
+ mutex_lock(&ts_drv->ts_mutex);
+
+ while (retries--) {
+ val = vt1603_get_reg8(ts_drv, VT1603_INTS_REG);
+ if ((val & BIT4) == 0) {
+ printk(KERN_ERR "Do not keep in touching, when vt1609 driver to be installed!\n");
+ msleep(20);
+ continue ;
+ }
+
+ val = vt1603_get_reg8(ts_drv, VT1603_CR_REG);
+ if ( val != 0x02) {
+ printk(KERN_ERR "VT1609 is not working in TS mode now!reg: C1=0x%02x\n",val);
+ msleep(10);
+ continue;
+ }
+
+ break ;
+ }
+
+ if (retries == 0) {
+ printk(KERN_ERR "Enable VT1609 Dual Touch Support Failed!\n");
+ ret = -1;
+ goto out;
+ }
+
+
+ vt1603_set_reg8(ts_drv, VT1603_CDPR_REG, 0x04);
+ vt1603_set_reg8(ts_drv, VT1603_TSPC_REG, 0x10);
+ vt1603_get_vxy(ts_drv, &vx_max, &vy_max);
+ vt1603_ts_auto_mode(ts_drv);
+ vt1603_clr_ts_irq(ts_drv, BIT0 | BIT2 | BIT3);
+
+ dual_buf_init(&avg_buf);
+ dual_buf_init(&x_buf);
+ dual_buf_init(&y_buf);
+
+ printk("VT1609 Dual Touch vx_max=%d,vy_max=%d, vxy=%d ver=%s\n",vx_max, vy_max,ts_drv->dual_dev.vxy,VT1609_DUAL_VERSION);
+out:
+ vt1603_clr_ts_irq(ts_drv, 0x0f);
+ //spin_unlock_irqrestore(&ts_drv->spinlock, flags);
+ mutex_unlock(&ts_drv->ts_mutex);
+
+ return ret;
+}
+
+void vt1603_dual_exit(struct vt1603_ts_drvdata *ts_drv)
+{
+ vt1603_set_reg8(ts_drv, VT1603_CDPR_REG, ts_drv->pdata->sclk_div);
+ vt1603_set_reg8(ts_drv, VT1603_TSPC_REG, 0x20);
+ printk("VT1609 Dual Touch Support Disabled.\n");
+
+ return ;
+}
+
+
diff --git a/drivers/input/touchscreen/vt1609_ts/vt1609_ts.c b/drivers/input/touchscreen/vt1609_ts/vt1609_ts.c
new file mode 100755
index 00000000..f41634fc
--- /dev/null
+++ b/drivers/input/touchscreen/vt1609_ts/vt1609_ts.c
@@ -0,0 +1,1481 @@
+/*
+ * vt1603_mt_i2c.c: VT1603A Touch-Panel Controller and SAR-ADC Driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * History: 2011.Jan.21st, version: 1.00
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+//#include <linux/spinlock.h>
+#include <linux/input.h>
+#include <linux/random.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+
+#include "vt1609_ts.h"
+
+
+//#define VT1609_DEBUG
+
+#undef dbg
+#ifdef VT1609_DEBUG
+#define dbg(fmt, args...) printk(KERN_ERR "[%s][%d]: " fmt, __func__ , __LINE__, ##args)
+#else
+#define dbg(fmt, args...)
+#endif
+
+static struct vt1603_fifo px;
+static struct vt1603_fifo py;
+static struct class *vt1603_ts_class;
+static struct vt1603_ts_pos pre_pos;
+static struct vt1603_ts_cal_info g_CalcParam;
+struct vt1603_ts_drvdata *pContext = NULL;
+
+static int vt1603_ts_isPendown(struct vt1603_ts_drvdata *ts_drv);
+static void vt1603_ts_dev_cleanup(struct vt1603_ts_drvdata *ts_drv);
+
+#ifdef TOUCH_KEY
+static unsigned int key_codes[TOUCH_KEY_NUM] = {
+ [0] = KEY_SEARCH,
+ [1] = KEY_BACK,
+ [2] = KEY_HOME,
+ [3] = KEY_MENU,
+};
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void vt1603ts_early_suspend(struct early_suspend *h);
+static void vt1603ts_late_resume(struct early_suspend *h);
+#endif
+
+#ifdef TOUCH_KEY
+static int setup_led_gpio(struct vt1603_ts_drvdata *ts_drv)
+{
+ if (ts_drv->ledgpio >= 0)
+ gpio_direction_output(ts_drv->ledgpio, 0);
+
+ return 0;
+}
+
+int set_key_led_gpio(struct vt1603_ts_drvdata *ts_drv, int val)
+{
+ if (ts_drv->ledgpio >= 0) {
+ if(val)
+ gpio_direction_output(ts_drv->ledgpio, 1);
+ else
+ gpio_direction_output(ts_drv->ledgpio, 0);
+ }
+
+ return 0;
+}
+
+static void led_timer_func(unsigned long data)
+{
+ set_key_led_gpio((struct vt1603_ts_drvdata *)data,LOW);
+ return;
+}
+
+#endif
+
+
+/*
+ * vt1603_set_reg8 - set register value of vt1603
+ * @ts_drv: vt1603 driver data
+ * @reg: vt1603 register address
+ * @val: value register will be set
+ */
+inline int vt1603_set_reg8(struct vt1603_ts_drvdata *ts_drv, u8 reg, u8 val)
+{
+ int ret =0;
+ if (ts_drv->tdev)
+ ret = ts_drv->tdev->reg_write(ts_drv->tdev,reg,val);
+
+ if(ret)
+ printk("vt1609 ts write error, errno%d\n", ret);
+
+ return ret;
+}
+
+/*
+ * vt1603_get_reg8 - get register value of vt1603
+ * @ts_drv: vt1603 driver data
+ * @reg: vt1603 register address
+ */
+inline u8 vt1603_get_reg8(struct vt1603_ts_drvdata *ts_drv, u8 reg)
+{
+ u8 val = 0;
+ int ret = 0;
+
+ if (ts_drv->tdev)
+ ret = ts_drv->tdev->reg_read(ts_drv->tdev,reg,&val);
+
+ if (ret)
+ printk("vt1609 ts read error, errno%d\n", ret);
+
+ return val;
+}
+
+
+#ifdef VT1609_DEBUG
+/*
+ * vt1603_reg_dump - dubug function, for dump vt1603 related registers
+ * @ts_drv: vt1603 driver data
+ */
+static void vt1603_reg_dump(struct vt1603_ts_drvdata *ts_drv)
+{
+ u8 i;
+ for (i = 0; i < 15; i++)
+ dbg("reg[%d]:0x%02X, reg[%d]:0x%02X\n",
+ i, vt1603_get_reg8(ts_drv, i), i + 0xC0, vt1603_get_reg8(ts_drv, i + 0xC0));
+}
+#endif
+
+/*
+ * vt1603_setbits - write bit1 to related register's bit
+ * @ts_drv: vt1603 driver data
+ * @reg: vt1603 register address
+ * @mask: bit setting mask
+ */
+inline void vt1603_setbits(struct vt1603_ts_drvdata *ts_drv, u8 reg, u8 mask)
+{
+ u8 tmp = 0;
+ tmp = vt1603_get_reg8(ts_drv, reg) | mask;
+ vt1603_set_reg8(ts_drv, reg, tmp);
+
+ return;
+}
+
+
+/*
+ * vt1603_clrbits - write bit0 to related register's bit
+ * @ts_drv: vt1603 driver data
+ * @reg: vt1603 register address
+ * @mask:bit setting mask
+ */
+inline void vt1603_clrbits(struct vt1603_ts_drvdata *ts_drv, u8 reg, u8 mask)
+{
+ u8 tmp = vt1603_get_reg8(ts_drv, reg) & (~mask);
+ vt1603_set_reg8(ts_drv, reg, tmp);
+
+ return;
+}
+
+/*
+ * vt1603_clr_ts_irq - clear touch panel pen down/up and
+ * conversion end/timeout interrupts
+ * @ts_drv: vt1603 driver data
+ * @mask: which interrupt will be cleared
+ */
+inline int vt1603_clr_ts_irq(struct vt1603_ts_drvdata *ts_drv, u8 mask)
+{
+ vt1603_setbits(ts_drv, VT1603_INTS_REG, mask);
+ return 0;
+}
+
+
+/*
+ * Enable I2S CLK, wmt-i2s.c have done this.
+ */
+static void vt1603_ts_clk_enable(void)
+{
+#if 0
+ /* set to 11.288MHz */
+ auto_pll_divisor(DEV_I2S, CLK_ENABLE , 0, 0);
+ auto_pll_divisor(DEV_I2S, SET_PLLDIV, 1, 11288);
+ /*clock = auto_pll_divisor(DEV_I2S, GET_FREQ , 0, 0);
+ info("%s : clock=%d \n" , __func__, clock);*/
+
+ /* Enable BIT4:ARFP clock, BIT3:ARF clock */
+ PMCEU_VAL |= (BIT4 | BIT3);
+
+ /* Enable BIT2:AUD clock */
+ PMCE3_VAL |= BIT2;
+
+ /* disable GPIO and Pull Down mode */
+ GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~0xFF;
+ GPIO_CTRL_GP27_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2);
+
+ GPIO_PULL_EN_GP10_I2S_BYTE_VAL &= ~0xFF;
+ GPIO_PULL_EN_GP27_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2);
+
+ /* set to 2ch input, 2ch output */
+ GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT3 | BIT17 | BIT19 | BIT20 | BIT22);
+ GPIO_PIN_SHARING_SEL_4BYTE_VAL |= (BIT0 | BIT2 | BIT16 | BIT18 | BIT21);
+#endif
+ return;
+}
+
+/*
+ * vt1603_setup_ts_mode - switch to VT1603 TS mode
+ * @ts_drv: vt1603 driver data
+ */
+static int vt1603_setup_ts_mode(struct vt1603_ts_drvdata *ts_drv)
+{
+ int ret = 0;
+ struct vt1603_ts_platform_data *ts_pdata;
+
+ ts_pdata = ts_drv->pdata;
+ ret |= vt1603_set_reg8(ts_drv, VT1603_CDPR_REG, ts_pdata->sclk_div);
+ if (ts_pdata->panel_type == PANEL_TYPE_4WIRED)
+ ret |= vt1603_set_reg8(ts_drv, VT1603_CR_REG, BIT1);
+ else
+ ret |= vt1603_set_reg8(ts_drv, VT1603_CR_REG, BIT1 | BIT0);
+
+ vt1603_clr_ts_irq(ts_drv, 0x0f);
+
+ return (ret < 0)? -1 : 0;
+
+}
+
+
+static int vt1603_fifo_push(struct vt1603_fifo *Fifo, int Data)
+{
+ Fifo->buf[Fifo->head] = Data;
+ Fifo->head++;
+ if(Fifo->head >= VT1603_FIFO_LEN){
+ Fifo->head = 0;
+ Fifo->full = 1;
+ }
+
+ return 0;
+}
+
+static int vt1603_fifo_avg(struct vt1603_fifo Fifo, int *Data)
+{
+ int i=0;
+ int Sum=0,Max=0,Min=0;
+
+ if(!Fifo.full && !Fifo.head)//FIFO is empty
+ return 0;
+
+ if(!Fifo.full ){
+ for(i=0; i<Fifo.head; i++)
+ Sum += Fifo.buf[i];
+
+ *Data = Sum/Fifo.head;
+ return 0;
+ }
+
+ Max = Fifo.buf[0];
+ Min = Fifo.buf[0];
+ for(i=0; i<VT1603_FIFO_LEN; i++){
+ Sum += Fifo.buf[i];
+
+ if(Max < Fifo.buf[i])
+ Max = Fifo.buf[i];
+
+ if(Min > Fifo.buf[i])
+ Min = Fifo.buf[i];
+ }
+ Sum -= Max;
+ Sum -= Min;
+ *Data = Sum/(VT1603_FIFO_LEN-2);
+
+ return 0;
+
+}
+
+
+inline int vt1603_ts_pos_calibration(struct vt1603_ts_drvdata *ts_drv,struct vt1603_ts_pos *to_cal)
+{
+ int x, y;
+
+ x = (g_CalcParam.a1 * to_cal->x + g_CalcParam.b1 * to_cal->y +
+ g_CalcParam.c1) / g_CalcParam.delta;
+ y = (g_CalcParam.a2 * to_cal->x + g_CalcParam.b2 * to_cal->y +
+ g_CalcParam.c2) / g_CalcParam.delta;
+
+ /* pos check */
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+ if (x > ts_drv->resl_x)
+ x = ts_drv->resl_x - 1;
+ if (y > ts_drv->resl_y)
+ y = ts_drv->resl_y - 1;
+
+ if (ts_drv->lcd_exchg) {
+ int tmp;
+ tmp = x;
+ x = y;
+ y = ts_drv->resl_x - tmp;
+ }
+
+ to_cal->x = x;
+ to_cal->y = y;
+
+ return 0;
+}
+
+static inline void vt1603_ts_set_rawdata(struct vt1603_ts_drvdata *ts_drv,struct vt1603_ts_pos *pos)
+{
+ ts_drv->raw_x = pos->x;
+ ts_drv->raw_y = pos->y;
+
+ return;
+}
+
+inline void vt1603_ts_report_pos(struct vt1603_ts_drvdata *ts_drv, struct vt1603_ts_pos *pos)
+{
+ vt1603_ts_set_rawdata(ts_drv,pos);
+ vt1603_ts_pos_calibration(ts_drv,pos);
+
+ input_report_abs(ts_drv->input, ABS_MT_POSITION_X, pos->x);
+ input_report_abs(ts_drv->input, ABS_MT_POSITION_Y, pos->y);
+ input_mt_sync(ts_drv->input);
+ input_sync(ts_drv->input);
+
+ return;
+}
+
+#ifdef TOUCH_KEY
+void vt1603_ts_report_key(struct vt1603_ts_drvdata *ts_drv)
+{
+ if(ts_drv->touch_key_used ){
+
+ if(ts_drv->ledgpio >= 0 )
+ mod_timer(&ts_drv->led_timer, jiffies+10*HZ);
+
+ if(ts_drv->key_pressed && ts_drv->key_idx < _MAX_NUM ){
+ input_report_key(ts_drv->input, key_codes[ts_drv->key_idx], 1);
+ input_sync(ts_drv->input);
+ input_report_key(ts_drv->input, key_codes[ts_drv->key_idx], 0);
+ input_sync(ts_drv->input);
+ dbg("report as key event %d \n",ts_drv->key_idx);
+ }
+ }
+
+ return;
+}
+
+inline int vt1603_ts_get_key(struct vt1603_ts_drvdata *ts_drv,struct vt1603_ts_pos pos)
+{
+ if(ts_drv->touch_key_used){
+
+ if(pos.y > ts_drv->tsc_key.low && pos.y < ts_drv->tsc_key.upper){
+
+ ts_drv->key_pressed = 1;
+ if(pos.x>(ts_drv->tsc_key.key[_SEARCH].pos-ts_drv->tsc_key.delta) &&
+ pos.x<(ts_drv->tsc_key.key[_SEARCH].pos+ts_drv->tsc_key.delta)){
+ ts_drv->key_idx = ts_drv->tsc_key.key[_SEARCH].idx;
+ }
+ else if(pos.x>(ts_drv->tsc_key.key[_BACK].pos-ts_drv->tsc_key.delta) &&
+ pos.x<(ts_drv->tsc_key.key[_BACK].pos+ts_drv->tsc_key.delta)){
+ ts_drv->key_idx = ts_drv->tsc_key.key[_BACK].idx;
+ }
+ else if(pos.x>(ts_drv->tsc_key.key[_HOME].pos-ts_drv->tsc_key.delta) &&
+ pos.x<(ts_drv->tsc_key.key[_HOME].pos+ts_drv->tsc_key.delta)){
+ ts_drv->key_idx = ts_drv->tsc_key.key[_HOME].idx;
+ }
+ else if(pos.x>(ts_drv->tsc_key.key[_MENU].pos-ts_drv->tsc_key.delta) &&
+ pos.x<(ts_drv->tsc_key.key[_MENU].pos+ts_drv->tsc_key.delta)){
+ ts_drv->key_idx = ts_drv->tsc_key.key[_MENU].idx;
+ }
+ else{
+ ts_drv->key_idx = _MAX_NUM;
+ }
+
+ if(ts_drv->key_idx < _MAX_NUM && ts_drv->ledgpio >= 0)
+ set_key_led_gpio(ts_drv,HIGH);
+
+ return 1;
+ }
+
+ if(ts_drv->ledgpio >= 0)
+ set_key_led_gpio(ts_drv,HIGH);
+ }
+
+ ts_drv->key_pressed= 0;
+
+ return 0 ;
+}
+
+#endif
+
+
+/*
+ * vt1603_ts_get_pos - get touch panel touched position from vt1603
+ * conversion register
+ * @ts_drv: vt1603 driver data
+ * @pos: vt1603 touch panel touched point conversion data
+ */
+static inline void vt1603_ts_get_pos(struct vt1603_ts_drvdata *ts_drv, struct vt1603_ts_pos *pos)
+{
+ u8 datal, datah;
+
+ /* get x-position */
+ datal = vt1603_get_reg8(ts_drv, VT1603_XPL_REG);
+ datah = vt1603_get_reg8(ts_drv, VT1603_XPH_REG);
+ pos->x = ADC_DATA(datal, datah);
+
+ /* get y-positin */
+ datal = vt1603_get_reg8(ts_drv, VT1603_YPL_REG);
+ datah = vt1603_get_reg8(ts_drv, VT1603_YPH_REG);
+ pos->y = ADC_DATA(datal, datah);
+ vt1603_clr_ts_irq(ts_drv, BIT0);
+
+ return;
+}
+
+/*
+ * vt1603_ts_isPendown - get touch panel pen state from vt1603
+ * interrup status register
+ * @ts_drv: vt1603 driver data
+ */
+static inline int vt1603_ts_isPendown(struct vt1603_ts_drvdata *ts_drv)
+{
+ u8 state = vt1603_get_reg8(ts_drv, VT1603_INTS_REG);
+
+ if (state & BIT4)
+ return TS_PENUP_STATE;
+ else
+ return TS_PENDOWN_STATE;
+}
+
+
+static inline int vt1603_pos_avg(struct vt1603_ts_pos *pos)
+{
+ vt1603_fifo_push(&px, pos->x);
+ vt1603_fifo_push(&py, pos->y);
+ vt1603_fifo_avg(px, &pos->x);
+ vt1603_fifo_avg(py, &pos->y);
+
+ return 0;
+}
+
+static int vt1603_pos_cleanup(void)
+{
+ px.full = 0;
+ px.head = 0;
+
+ py.full = 0;
+ py.head = 0;
+
+ return 0;
+}
+
+static void vt1603_read_loop(struct work_struct* dwork)
+{
+ struct vt1603_ts_drvdata *ts_drv=NULL;
+ struct vt1603_ts_pos pos;
+
+ ts_drv = container_of(dwork, struct vt1603_ts_drvdata, read_work.work);
+ ts_drv->pen_state= vt1603_ts_isPendown(ts_drv);
+ if((ts_drv->pen_state == TS_PENUP_STATE) ||ts_drv->earlysus){
+ vt1603_clr_ts_irq(ts_drv, 0x0F);
+ if(jiffies_to_msecs(jiffies - ts_drv->ts_stamp) < 80 && !ts_drv->earlysus){
+ //dbg("Debounceing@@@@@@@@\n");
+ goto next_loop;
+ }
+
+ dbg("============== penup ==============\n");
+ vt1603_set_reg8(ts_drv, VT1603_ISEL_REG36, 0x04);/* vt1603 gpio1 as IRQ output */
+ input_mt_sync(ts_drv->input);
+ input_sync(ts_drv->input);
+ #ifdef TOUCH_KEY
+ vt1603_ts_report_key(ts_drv);
+ #endif
+ pre_pos.x = 0;
+ pre_pos.y = 0;
+ ts_drv->hcnt = 0;
+ vt1603_pos_cleanup();
+
+ if(!ts_drv->earlysus)
+ wmt_gpio_unmask_irq(ts_drv->intgpio);
+
+ return;
+ }
+
+ ts_drv->ts_stamp = jiffies;
+ vt1603_ts_get_pos(ts_drv, &pos);
+
+ //Filter the first N point
+ if(ts_drv->hcnt < VT1603_FILTER_HEAD_COUNT){
+ ts_drv->hcnt++;
+ goto next_loop;
+ }
+
+ /* ƽ»¬Â˲¨*/
+ if((pre_pos.x != 0 && pre_pos.y != 0)&&(abs(pre_pos.x-pos.x) > VT1603_JITTER_THRESHOLD||abs(pre_pos.y-pos.y) > VT1603_JITTER_THRESHOLD)){
+ pre_pos.x = pos.x;
+ pre_pos.y = pos.y;
+ goto next_loop;
+ }
+#ifdef TOUCH_KEY
+ if(vt1603_ts_get_key(ts_drv,pos))
+ goto next_loop;
+#endif
+
+ vt1603_pos_avg(&pos);
+ pre_pos.x = pos.x;
+ pre_pos.y = pos.y;
+
+ dbg("x=%d, y=%d\n",pos.x,pos.y);
+ vt1603_ts_report_pos(ts_drv, &pos);
+
+next_loop:
+ queue_delayed_work(ts_drv->workqueue, &ts_drv->read_work, 20*HZ/1000);
+
+ return;
+}
+
+
+static void vt1603_ts_work(struct work_struct *work)
+{
+ int int_sts;
+ //unsigned long flags;
+ struct vt1603_ts_drvdata *ts_drv=pContext;
+
+ //spin_lock_irqsave(&ts_drv->spinlock, flags);
+ mutex_lock(&ts_drv->ts_mutex);
+ int_sts = vt1603_get_reg8(ts_drv, VT1603_INTS_REG);
+ dbg("+++++++ ts int status 0x%02x +++++++\n",int_sts);
+
+ if ((int_sts & BIT4) == 0 || (int_sts & BIT1) == BIT1){
+ if(ts_drv->pen_state == TS_PENUP_STATE){
+ vt1603_set_reg8(ts_drv, VT1603_ISEL_REG36, 0x03);/* vt1603 gpio1 as logic high output */
+ ts_drv->pen_state = TS_PENDOWN_STATE;
+ ts_drv->ts_stamp = jiffies;
+ vt1603_setup_ts_mode(ts_drv);
+ dbg("============= pendown =============\n");
+ vt1603_pos_cleanup();
+ if(ts_drv->dual_enable)
+ vt1603_ts_dual_support(&ts_drv->dual_work.work);
+ else
+ vt1603_read_loop(&ts_drv->read_work.work);
+
+ vt1603_clr_ts_irq(ts_drv, int_sts&0x0f);
+ //spin_unlock_irqrestore(&ts_drv->spinlock, flags);
+ mutex_unlock(&ts_drv->ts_mutex);
+
+ return;
+ }
+ }
+
+ vt1603_clr_ts_irq(ts_drv, int_sts&0x0f);
+ //spin_unlock_irqrestore(&ts_drv->spinlock, flags);
+ mutex_unlock(&ts_drv->ts_mutex);
+ wmt_gpio_unmask_irq(ts_drv->intgpio);
+
+ return ;
+}
+
+
+static irqreturn_t vt1603_ts_isr(int irq, void *dev_id)
+{
+ struct vt1603_ts_drvdata *ts_drv = dev_id;
+
+ if(!gpio_irqstatus(ts_drv->intgpio) ||
+ !is_gpio_irqenable(ts_drv->intgpio))
+ return IRQ_NONE;
+
+ wmt_gpio_ack_irq(ts_drv->intgpio);
+ wmt_gpio_mask_irq(ts_drv->intgpio);
+ schedule_work(&ts_drv->work);
+ dbg("@@@@@@@ touch irq @@@@@@@\n");
+
+ return IRQ_HANDLED;
+}
+
+static int vt1603_register_input(struct vt1603_ts_drvdata * ts_drv)
+{
+ if(strcmp(ts_drv->dev_id,"VT1609"))
+ ts_drv->input->name = "vt1603-touch";
+ else
+ ts_drv->input->name = "vt1609-touch";
+
+ ts_drv->input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ ts_drv->input->propbit[0] = BIT_MASK(INPUT_PROP_DIRECT);
+
+#ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used){
+ int i;
+ for (i = 0; i < TOUCH_KEY_NUM; i++)
+ set_bit(key_codes[i], ts_drv->input->keybit);
+
+ ts_drv->input->keycode = key_codes;
+ ts_drv->input->keycodesize = sizeof(unsigned int);
+ ts_drv->input->keycodemax = TOUCH_KEY_NUM;
+ }
+#endif
+ if (ts_drv->lcd_exchg) {
+ input_set_abs_params(ts_drv->input, ABS_MT_POSITION_X, 0, ts_drv->resl_y, 0, 0);
+ input_set_abs_params(ts_drv->input, ABS_MT_POSITION_Y, 0, ts_drv->resl_x, 0, 0);
+ } else {
+ input_set_abs_params(ts_drv->input, ABS_MT_POSITION_X, 0, ts_drv->resl_x, 0, 0);
+ input_set_abs_params(ts_drv->input, ABS_MT_POSITION_Y, 0, ts_drv->resl_y, 0, 0);
+ }
+
+ input_register_device(ts_drv->input);
+ return 0;
+}
+
+
+/*
+ * vt1603_ts_calibration - vt1603 self calibration routine
+ * @ts_drv: vt1603 driver data
+ */
+static void vt1603_ts_calibration(struct vt1603_ts_drvdata *ts_drv)
+{
+ unsigned char i, j, tmp;
+ unsigned char cal[5][8] = {{0}};
+ unsigned int cal_sum[8] = {0};
+ struct vt1603_ts_platform_data *ts_pdata;
+
+ dbg("Enter\n");
+ ts_pdata = ts_drv->pdata;
+ for (j = 0; j < 5; j++) {
+ tmp = BIT6 | BIT0 | (ts_pdata->cal_sel << 4);
+ vt1603_set_reg8(ts_drv, VT1603_CCCR_REG, tmp);
+ msleep(100);
+ for (i = 0; i < 8; i++)
+ cal[j][i] = vt1603_get_reg8(ts_drv, VT1603_ERR8_REG + i);
+ }
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 5; j++)
+ cal_sum[i] += cal[j][i];
+ tmp = (u8)cal_sum[i]/5;
+ vt1603_set_reg8(ts_drv, VT1603_DBG8_REG + i, tmp);
+ }
+
+ dbg("Exit\n");
+ return ;
+}
+
+/*
+ * vt1603_ts_reset - reset vt1603, auto postition conversion mode,
+ * do self calibration if enable
+ * @ts_drv: vt1603 driver data
+ */
+static void vt1603_ts_reset(struct vt1603_ts_drvdata * ts_drv)
+{
+ struct vt1603_ts_platform_data *ts_pdata;
+ ts_pdata = ts_drv->pdata;
+
+ /* power control enable */
+ vt1603_set_reg8(ts_drv, VT1603_PWC_REG, 0x18);
+ /* begin calibrate if calibration enable */
+ if ((ts_pdata != NULL) && (ts_pdata->cal_en == CALIBRATION_ENABLE)) {
+ vt1603_ts_calibration(ts_drv);
+ }
+
+ /* clock divider */
+ vt1603_set_reg8(ts_drv, VT1603_CDPR_REG, ts_pdata->sclk_div);
+
+ /* clean debug register,for some 2 layer PCB machine enter debug mode unexpected */
+ vt1603_set_reg8(ts_drv, VT1603_DCR_REG, 0x00);
+
+ vt1603_set_reg8(ts_drv, VT1603_INTEN_REG, BIT1);//Just Enable pendown IRQ
+
+ /* auto position conversion mode and panel type config */
+ if (ts_pdata->panel_type== PANEL_TYPE_4WIRED)
+ vt1603_set_reg8(ts_drv, VT1603_CR_REG, BIT1);
+ else
+ vt1603_set_reg8(ts_drv, VT1603_CR_REG, BIT1 | BIT0);
+
+ /* interrupt control, pen up/down detection enable */
+ vt1603_set_reg8(ts_drv, VT1603_INTCR_REG, 0xff);
+
+ /* mask other module interrupts */
+ vt1603_set_reg8(ts_drv, VT1603_IMASK_REG27, 0xff);
+ vt1603_set_reg8(ts_drv, VT1603_IMASK_REG28, 0xFF);
+ vt1603_set_reg8(ts_drv, VT1603_IMASK_REG29, 0xFF);
+ /* reset headphone detect irq */
+ vt1603_set_reg8(ts_drv, VT1603_IMASK_REG27, 0xfd);
+
+ if (ts_pdata->irq_type == HIGH_ACTIVE|| ts_pdata->irq_type == RISING_EDGE_ACTIVE)
+ vt1603_clrbits(ts_drv, VT1603_IPOL_REG33, BIT5);
+ else
+ vt1603_setbits(ts_drv, VT1603_IPOL_REG33, BIT5);
+
+ vt1603_set_reg8(ts_drv, VT1603_ISEL_REG36, 0x04);/* vt1603 gpio1 as IRQ output */
+ /* clear irq */
+ vt1603_clr_ts_irq(ts_drv, 0x0F);
+
+ return;
+}
+
+
+static struct vt1603_ts_platform_data vt1603_ts_pdata = {
+ .panel_type = PANEL_TYPE_4WIRED,
+ .cal_en = CALIBRATION_DISABLE,
+ .cal_sel = 0x00,
+ .shift = 0x00,
+ .sclk_div = 0x08,
+ .irq_type = LOW_ACTIVE,
+};
+
+struct delayed_work resume_work;
+static void vt1603_resume_work(struct work_struct* dwork)
+{
+ struct vt1603_ts_drvdata *ts_drv = pContext;
+ ts_drv->pen_state = TS_PENUP_STATE;
+
+ /* must ensure mclk is available */
+ vt1603_ts_clk_enable();
+ /* vt1603 ts hardware resume */
+ vt1603_ts_reset(ts_drv);
+ /* clear irq before enale gpio irq */
+ vt1603_clr_ts_irq(ts_drv, 0x0f );
+
+ gpio_direction_input(ts_drv->intgpio);
+ wmt_gpio_set_irq_type(ts_drv->intgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ts_drv->intgpio);
+ #ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0)
+ setup_led_gpio(ts_drv);
+#endif
+#ifdef VT1609_DEBUG
+ /* 4. dump vt1603 to ensure setting ok */
+ vt1603_reg_dump(ts_drv);
+#endif
+}
+static __devinit int
+vt1603_ts_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct vt1603_ts_drvdata *ts_drv = pContext;
+ struct vt1603_ts_platform_data *ts_pdata = NULL;
+
+ ts_pdata = &vt1603_ts_pdata;
+
+ ts_drv->pdata = ts_pdata;
+ ts_drv->pen_state = TS_PENUP_STATE;
+ ts_drv->tdev = dev_get_platdata(&pdev->dev);
+
+ //spin_lock_init(&ts_drv->spinlock);
+ mutex_init(&ts_drv->ts_mutex);
+
+ dev_set_drvdata(&pdev->dev, ts_drv);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts_drv->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts_drv->earlysuspend.suspend = vt1603ts_early_suspend;
+ ts_drv->earlysuspend.resume = vt1603ts_late_resume;
+ register_early_suspend(&ts_drv->earlysuspend);
+#endif
+ /* 1. mclk enable */
+ vt1603_ts_clk_enable();
+ /* 2.vt1603 touch-panel and sar-adc module reset */
+ vt1603_ts_reset(ts_drv);
+
+#ifdef VT1609_DEBUG
+ /* 4. dump vt1603 to ensure setting ok */
+ vt1603_reg_dump(ts_drv);
+#endif
+ /* initial battery if battery detection enable */
+ /* initial temperature if temperature detection enable */
+
+ /* request iuput device */
+ ts_drv->input = input_allocate_device();
+ if (!ts_drv->input) {
+ printk("vt1603_ts: alloc input device failed");
+ ret = -ENOMEM;
+ goto release_driver_data;
+ }
+ vt1603_register_input(ts_drv);
+
+ INIT_DELAYED_WORK(&ts_drv->read_work, vt1603_read_loop);
+ INIT_DELAYED_WORK(&ts_drv->dual_work, vt1603_ts_dual_support);
+ INIT_DELAYED_WORK(&resume_work, vt1603_resume_work);
+ ts_drv->workqueue = create_singlethread_workqueue("vt160x-touch");
+ if(!ts_drv->workqueue){
+ printk("vt160x create singlethread work queue failed!\n");
+ goto release_driver_data;
+ }
+
+ INIT_WORK(&ts_drv->work, vt1603_ts_work);
+
+ if (request_irq(ts_drv->gpio_irq, vt1603_ts_isr, IRQF_SHARED, "vt160x-touch", ts_drv)) {
+ printk("vt160x_ts: request IRQ %d failed\n", ts_drv->gpio_irq);
+ ret = -ENODEV;
+ }
+
+ wmt_gpio_set_irq_type(ts_drv->intgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ts_drv->intgpio);
+
+#ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0)
+ setup_led_gpio(ts_drv);
+#endif
+
+ dbg("%s Touch Screen Driver Installed!\n",ts_drv->dev_id);
+ return ret;
+
+release_driver_data:
+ kfree(ts_drv);
+ ts_drv = NULL;
+
+ return ret;
+}
+
+static __devexit int
+vt1603_ts_remove(struct platform_device *pdev)
+{
+ struct vt1603_ts_drvdata *ts_drv;
+ ts_drv = dev_get_drvdata(&pdev->dev);
+
+ dbg("Enter\n");
+
+ wmt_gpio_mask_irq(ts_drv->intgpio);
+ if(ts_drv->dual_enable)
+ vt1603_dual_exit(ts_drv);
+
+ /* input unregister */
+ input_unregister_device(ts_drv->input);
+ cancel_work_sync(&ts_drv->work);
+ cancel_delayed_work_sync(&ts_drv->dual_work);
+ cancel_delayed_work_sync(&ts_drv->read_work);
+ destroy_workqueue(ts_drv->workqueue);
+ vt1603_ts_dev_cleanup(ts_drv);
+#ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0)
+ del_timer_sync(&ts_drv->led_timer);
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts_drv->earlysuspend);
+#endif
+ /* free vt1603 driver data */
+ dev_set_drvdata(&pdev->dev, NULL);
+ mutex_destroy(&ts_drv->ts_mutex);
+ free_irq(ts_drv->gpio_irq,ts_drv);
+ kfree(ts_drv);
+ ts_drv = NULL;
+
+ dbg("Exit\n");
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void
+vt1603ts_early_suspend(struct early_suspend *h)
+{
+ struct vt1603_ts_drvdata *ts_drv = pContext;
+
+ dbg("Enter\n");
+
+ wmt_gpio_mask_irq(ts_drv->intgpio);
+#ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0){
+ del_timer_sync(&ts_drv->led_timer);
+ set_key_led_gpio(ts_drv,LOW);
+ }
+#endif
+
+ ts_drv->earlysus = 1;
+ dbg("Exit\n");
+ return;
+}
+
+static void
+vt1603ts_late_resume(struct early_suspend *h)
+{
+ struct vt1603_ts_drvdata *ts_drv = pContext;
+
+ dbg("Enter\n");
+ ts_drv->pen_state = TS_PENUP_STATE;
+
+ /* must ensure mclk is available */
+ vt1603_ts_clk_enable();
+ /* vt1603 ts hardware resume */
+ vt1603_ts_reset(ts_drv);
+ /* clear irq before enale gpio irq */
+ vt1603_clr_ts_irq(ts_drv, 0x0f );
+
+ ts_drv->earlysus = 0;
+ //wmt_gpio_set_irq_type(ts_drv->intgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ts_drv->intgpio);
+ #ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0)
+ setup_led_gpio(ts_drv);
+ dbg("Exit\n");
+#endif
+
+ return;
+}
+
+#endif
+
+static int
+vt1603_ts_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ struct vt1603_ts_drvdata *ts_drv = dev_get_drvdata(&pdev->dev);
+
+ dbg("Enter\n");
+#ifdef VT1609_DEBUG
+ /* 4. dump vt1603 to ensure setting ok */
+ vt1603_reg_dump(ts_drv);
+#endif
+
+ ts_drv = dev_get_drvdata(&pdev->dev);
+
+ wmt_gpio_mask_irq(ts_drv->intgpio);
+#ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0){
+ set_key_led_gpio(ts_drv,LOW);
+ del_timer_sync(&ts_drv->led_timer);
+ }
+#endif
+
+ dbg("Exit\n");
+ return 0;
+}
+
+static int
+vt1603_ts_resume(struct platform_device *pdev)
+{
+ //struct vt1603_ts_drvdata *ts_drv = dev_get_drvdata(&pdev->dev);
+
+ dbg("Enter\n");
+ //delay resume work because some resources were closed by audio driver.
+ schedule_delayed_work(&resume_work, HZ);
+#if 0
+ ts_drv->pen_state = TS_PENUP_STATE;
+
+ /* must ensure mclk is available */
+ vt1603_ts_clk_enable();
+ /* vt1603 ts hardware resume */
+ vt1603_ts_reset(ts_drv);
+ /* clear irq before enale gpio irq */
+ vt1603_clr_ts_irq(ts_drv, 0x0f );
+
+ gpio_direction_input(ts_drv->intgpio);
+ wmt_gpio_set_irq_type(ts_drv->intgpio, IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_unmask_irq(ts_drv->intgpio);
+ #ifdef TOUCH_KEY
+ if(ts_drv->touch_key_used && ts_drv->ledgpio >= 0)
+ setup_led_gpio(ts_drv);
+#endif
+#ifdef VT1609_DEBUG
+ /* 4. dump vt1603 to ensure setting ok */
+ vt1603_reg_dump(ts_drv);
+#endif
+#endif
+ dbg("Exit\n");
+
+ return 0;
+}
+
+#else
+#define vt1603_ts_suspend NULL
+#define vt1603_ts_resume NULL
+#endif
+
+
+static struct platform_driver vt1603_driver = {
+ .driver = {
+ .name = VT1603_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = vt1603_ts_probe,
+ .remove = vt1603_ts_remove,
+ .suspend = vt1603_ts_suspend,
+ .resume = vt1603_ts_resume,
+};
+
+static int vt1603_ts_dev_open(struct inode *inode, struct file *filp)
+{
+ struct vt1603_ts_drvdata *ts_drv;
+
+ dbg("Enter\n");
+
+ ts_drv = container_of(inode->i_cdev, struct vt1603_ts_drvdata , cdev);
+ if (ts_drv == NULL) {
+ printk("can not get vt1603_ts driver data\n");
+ return -ENODATA;
+ }
+ filp->private_data = ts_drv;
+
+ dbg("Exit\n");
+ return 0;
+}
+
+static int vt1603_ts_dev_close(struct inode *inode, struct file *filp)
+{
+ struct vt1603_ts_drvdata *ts_drv;
+
+ dbg("Enter\n");
+
+ ts_drv = container_of(inode->i_cdev, struct vt1603_ts_drvdata , cdev);
+ if (ts_drv == NULL) {
+ printk("can not get vt1603_ts driver data\n");
+ return -ENODATA;
+ }
+
+ dbg("Exit\n");
+ return 0;
+}
+
+static long vt1603_ts_dev_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int nBuff[7] = { 0 };
+ char env_val[96] = { 0 };
+ struct vt1603_ts_drvdata *ts_drv;
+
+ dbg("Enter\n");
+ /* check type and command number */
+ if (_IOC_TYPE(cmd) != VT1603_TS_IOC_MAGIC)
+ return -ENOTTY;
+
+ ts_drv = filp->private_data;
+ switch (cmd) {
+ case VT1603_TS_IOC_CAL_DONE:
+ copy_from_user(nBuff, (unsigned int *)arg, 7 * sizeof(int));
+ g_CalcParam.a1 = nBuff[0];
+ g_CalcParam.b1 = nBuff[1];
+ g_CalcParam.c1 = nBuff[2];
+ g_CalcParam.a2 = nBuff[3];
+ g_CalcParam.b2 = nBuff[4];
+ g_CalcParam.c2 = nBuff[5];
+ g_CalcParam.delta = nBuff[6];
+
+ if (g_CalcParam.delta == 0)
+ g_CalcParam.delta = 1;
+
+ sprintf(env_val, "%d %d %d %d %d %d %d",
+ nBuff[0], nBuff[1], nBuff[2], nBuff[3], nBuff[4], nBuff[5], nBuff[6]);
+
+ wmt_setsyspara("wmt.io.ts.2dcal", env_val);
+ printk("TOUCH CAL DONE: [%s]\n", env_val);
+ break;
+
+ case VT1603_TS_IOC_CAL_RAWDATA:
+ nBuff[0] = ts_drv->raw_x;
+ nBuff[1] = ts_drv->raw_y;
+ copy_to_user((unsigned int *)arg, nBuff, 2 * sizeof(int));
+ printk("TOUCH CAL RAWDATA: x=%-4d, y=%-4d \n", nBuff[0], nBuff[1]);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ dbg("Exit\n");
+ return ret;
+}
+
+static struct file_operations vt1603_ts_fops = {
+ .owner = THIS_MODULE,
+ .open = vt1603_ts_dev_open,
+ .unlocked_ioctl = vt1603_ts_dev_ioctl,
+ .release = vt1603_ts_dev_close,
+};
+
+
+static int vt1603_ts_dev_setup(struct vt1603_ts_drvdata *ts_drv)
+{
+ dev_t dev_no = 0;
+ int ret = 0;
+ struct device *dev = NULL;
+
+ dbg("Enter\n");
+ if (VT1603_TS_DEV_MAJOR) {
+ ts_drv->major = VT1603_TS_DEV_MAJOR;
+ ts_drv->minor = 0;
+ dev_no = MKDEV(ts_drv->major, ts_drv->minor);
+ ret = register_chrdev_region(dev_no, VT1603_TS_NR_DEVS, DEV_NAME);
+ } else {
+ ret = alloc_chrdev_region(&dev_no, 0, VT1603_TS_NR_DEVS, DEV_NAME);
+ ts_drv->major = MAJOR(dev_no);
+ ts_drv->minor = MINOR(dev_no);
+ dbg("vt1603_ts device major = %d, minor = %d \n", ts_drv->major,ts_drv->minor);
+ }
+
+ if (ret < 0) {
+ printk("can not get major %d\n", ts_drv->major);
+ goto out;
+ }
+
+ cdev_init(&ts_drv->cdev, &vt1603_ts_fops);
+
+ ts_drv->cdev.owner = THIS_MODULE;
+ ts_drv->cdev.ops = &vt1603_ts_fops;
+ ret = cdev_add(&ts_drv->cdev, dev_no, VT1603_TS_NR_DEVS);
+ if (ret) {
+ printk("add char dev for vt1603 ts failed\n");
+ goto release_region;
+ }
+
+ vt1603_ts_class = class_create(THIS_MODULE, ts_drv->dev_id);
+ if (IS_ERR(vt1603_ts_class)) {
+ printk("create vt1603_ts class failed\n");
+ ret = PTR_ERR(vt1603_ts_class);
+ goto release_cdev;
+ }
+
+ dev = device_create(vt1603_ts_class, NULL, dev_no, NULL, DEV_NAME);
+ if (IS_ERR(dev)) {
+ printk("create device for vt160x ts failed\n");
+ ret = PTR_ERR(dev);
+ goto release_class;
+ }
+
+ dbg("Exit\n");
+ return ret;
+
+release_class:
+ class_destroy(vt1603_ts_class);
+ vt1603_ts_class = NULL;
+release_cdev:
+ cdev_del(&ts_drv->cdev);
+release_region:
+ unregister_chrdev_region(dev_no, VT1603_TS_NR_DEVS);
+out:
+ return ret;
+}
+
+static void vt1603_ts_dev_cleanup(struct vt1603_ts_drvdata *ts_drv)
+{
+ dev_t dev_no = MKDEV(ts_drv->major, ts_drv->minor);
+
+ dbg("Enter\n");
+ cdev_del(&ts_drv->cdev);
+ unregister_chrdev_region(dev_no, VT1603_TS_NR_DEVS);
+ device_destroy(vt1603_ts_class, dev_no);
+ class_destroy(vt1603_ts_class);
+ dbg("Exit\n");
+}
+
+#ifdef TOUCH_KEY
+static int parse_touch_key_env(struct vt1603_ts_drvdata *ts_drv)
+{
+ int i = 0;
+ int ret = 0;
+ int len = 96;
+ char retval[96] = {0};
+ char *p = NULL;
+
+ ret = wmt_getsyspara("wmt.ts.vkey", retval, &len);
+ if(ret){
+ printk("Read wmt.ts.vkey Failed.\n");
+ return -EIO;
+ }
+
+ sscanf(retval,"%d:%d:%d:%d", &ts_drv->tsc_key.key_num,
+ &ts_drv->tsc_key.low, &ts_drv->tsc_key.upper, &ts_drv->tsc_key.delta);
+
+ if(!ts_drv->tsc_key.key_num){
+ printk("tsc key number is zero!\n");
+ return -EIO;
+ }
+
+ p = retval;
+ i = 4;
+ while(i--){
+ p = strchr(p,':');
+ p++;
+ }
+
+ for(i = 0; i < ts_drv->tsc_key.key_num; i++){
+ sscanf(p,"%d_%d",&ts_drv->tsc_key.key[i].pos,&ts_drv->tsc_key.key[i].idx );
+ p = strchr(p,':');
+ p++;
+ }
+
+ dbg("%d:%d:%d:%d:%d_%d:%d_%d:%d_%d:%d_%d\n",
+ ts_drv->tsc_key.key_num,
+ ts_drv->tsc_key.low,
+ ts_drv->tsc_key.upper,
+ ts_drv->tsc_key.delta,
+ ts_drv->tsc_key.key[0].pos,
+ ts_drv->tsc_key.key[0].idx,
+ ts_drv->tsc_key.key[1].pos,
+ ts_drv->tsc_key.key[1].idx,
+ ts_drv->tsc_key.key[2].pos,
+ ts_drv->tsc_key.key[2].idx,
+ ts_drv->tsc_key.key[3].pos,
+ ts_drv->tsc_key.key[3].idx);
+
+ return 0;
+
+}
+#endif
+
+static int parse_dual_env(struct vt1603_ts_drvdata *ts_drv)
+{
+ int ret = 0;
+ int len = 96;
+ char retval[96] = {0};
+
+ len = sizeof(retval);
+ ret = wmt_getsyspara("wmt.io.vt1609", retval, &len);
+ if(ret){
+ //printk("wmt.io.vt1609 not set, use default parameter.\n");
+ ts_drv->dual_dev.vxy = 17;
+ ts_drv->dual_dev.scale_x = 4;
+ ts_drv->dual_dev.scale_y = 2;
+ ts_drv->dual_dev.F1_CNT = 2;
+ ts_drv->dual_dev.F2_CNT = 7;
+ ts_drv->dual_dev.F2T1_CNT = 15;
+ ts_drv->dual_dev.SAMPLE_CNT = 1;
+ ts_drv->dual_dev.THR_MIN_DX = 13;
+ ts_drv->dual_dev.THR_MAX_DX = 256;
+ ts_drv->dual_dev.exch = 0;
+
+ return 0;
+ }
+
+ sscanf(retval,"%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", &ts_drv->dual_dev.vxy,
+ &ts_drv->dual_dev.scale_x,
+ &ts_drv->dual_dev.scale_y,
+ &ts_drv->dual_dev.F1_CNT,
+ &ts_drv->dual_dev.F2_CNT,
+ &ts_drv->dual_dev.F2T1_CNT,
+ &ts_drv->dual_dev.SAMPLE_CNT,
+ &ts_drv->dual_dev.THR_MIN_DX,
+ &ts_drv->dual_dev.THR_MAX_DX,
+ &ts_drv->dual_dev.exch);
+ /*
+ printk("%d:%d:%d:%d:%d:%d:%d:%d:%d\n",
+ ts_drv->dual_dev.vxy,
+ ts_drv->dual_dev.scale_x,
+ ts_drv->dual_dev.scale_y,
+ ts_drv->dual_dev.F1_CNT,
+ ts_drv->dual_dev.F2_CNT ,
+ ts_drv->dual_dev.F2T1_CNT ,
+ ts_drv->dual_dev.SAMPLE_CNT ,
+ ts_drv->dual_dev.THR_MIN_DX ,
+ ts_drv->dual_dev.THR_MAX_DX),
+ ts_drv->dual_dev.exch;
+ */
+ return 0;
+}
+
+static int vt1603_uboot_env_check(struct vt1603_ts_drvdata *ts_drv)
+{
+ int nBuff[7] = {0};
+ int i = 0, Enable = 0;
+ int intgpio=0;
+ int ledgpio=-1;
+ int reslx=480,resly=800;
+ int ret=0,len = 96;
+ char retval[96] = {0};
+ char *p=NULL;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ printk("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+
+ sscanf(retval,"%d:",&Enable);
+ //check touch enable
+ if(Enable == 0){
+ printk("System touchscreen is disbaled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(retval,':');
+ p++;
+ if(strncmp(p,"vt1603",6) == 0){//check touch ID
+ strcpy(ts_drv->dev_id, "VT1603A");
+ }
+ else if(strncmp(p,"vt1609",6) == 0){//check touch ID
+ ts_drv->dual_enable = 1;
+ strcpy(ts_drv->dev_id, "VT1609");
+ parse_dual_env(ts_drv);
+ }
+ else{
+ printk("Vt1609 touchscreen driver disabled.\n");
+ return -ENODEV;
+ }
+
+ p = strchr(p,':');
+ p++;
+ sscanf(p,"%d:%d:%d:%d",&reslx, &resly, &intgpio, &ledgpio);
+
+ ts_drv->resl_x = reslx;
+ ts_drv->resl_y = resly;
+
+ ts_drv->intgpio = intgpio;
+
+ ts_drv->ledgpio = ledgpio;
+
+ ts_drv->gpio_irq = IRQ_GPIO;
+
+ printk("%s-Touch: reslx=%d resly=%d, Interrupt GPIO%d , Virtual Touch Key Led GPIO%d\n",ts_drv->dev_id,
+ ts_drv->resl_x, ts_drv->resl_y, ts_drv->intgpio, ts_drv->ledgpio);
+
+ len = sizeof(retval);
+ memset(retval, 0, sizeof(retval));
+
+ ret = wmt_getsyspara("wmt.io.ts.2dcal", retval, &len);
+ if(ret){
+ printk("Read env wmt.io.ts.2dcal Failed.\n");
+ //return -EIO;
+ }
+
+ for (i = 0; i < sizeof(retval); i++) {
+ if (retval[i] == ' ' || retval[i] == ',' || retval[i] == ':')
+ retval[i] = '\0';
+ }
+
+ p = retval;
+ for (i = 0; (i < 7) && (p < (retval + sizeof(retval))); ) {
+ if (*p == '\0')
+ p++;
+ else {
+ sscanf(p, "%d", &nBuff[i]);
+ p = p + strlen(p);
+ i++;
+ }
+ }
+ dbg("Touchscreen Calibrate Data: [%d %d %d %d %d %d %d]\n",
+ nBuff[0], nBuff[1], nBuff[2], nBuff[3], nBuff[4], nBuff[5], nBuff[6]);
+
+ g_CalcParam.a1 = nBuff[0];
+ g_CalcParam.b1 = nBuff[1];
+ g_CalcParam.c1 = nBuff[2];
+ g_CalcParam.a2 = nBuff[3];
+ g_CalcParam.b2 = nBuff[4];
+ g_CalcParam.c2 = nBuff[5];
+ g_CalcParam.delta = nBuff[6];
+
+ if(g_CalcParam.delta == 0)
+ g_CalcParam.delta = 1;
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ ts_drv->lcd_exchg = 1;
+ }
+
+ return 0;
+}
+
+static int gpio_resource_request(void)
+{
+ if (gpio_request(pContext->intgpio, "ts_irq") < 0) {
+ printk("gpio(%d) touchscreen interrupt request fail\n", pContext->intgpio);
+ return -EIO;
+ }
+ gpio_direction_input(pContext->intgpio);
+
+ if (pContext->ledgpio >= 0) {
+ if (gpio_request(pContext->ledgpio, "ts_led") < 0) {
+ printk("gpio(%d) touchscreen led gpio request fail\n", pContext->ledgpio);
+ gpio_free(pContext->intgpio);
+ return -EIO;
+ }
+ gpio_direction_output(pContext->ledgpio, 0);
+ }
+
+ return 0;
+}
+static void gpio_resource_free(void)
+{
+ gpio_free(pContext->intgpio);
+ if (pContext->ledgpio >= 0)
+ gpio_free(pContext->ledgpio);
+}
+
+
+static int __init vt1603_ts_init(void)
+{
+ int ret = 0;
+ struct vt1603_ts_drvdata *ts_drv = NULL;
+
+ dbg("Enter\n");
+ ts_drv = kzalloc(sizeof(struct vt1603_ts_drvdata), GFP_KERNEL);
+ if (!ts_drv) {
+ printk("vt160x ts: alloc driver data failed\n");
+ return -ENOMEM;
+ }
+ pContext = ts_drv;
+ ret = vt1603_uboot_env_check(ts_drv);
+ if (ret) {//vt1603 touch disabled
+ goto out;
+ }else{//vt1603 touch enabled
+ if (gpio_resource_request())
+ goto out;
+ ret = vt1603_ts_dev_setup(ts_drv);//only touch calibrate need dev node
+ if (ret) {
+ printk("##ERR## vt160x ts create device node failed.\n");
+ goto freegpio;
+ }
+
+#ifdef TOUCH_KEY
+ if(!parse_touch_key_env(ts_drv)){
+ ts_drv->touch_key_used = 1;
+ if(ts_drv->ledgpio >= 0){//touch virtual key back light led enabled
+ init_timer(&ts_drv->led_timer);
+ ts_drv->led_timer.function = led_timer_func;
+ ts_drv->led_timer.data = (unsigned long) ts_drv;
+ }
+ }
+#endif
+ }
+
+ ret = platform_driver_register(&vt1603_driver);
+ if(ret){
+ printk("vt160x platform driver register failed!.\n");
+ goto release_dev;
+ }
+
+ if(ts_drv->dual_enable)
+ vt1603_dual_init(ts_drv);
+
+ dbg("Exit\n");
+ return ret;
+
+release_dev:
+ vt1603_ts_dev_cleanup(ts_drv);
+freegpio:
+ gpio_resource_free();
+out:
+ kfree(ts_drv);
+
+ return ret;
+}
+//module_init(vt1603_ts_init);
+late_initcall(vt1603_ts_init);
+static void __exit vt1603_ts_exit(void)
+{
+ dbg("Enter\n");
+
+ platform_driver_unregister(&vt1603_driver);
+ gpio_resource_free();
+
+ dbg("Exit\n");
+}
+module_exit(vt1603_ts_exit);
+
+MODULE_DESCRIPTION("VT1603A/VT1609 TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/vt1609_ts/vt1609_ts.h b/drivers/input/touchscreen/vt1609_ts/vt1609_ts.h
new file mode 100755
index 00000000..31210579
--- /dev/null
+++ b/drivers/input/touchscreen/vt1609_ts/vt1609_ts.h
@@ -0,0 +1,301 @@
+#ifndef __VT1603_TS_H__
+#define __VT1603_TS_H__
+#include <linux/mfd/vt1603/core.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#define DEV_NAME "wmtts"
+#define VT1603_DRIVER_NAME "vt1603-touch"
+
+#undef abs
+#define abs(x) (((x)>0)?(x):(-(x)))
+
+#define FALSE 0
+#define TRUE 1
+#define VT1603_TS_NR_DEVS 1
+#define VT1603_TS_DEV_MAJOR 160
+#define TS_DEBOUNCE 50
+#define VT1603_FILTER_HEAD_COUNT 2
+#define VT1603_JITTER_THRESHOLD 1200//µ¥µãÇ°ºó2µãÖ®¼äµÄx/y ±ä»¯×î´óÈÝÐíÖµ
+
+#define None_TOUCH 0x00
+#define Single_TOUCH 0x01
+#define Multi_TOUCH 0x02
+
+#define ADC_DATA(low, high) ((((high) & 0x0F) << 8) + (low))
+
+/* touch panel type config */
+#define PANEL_TYPE_4WIRED 0x10
+#define PANEL_TYPE_5WIRED 0x11
+
+/* enable calibration or not */
+#define CALIBRATION_ENABLE 0x01
+#define CALIBRATION_DISABLE 0x00
+
+/* VT1603 working mode */
+#define VT1603_TS_MODE BIT1
+#define VT1603_TEMP_MODE BIT2
+#define VT1603_BAT_MODE BIT3
+
+/* VT1603 touch panel state */
+#define TS_PENDOWN_STATE 0x00
+#define TS_PENUP_STATE 0x01
+
+struct vt1603_ts_pos {
+ int x;
+ int y;
+};
+
+#define VT1603_FIFO_LEN 3
+struct vt1603_fifo{
+ int head;
+ int full;
+ int buf[VT1603_FIFO_LEN];
+};
+
+#define I2C_BUS 0x00
+#define SPI_BUS 0x01
+
+#define VT1603_SPI_FIX_CS 0x00
+#define VT1603_SPI_FAKE_CS 0x03
+#define VT1603_SPI_BUS_0 0x00
+#define VT1603_SPI_BUS_1 0x01
+#define VT1603_MAX_SPI_CLK (20*1000*1000)
+#define SPI_DEFAULT_CLK (4*1000*1000)
+#define IDLE_DATA_NUM 5
+
+#define VT1603_I2C_FIX_ADDR 0x1A
+#define VT1603_I2C_FAKE_ADDR 0xFF
+#define VT1603_TS_I2C_WCMD 0x00
+#define VT1603_TS_I2C_RCMD 0x01
+#define VT1603_TS_I2C_RWCMD 0x02
+#define VT1603_I2C_BUS_0 0x00
+#define VT1603_I2C_BUS_1 0x01
+
+///////////////////////////////
+//#define TOUCH_KEY
+#define KEY_DETLA 300
+#define TOUCH_KEY_NUM 4
+#define TOUCH_KEY_LED_GPIO 4
+
+#define HIGH 1
+#define LOW 0
+
+struct tsc_key{
+ int pos;
+ int idx;
+};
+
+struct tsc_key_st{
+ int key_num;
+ int low;
+ int upper;
+ int delta;
+ struct tsc_key key[TOUCH_KEY_NUM];
+};
+
+enum key_idx{
+ _SEARCH,
+ _BACK,
+ _HOME,
+ _MENU,
+ _MAX_NUM,
+};
+/////////////////////////
+
+enum gpio_irq_type {
+ HIGH_ACTIVE = 0,
+ LOW_ACTIVE = 1,
+ RISING_EDGE_ACTIVE = 3,
+ FALLING_EDGE_ACTIVE = 4,
+ UNKOWN_TYPE = 0xFF
+};
+
+/*
+ * vt1603_ts_platform_data - vt1603 configuration data
+ * @panel_type: touch panel type: 4-wired or 5-wired
+ * @cal_en: enable calibration circuit or not
+ * @cal_sel: calibratin capacitor control bits
+ * @shfit: conversion data shfit
+ * @sclk_div: initial value of sclk dividor if mclk = 12.288MHZ 0x04 = 200ksps 0x08 = 100ksps
+ * @soc_gpio_irq: soc gpio interrupts, connect with vt1603 gpio1
+ */
+struct vt1603_ts_platform_data {
+ u8 panel_type;
+ u8 cal_en;
+ u8 cal_sel:2;
+ u8 shift;
+ u8 sclk_div;
+ int soc_gpio_irq;
+ int gpio_num;
+ enum gpio_irq_type irq_type;
+};
+
+struct vt1609_dual_st{
+ int vxy;
+
+ int scale_x;
+ int scale_y;
+
+ int F1_CNT;
+ int F2_CNT;
+ int F2T1_CNT;
+ int SAMPLE_CNT;
+
+ int THR_MIN_DX;
+ int THR_MAX_DX;
+
+ int exch;
+};
+
+struct vt1603_ts_drvdata {
+ struct vt1603 *tdev;
+ //spinlock_t spinlock;
+ struct mutex ts_mutex;
+ struct input_dev *input;
+ struct work_struct work;
+ struct delayed_work read_work;
+ struct delayed_work dual_work;
+ struct workqueue_struct *workqueue;
+ struct vt1603_ts_platform_data *pdata;
+
+ int earlysus;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend earlysuspend;
+#endif
+ char dev_id[32];
+
+ struct cdev cdev;
+ int major;
+ int minor;
+
+ int gpio_irq;
+
+ int intgpio;
+
+ u8 pen_state;
+ int ts_stamp;
+ int hcnt;
+
+ int resl_x;
+ int resl_y;
+ int lcd_exchg;
+
+ int raw_x;
+ int raw_y;
+
+ int dual_enable;
+ struct vt1609_dual_st dual_dev;
+
+ int ledgpio;
+#ifdef TOUCH_KEY
+ int key_idx;
+ int key_pressed;
+ int touch_key_used;
+ struct timer_list led_timer;
+ struct tsc_key_st tsc_key;
+#endif
+
+};
+
+/* VT1603 Register address */
+#define VT1603_BTHD_REG 0x78
+#define VT1603_BCLK_REG 0x88
+#define VT1603_BAEN_REG 0x04
+
+#define VT1603_PWC_REG 0xC0
+#define VT1603_CR_REG 0xC1
+#define VT1603_CCCR_REG 0xC2
+#define VT1603_CDPR_REG 0xC3
+#define VT1603_TSPC_REG 0xC4
+#define VT1603_AMCR_REG 0xC7
+#define VT1603_INTCR_REG 0xC8
+#define VT1603_INTEN_REG 0xC9
+#define VT1603_INTS_REG 0xCA
+#define VT1603_DCR_REG 0xCB
+
+#define VT1603_TODCL_REG 0xCC
+#define VT1603_TODCH_REG 0xCD
+
+#define VT1603_DATL_REG 0xCE
+#define VT1603_DATH_REG 0xCF
+
+#define VT1603_XPL_REG 0xD0
+#define VT1603_XPH_REG 0xD1
+#define VT1603_YPL_REG 0xD2
+#define VT1603_YPH_REG 0xD3
+
+#define VT1603_BATL_REG 0xD4
+#define VT1603_BATH_REG 0xD5
+
+#define VT1603_TEMPL_REG 0xD6
+#define VT1603_TEMPH_REG 0xD7
+
+#define VT1603_ERR8_REG 0xD8
+#define VT1603_ERR7_REG 0xD9
+#define VT1603_ERR6_REG 0xDA
+#define VT1603_ERR5_REG 0xDB
+#define VT1603_ERR4_REG 0xDC
+#define VT1603_ERR3_REG 0xDD
+#define VT1603_ERR2_REG 0xDE
+#define VT1603_ERR1_REG 0xDF
+
+#define VT1603_DBG8_REG 0xE0
+#define VT1603_DBG7_REG 0xE1
+#define VT1603_DBG6_REG 0xE2
+#define VT1603_DBG5_REG 0xE3
+#define VT1603_DBG4_REG 0xE4
+#define VT1603_DBG3_REG 0xE5
+#define VT1603_DBG2_REG 0xE6
+#define VT1603_DBG1_REG 0xE7
+
+/* for VT1603 GPIO1 interrupt setting */
+#define VT1603_IMASK_REG27 27
+#define VT1603_IMASK_REG28 28
+#define VT1603_IMASK_REG29 29
+#define VT1603_IPOL_REG33 33
+#define VT1603_ISEL_REG36 36
+
+struct vt1603_ts_cal_info {
+ int a1;
+ int b1;
+ int c1;
+ int a2;
+ int b2;
+ int c2;
+ int delta;
+};
+
+/* VT1603 TS and SAR-ADC IOCTL */
+#define VT1603_TS_IOC_MAGIC 't'
+
+/* for touch screen calibration */
+#define VT1603_TS_IOC_CAL_START _IO(VT1603_TS_IOC_MAGIC, 1)
+#define VT1603_TS_IOC_CAL_DONE _IOW(VT1603_TS_IOC_MAGIC, 2, int *)
+#define VT1603_TS_IOC_CAL_RAWDATA _IOR(VT1603_TS_IOC_MAGIC, 3, int *)
+#define VT1603_TS_IOC_CAL_QUIT _IOW(VT1603_TS_IOC_MAGIC, 4, int *)
+
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlenex);
+
+int vt1603_clr_ts_irq(struct vt1603_ts_drvdata *ts_drv, u8 mask);
+int vt1603_set_reg8(struct vt1603_ts_drvdata *ts_drv, u8 reg, u8 val);
+u8 vt1603_get_reg8(struct vt1603_ts_drvdata *ts_drv, u8 reg);
+void vt1603_setbits(struct vt1603_ts_drvdata *ts_drv, u8 reg, u8 mask);
+void vt1603_clrbits(struct vt1603_ts_drvdata *ts_drv, u8 reg, u8 mask);
+
+void vt1603_ts_report_pos(struct vt1603_ts_drvdata *ts_drv, struct vt1603_ts_pos *pos);
+int vt1603_ts_pos_calibration(struct vt1603_ts_drvdata *ts_drv,struct vt1603_ts_pos *to_cal);
+
+#ifdef TOUCH_KEY
+void vt1603_ts_report_key(struct vt1603_ts_drvdata *ts_drv);
+int vt1603_ts_get_key(struct vt1603_ts_drvdata *ts_drv,struct vt1603_ts_pos pos);
+int set_key_led_gpio(struct vt1603_ts_drvdata *ts_drv,int val);
+#endif
+
+int vt1603_dual_init(struct vt1603_ts_drvdata *ts_drv);
+void vt1603_dual_exit(struct vt1603_ts_drvdata *ts_drv);
+void vt1603_ts_dual_support(struct work_struct* work);
+
+#endif /* __VT1603_TS_H__ */
diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c
new file mode 100644
index 00000000..9396b21d
--- /dev/null
+++ b/drivers/input/touchscreen/w90p910_ts.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+/* ADC controller bit defines */
+#define ADC_DELAY 0xf00
+#define ADC_DOWN 0x01
+#define ADC_TSC_Y (0x01 << 8)
+#define ADC_TSC_X (0x00 << 8)
+#define TSC_FOURWIRE (~(0x03 << 1))
+#define ADC_CLK_EN (0x01 << 28) /* ADC clock enable */
+#define ADC_READ_CON (0x01 << 12)
+#define ADC_CONV (0x01 << 13)
+#define ADC_SEMIAUTO (0x01 << 14)
+#define ADC_WAITTRIG (0x03 << 14)
+#define ADC_RST1 (0x01 << 16)
+#define ADC_RST0 (0x00 << 16)
+#define ADC_EN (0x01 << 17)
+#define ADC_INT (0x01 << 18)
+#define WT_INT (0x01 << 20)
+#define ADC_INT_EN (0x01 << 21)
+#define LVD_INT_EN (0x01 << 22)
+#define WT_INT_EN (0x01 << 23)
+#define ADC_DIV (0x04 << 1) /* div = 6 */
+
+enum ts_state {
+ TS_WAIT_NEW_PACKET, /* We are waiting next touch report */
+ TS_WAIT_X_COORD, /* We are waiting for ADC to report X coord */
+ TS_WAIT_Y_COORD, /* We are waiting for ADC to report Y coord */
+ TS_IDLE, /* Input device is closed, don't do anything */
+};
+
+struct w90p910_ts {
+ struct input_dev *input;
+ struct timer_list timer;
+ struct clk *clk;
+ int irq_num;
+ void __iomem *ts_reg;
+ spinlock_t lock;
+ enum ts_state state;
+};
+
+static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down)
+{
+ struct input_dev *dev = w90p910_ts->input;
+
+ if (down) {
+ input_report_abs(dev, ABS_X,
+ __raw_readl(w90p910_ts->ts_reg + 0x0c));
+ input_report_abs(dev, ABS_Y,
+ __raw_readl(w90p910_ts->ts_reg + 0x10));
+ }
+
+ input_report_key(dev, BTN_TOUCH, down);
+ input_sync(dev);
+}
+
+static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts)
+{
+ unsigned long ctlreg;
+
+ __raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04);
+ ctlreg = __raw_readl(w90p910_ts->ts_reg);
+ ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN);
+ ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
+ __raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+ w90p910_ts->state = TS_WAIT_X_COORD;
+}
+
+static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts)
+{
+ unsigned long ctlreg;
+
+ __raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04);
+ ctlreg = __raw_readl(w90p910_ts->ts_reg);
+ ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN);
+ ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
+ __raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+ w90p910_ts->state = TS_WAIT_Y_COORD;
+}
+
+static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts)
+{
+ unsigned long ctlreg;
+
+ ctlreg = __raw_readl(w90p910_ts->ts_reg);
+ ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV);
+ ctlreg |= ADC_WAITTRIG | WT_INT_EN;
+ __raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+ w90p910_ts->state = TS_WAIT_NEW_PACKET;
+}
+
+static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id)
+{
+ struct w90p910_ts *w90p910_ts = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&w90p910_ts->lock, flags);
+
+ switch (w90p910_ts->state) {
+ case TS_WAIT_NEW_PACKET:
+ /*
+ * The controller only generates interrupts when pen
+ * is down.
+ */
+ del_timer(&w90p910_ts->timer);
+ w90p910_prepare_x_reading(w90p910_ts);
+ break;
+
+
+ case TS_WAIT_X_COORD:
+ w90p910_prepare_y_reading(w90p910_ts);
+ break;
+
+ case TS_WAIT_Y_COORD:
+ w90p910_report_event(w90p910_ts, true);
+ w90p910_prepare_next_packet(w90p910_ts);
+ mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100));
+ break;
+
+ case TS_IDLE:
+ break;
+ }
+
+ spin_unlock_irqrestore(&w90p910_ts->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void w90p910_check_pen_up(unsigned long data)
+{
+ struct w90p910_ts *w90p910_ts = (struct w90p910_ts *) data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&w90p910_ts->lock, flags);
+
+ if (w90p910_ts->state == TS_WAIT_NEW_PACKET &&
+ !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) {
+
+ w90p910_report_event(w90p910_ts, false);
+ }
+
+ spin_unlock_irqrestore(&w90p910_ts->lock, flags);
+}
+
+static int w90p910_open(struct input_dev *dev)
+{
+ struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
+ unsigned long val;
+
+ /* enable the ADC clock */
+ clk_enable(w90p910_ts->clk);
+
+ __raw_writel(ADC_RST1, w90p910_ts->ts_reg);
+ msleep(1);
+ __raw_writel(ADC_RST0, w90p910_ts->ts_reg);
+ msleep(1);
+
+ /* set delay and screen type */
+ val = __raw_readl(w90p910_ts->ts_reg + 0x04);
+ __raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04);
+ __raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08);
+
+ w90p910_ts->state = TS_WAIT_NEW_PACKET;
+ wmb();
+
+ /* set trigger mode */
+ val = __raw_readl(w90p910_ts->ts_reg);
+ val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN;
+ __raw_writel(val, w90p910_ts->ts_reg);
+
+ return 0;
+}
+
+static void w90p910_close(struct input_dev *dev)
+{
+ struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
+ unsigned long val;
+
+ /* disable trigger mode */
+
+ spin_lock_irq(&w90p910_ts->lock);
+
+ w90p910_ts->state = TS_IDLE;
+
+ val = __raw_readl(w90p910_ts->ts_reg);
+ val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN);
+ __raw_writel(val, w90p910_ts->ts_reg);
+
+ spin_unlock_irq(&w90p910_ts->lock);
+
+ /* Now that interrupts are shut off we can safely delete timer */
+ del_timer_sync(&w90p910_ts->timer);
+
+ /* stop the ADC clock */
+ clk_disable(w90p910_ts->clk);
+}
+
+static int __devinit w90x900ts_probe(struct platform_device *pdev)
+{
+ struct w90p910_ts *w90p910_ts;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int err;
+
+ w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!w90p910_ts || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ w90p910_ts->input = input_dev;
+ w90p910_ts->state = TS_IDLE;
+ spin_lock_init(&w90p910_ts->lock);
+ setup_timer(&w90p910_ts->timer, w90p910_check_pen_up,
+ (unsigned long)w90p910_ts);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENXIO;
+ goto fail1;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ pdev->name)) {
+ err = -EBUSY;
+ goto fail1;
+ }
+
+ w90p910_ts->ts_reg = ioremap(res->start, resource_size(res));
+ if (!w90p910_ts->ts_reg) {
+ err = -ENOMEM;
+ goto fail2;
+ }
+
+ w90p910_ts->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(w90p910_ts->clk)) {
+ err = PTR_ERR(w90p910_ts->clk);
+ goto fail3;
+ }
+
+ input_dev->name = "W90P910 TouchScreen";
+ input_dev->phys = "w90p910ts/event0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0005;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->open = w90p910_open;
+ input_dev->close = w90p910_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0);
+
+ input_set_drvdata(input_dev, w90p910_ts);
+
+ w90p910_ts->irq_num = platform_get_irq(pdev, 0);
+ if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
+ 0, "w90p910ts", w90p910_ts)) {
+ err = -EBUSY;
+ goto fail4;
+ }
+
+ err = input_register_device(w90p910_ts->input);
+ if (err)
+ goto fail5;
+
+ platform_set_drvdata(pdev, w90p910_ts);
+
+ return 0;
+
+fail5: free_irq(w90p910_ts->irq_num, w90p910_ts);
+fail4: clk_put(w90p910_ts->clk);
+fail3: iounmap(w90p910_ts->ts_reg);
+fail2: release_mem_region(res->start, resource_size(res));
+fail1: input_free_device(input_dev);
+ kfree(w90p910_ts);
+ return err;
+}
+
+static int __devexit w90x900ts_remove(struct platform_device *pdev)
+{
+ struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(w90p910_ts->irq_num, w90p910_ts);
+ del_timer_sync(&w90p910_ts->timer);
+ iounmap(w90p910_ts->ts_reg);
+
+ clk_put(w90p910_ts->clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ input_unregister_device(w90p910_ts->input);
+ kfree(w90p910_ts);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver w90x900ts_driver = {
+ .probe = w90x900ts_probe,
+ .remove = __devexit_p(w90x900ts_remove),
+ .driver = {
+ .name = "nuc900-ts",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(w90x900ts_driver);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 touch screen driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-ts");
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
new file mode 100644
index 00000000..1569a393
--- /dev/null
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -0,0 +1,608 @@
+/*
+ * Wacom W8001 penabled serial touchscreen driver
+ *
+ * Copyright (c) 2008 Jaya Kumar
+ * Copyright (c) 2010 Red Hat, Inc.
+ * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Layout based on Elo serial touchscreen driver by Vojtech Pavlik
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#define DRIVER_DESC "Wacom W8001 serial touchscreen driver"
+
+MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define W8001_MAX_LENGTH 11
+#define W8001_LEAD_MASK 0x80
+#define W8001_LEAD_BYTE 0x80
+#define W8001_TAB_MASK 0x40
+#define W8001_TAB_BYTE 0x40
+/* set in first byte of touch data packets */
+#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK)
+#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE)
+
+#define W8001_QUERY_PACKET 0x20
+
+#define W8001_CMD_STOP '0'
+#define W8001_CMD_START '1'
+#define W8001_CMD_QUERY '*'
+#define W8001_CMD_TOUCHQUERY '%'
+
+/* length of data packets in bytes, depends on device. */
+#define W8001_PKTLEN_TOUCH93 5
+#define W8001_PKTLEN_TOUCH9A 7
+#define W8001_PKTLEN_TPCPEN 9
+#define W8001_PKTLEN_TPCCTL 11 /* control packet */
+#define W8001_PKTLEN_TOUCH2FG 13
+
+/* resolution in points/mm */
+#define W8001_PEN_RESOLUTION 100
+#define W8001_TOUCH_RESOLUTION 10
+
+struct w8001_coord {
+ u8 rdy;
+ u8 tsw;
+ u8 f1;
+ u8 f2;
+ u16 x;
+ u16 y;
+ u16 pen_pressure;
+ u8 tilt_x;
+ u8 tilt_y;
+};
+
+/* touch query reply packet */
+struct w8001_touch_query {
+ u16 x;
+ u16 y;
+ u8 panel_res;
+ u8 capacity_res;
+ u8 sensor_id;
+};
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct w8001 {
+ struct input_dev *dev;
+ struct serio *serio;
+ struct completion cmd_done;
+ int id;
+ int idx;
+ unsigned char response_type;
+ unsigned char response[W8001_MAX_LENGTH];
+ unsigned char data[W8001_MAX_LENGTH];
+ char phys[32];
+ int type;
+ unsigned int pktlen;
+ u16 max_touch_x;
+ u16 max_touch_y;
+ u16 max_pen_x;
+ u16 max_pen_y;
+ char name[64];
+};
+
+static void parse_pen_data(u8 *data, struct w8001_coord *coord)
+{
+ memset(coord, 0, sizeof(*coord));
+
+ coord->rdy = data[0] & 0x20;
+ coord->tsw = data[0] & 0x01;
+ coord->f1 = data[0] & 0x02;
+ coord->f2 = data[0] & 0x04;
+
+ coord->x = (data[1] & 0x7F) << 9;
+ coord->x |= (data[2] & 0x7F) << 2;
+ coord->x |= (data[6] & 0x60) >> 5;
+
+ coord->y = (data[3] & 0x7F) << 9;
+ coord->y |= (data[4] & 0x7F) << 2;
+ coord->y |= (data[6] & 0x18) >> 3;
+
+ coord->pen_pressure = data[5] & 0x7F;
+ coord->pen_pressure |= (data[6] & 0x07) << 7 ;
+
+ coord->tilt_x = data[7] & 0x7F;
+ coord->tilt_y = data[8] & 0x7F;
+}
+
+static void parse_single_touch(u8 *data, struct w8001_coord *coord)
+{
+ coord->x = (data[1] << 7) | data[2];
+ coord->y = (data[3] << 7) | data[4];
+ coord->tsw = data[0] & 0x01;
+}
+
+static void scale_touch_coordinates(struct w8001 *w8001,
+ unsigned int *x, unsigned int *y)
+{
+ if (w8001->max_pen_x && w8001->max_touch_x)
+ *x = *x * w8001->max_pen_x / w8001->max_touch_x;
+
+ if (w8001->max_pen_y && w8001->max_touch_y)
+ *y = *y * w8001->max_pen_y / w8001->max_touch_y;
+}
+
+static void parse_multi_touch(struct w8001 *w8001)
+{
+ struct input_dev *dev = w8001->dev;
+ unsigned char *data = w8001->data;
+ unsigned int x, y;
+ int i;
+ int count = 0;
+
+ for (i = 0; i < 2; i++) {
+ bool touch = data[0] & (1 << i);
+
+ input_mt_slot(dev, i);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
+ if (touch) {
+ x = (data[6 * i + 1] << 7) | data[6 * i + 2];
+ y = (data[6 * i + 3] << 7) | data[6 * i + 4];
+ /* data[5,6] and [11,12] is finger capacity */
+
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
+ count++;
+ }
+ }
+
+ /* emulate single touch events when stylus is out of proximity.
+ * This is to make single touch backward support consistent
+ * across all Wacom single touch devices.
+ */
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED;
+ input_mt_report_pointer_emulation(dev, true);
+ }
+
+ input_sync(dev);
+}
+
+static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
+{
+ memset(query, 0, sizeof(*query));
+
+ query->panel_res = data[1];
+ query->sensor_id = data[2] & 0x7;
+ query->capacity_res = data[7];
+
+ query->x = data[3] << 9;
+ query->x |= data[4] << 2;
+ query->x |= (data[2] >> 5) & 0x3;
+
+ query->y = data[5] << 9;
+ query->y |= data[6] << 2;
+ query->y |= (data[2] >> 3) & 0x3;
+
+ /* Early days' single-finger touch models need the following defaults */
+ if (!query->x && !query->y) {
+ query->x = 1024;
+ query->y = 1024;
+ if (query->panel_res)
+ query->x = query->y = (1 << query->panel_res);
+ query->panel_res = W8001_TOUCH_RESOLUTION;
+ }
+}
+
+static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
+{
+ struct input_dev *dev = w8001->dev;
+
+ /*
+ * We have 1 bit for proximity (rdy) and 3 bits for tip, side,
+ * side2/eraser. If rdy && f2 are set, this can be either pen + side2,
+ * or eraser. Assume:
+ * - if dev is already in proximity and f2 is toggled → pen + side2
+ * - if dev comes into proximity with f2 set → eraser
+ * If f2 disappears after assuming eraser, fake proximity out for
+ * eraser and in for pen.
+ */
+
+ switch (w8001->type) {
+ case BTN_TOOL_RUBBER:
+ if (!coord->f2) {
+ input_report_abs(dev, ABS_PRESSURE, 0);
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_key(dev, BTN_STYLUS, 0);
+ input_report_key(dev, BTN_STYLUS2, 0);
+ input_report_key(dev, BTN_TOOL_RUBBER, 0);
+ input_sync(dev);
+ w8001->type = BTN_TOOL_PEN;
+ }
+ break;
+
+ case BTN_TOOL_FINGER:
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_key(dev, BTN_TOOL_FINGER, 0);
+ input_sync(dev);
+ /* fall through */
+
+ case KEY_RESERVED:
+ w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ break;
+
+ default:
+ input_report_key(dev, BTN_STYLUS2, coord->f2);
+ break;
+ }
+
+ input_report_abs(dev, ABS_X, coord->x);
+ input_report_abs(dev, ABS_Y, coord->y);
+ input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure);
+ input_report_key(dev, BTN_TOUCH, coord->tsw);
+ input_report_key(dev, BTN_STYLUS, coord->f1);
+ input_report_key(dev, w8001->type, coord->rdy);
+ input_sync(dev);
+
+ if (!coord->rdy)
+ w8001->type = KEY_RESERVED;
+}
+
+static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
+{
+ struct input_dev *dev = w8001->dev;
+ unsigned int x = coord->x;
+ unsigned int y = coord->y;
+
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_key(dev, BTN_TOUCH, coord->tsw);
+ input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
+
+ input_sync(dev);
+
+ w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;
+}
+
+static irqreturn_t w8001_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct w8001 *w8001 = serio_get_drvdata(serio);
+ struct w8001_coord coord;
+ unsigned char tmp;
+
+ w8001->data[w8001->idx] = data;
+ switch (w8001->idx++) {
+ case 0:
+ if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) {
+ pr_debug("w8001: unsynchronized data: 0x%02x\n", data);
+ w8001->idx = 0;
+ }
+ break;
+
+ case W8001_PKTLEN_TOUCH93 - 1:
+ case W8001_PKTLEN_TOUCH9A - 1:
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+ if (tmp != W8001_TOUCH_BYTE)
+ break;
+
+ if (w8001->pktlen == w8001->idx) {
+ w8001->idx = 0;
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ parse_single_touch(w8001->data, &coord);
+ report_single_touch(w8001, &coord);
+ }
+ }
+ break;
+
+ /* Pen coordinates packet */
+ case W8001_PKTLEN_TPCPEN - 1:
+ tmp = w8001->data[0] & W8001_TAB_MASK;
+ if (unlikely(tmp == W8001_TAB_BYTE))
+ break;
+
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+ if (tmp == W8001_TOUCH_BYTE)
+ break;
+
+ w8001->idx = 0;
+ parse_pen_data(w8001->data, &coord);
+ report_pen_events(w8001, &coord);
+ break;
+
+ /* control packet */
+ case W8001_PKTLEN_TPCCTL - 1:
+ tmp = w8001->data[0] & W8001_TOUCH_MASK;
+ if (tmp == W8001_TOUCH_BYTE)
+ break;
+
+ w8001->idx = 0;
+ memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH);
+ w8001->response_type = W8001_QUERY_PACKET;
+ complete(&w8001->cmd_done);
+ break;
+
+ /* 2 finger touch packet */
+ case W8001_PKTLEN_TOUCH2FG - 1:
+ w8001->idx = 0;
+ parse_multi_touch(w8001);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int w8001_command(struct w8001 *w8001, unsigned char command,
+ bool wait_response)
+{
+ int rc;
+
+ w8001->response_type = 0;
+ init_completion(&w8001->cmd_done);
+
+ rc = serio_write(w8001->serio, command);
+ if (rc == 0 && wait_response) {
+
+ wait_for_completion_timeout(&w8001->cmd_done, HZ);
+ if (w8001->response_type != W8001_QUERY_PACKET)
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+static int w8001_open(struct input_dev *dev)
+{
+ struct w8001 *w8001 = input_get_drvdata(dev);
+
+ return w8001_command(w8001, W8001_CMD_START, false);
+}
+
+static void w8001_close(struct input_dev *dev)
+{
+ struct w8001 *w8001 = input_get_drvdata(dev);
+
+ w8001_command(w8001, W8001_CMD_STOP, false);
+}
+
+static int w8001_setup(struct w8001 *w8001)
+{
+ struct input_dev *dev = w8001->dev;
+ struct w8001_coord coord;
+ struct w8001_touch_query touch;
+ int error;
+
+ error = w8001_command(w8001, W8001_CMD_STOP, false);
+ if (error)
+ return error;
+
+ msleep(250); /* wait 250ms before querying the device */
+
+ dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+
+ /* penabled? */
+ error = w8001_command(w8001, W8001_CMD_QUERY, true);
+ if (!error) {
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_PEN, dev->keybit);
+ __set_bit(BTN_TOOL_RUBBER, dev->keybit);
+ __set_bit(BTN_STYLUS, dev->keybit);
+ __set_bit(BTN_STYLUS2, dev->keybit);
+
+ parse_pen_data(w8001->response, &coord);
+ w8001->max_pen_x = coord.x;
+ w8001->max_pen_y = coord.y;
+
+ input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
+ input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
+ input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
+ if (coord.tilt_x && coord.tilt_y) {
+ input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
+ input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
+ }
+ w8001->id = 0x90;
+ strlcat(w8001->name, " Penabled", sizeof(w8001->name));
+ }
+
+ /* Touch enabled? */
+ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
+
+ /*
+ * Some non-touch devices may reply to the touch query. But their
+ * second byte is empty, which indicates touch is not supported.
+ */
+ if (!error && w8001->response[1]) {
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+
+ parse_touchquery(w8001->response, &touch);
+ w8001->max_touch_x = touch.x;
+ w8001->max_touch_y = touch.y;
+
+ if (w8001->max_pen_x && w8001->max_pen_y) {
+ /* if pen is supported scale to pen maximum */
+ touch.x = w8001->max_pen_x;
+ touch.y = w8001->max_pen_y;
+ touch.panel_res = W8001_PEN_RESOLUTION;
+ }
+
+ input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
+ input_abs_set_res(dev, ABS_X, touch.panel_res);
+ input_abs_set_res(dev, ABS_Y, touch.panel_res);
+
+ switch (touch.sensor_id) {
+ case 0:
+ case 2:
+ w8001->pktlen = W8001_PKTLEN_TOUCH93;
+ w8001->id = 0x93;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+ break;
+
+ case 1:
+ case 3:
+ case 4:
+ w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+ w8001->id = 0x9a;
+ break;
+
+ case 5:
+ w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+ input_mt_init_slots(dev, 2);
+ input_set_abs_params(dev, ABS_MT_POSITION_X,
+ 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y,
+ 0, touch.y, 0, 0);
+ input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+
+ strlcat(w8001->name, " 2FG", sizeof(w8001->name));
+ if (w8001->max_pen_x && w8001->max_pen_y)
+ w8001->id = 0xE3;
+ else
+ w8001->id = 0xE2;
+ break;
+ }
+ }
+
+ strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+
+ return 0;
+}
+
+/*
+ * w8001_disconnect() is the opposite of w8001_connect()
+ */
+
+static void w8001_disconnect(struct serio *serio)
+{
+ struct w8001 *w8001 = serio_get_drvdata(serio);
+
+ serio_close(serio);
+
+ input_unregister_device(w8001->dev);
+ kfree(w8001);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+/*
+ * w8001_connect() is the routine that is called when someone adds a
+ * new serio device that supports the w8001 protocol and registers it as
+ * an input device.
+ */
+
+static int w8001_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct w8001 *w8001;
+ struct input_dev *input_dev;
+ int err;
+
+ w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!w8001 || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ w8001->serio = serio;
+ w8001->dev = input_dev;
+ init_completion(&w8001->cmd_done);
+ snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
+
+ serio_set_drvdata(serio, w8001);
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = w8001_setup(w8001);
+ if (err)
+ goto fail3;
+
+ input_dev->name = w8001->name;
+ input_dev->phys = w8001->phys;
+ input_dev->id.product = w8001->id;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = 0x056a;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->open = w8001_open;
+ input_dev->close = w8001_close;
+
+ input_set_drvdata(input_dev, w8001);
+
+ err = input_register_device(w8001->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+fail3:
+ serio_close(serio);
+fail2:
+ serio_set_drvdata(serio, NULL);
+fail1:
+ input_free_device(input_dev);
+ kfree(w8001);
+ return err;
+}
+
+static struct serio_device_id w8001_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_W8001,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, w8001_serio_ids);
+
+static struct serio_driver w8001_drv = {
+ .driver = {
+ .name = "w8001",
+ },
+ .description = DRIVER_DESC,
+ .id_table = w8001_serio_ids,
+ .interrupt = w8001_interrupt,
+ .connect = w8001_connect,
+ .disconnect = w8001_disconnect,
+};
+
+static int __init w8001_init(void)
+{
+ return serio_register_driver(&w8001_drv);
+}
+
+static void __exit w8001_exit(void)
+{
+ serio_unregister_driver(&w8001_drv);
+}
+
+module_init(w8001_init);
+module_exit(w8001_exit);
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
new file mode 100644
index 00000000..4bc851a9
--- /dev/null
+++ b/drivers/input/touchscreen/wm831x-ts.c
@@ -0,0 +1,410 @@
+/*
+ * Touchscreen driver for WM831x PMICs
+ *
+ * Copyright 2011 Wolfson Microelectronics plc.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * R16424 (0x4028) - Touch Control 1
+ */
+#define WM831X_TCH_ENA 0x8000 /* TCH_ENA */
+#define WM831X_TCH_CVT_ENA 0x4000 /* TCH_CVT_ENA */
+#define WM831X_TCH_SLPENA 0x1000 /* TCH_SLPENA */
+#define WM831X_TCH_Z_ENA 0x0400 /* TCH_Z_ENA */
+#define WM831X_TCH_Y_ENA 0x0200 /* TCH_Y_ENA */
+#define WM831X_TCH_X_ENA 0x0100 /* TCH_X_ENA */
+#define WM831X_TCH_DELAY_MASK 0x00E0 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_SHIFT 5 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_WIDTH 3 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_RATE_MASK 0x001F /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_SHIFT 0 /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_WIDTH 5 /* TCH_RATE - [4:0] */
+
+/*
+ * R16425 (0x4029) - Touch Control 2
+ */
+#define WM831X_TCH_PD_WK 0x2000 /* TCH_PD_WK */
+#define WM831X_TCH_5WIRE 0x1000 /* TCH_5WIRE */
+#define WM831X_TCH_PDONLY 0x0800 /* TCH_PDONLY */
+#define WM831X_TCH_ISEL 0x0100 /* TCH_ISEL */
+#define WM831X_TCH_RPU_MASK 0x000F /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_SHIFT 0 /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_WIDTH 4 /* TCH_RPU - [3:0] */
+
+/*
+ * R16426-8 (0x402A-C) - Touch Data X/Y/X
+ */
+#define WM831X_TCH_PD 0x8000 /* TCH_PD1 */
+#define WM831X_TCH_DATA_MASK 0x0FFF /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_SHIFT 0 /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_WIDTH 12 /* TCH_DATA - [11:0] */
+
+struct wm831x_ts {
+ struct input_dev *input_dev;
+ struct wm831x *wm831x;
+ unsigned int data_irq;
+ unsigned int pd_irq;
+ bool pressure;
+ bool pen_down;
+ struct work_struct pd_data_work;
+};
+
+static void wm831x_pd_data_work(struct work_struct *work)
+{
+ struct wm831x_ts *wm831x_ts =
+ container_of(work, struct wm831x_ts, pd_data_work);
+
+ if (wm831x_ts->pen_down) {
+ enable_irq(wm831x_ts->data_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
+ } else {
+ enable_irq(wm831x_ts->pd_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
+ }
+}
+
+static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
+{
+ struct wm831x_ts *wm831x_ts = irq_data;
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+ static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
+ u16 data[3];
+ int count;
+ int i, ret;
+
+ if (wm831x_ts->pressure)
+ count = 3;
+ else
+ count = 2;
+
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+ ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
+ data);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to read touch data: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ /*
+ * We get a pen down reading on every reading, report pen up if any
+ * individual reading does so.
+ */
+ wm831x_ts->pen_down = true;
+ for (i = 0; i < count; i++) {
+ if (!(data[i] & WM831X_TCH_PD)) {
+ wm831x_ts->pen_down = false;
+ continue;
+ }
+ input_report_abs(wm831x_ts->input_dev, data_types[i],
+ data[i] & WM831X_TCH_DATA_MASK);
+ }
+
+ if (!wm831x_ts->pen_down) {
+ /* Switch from data to pen down */
+ dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
+
+ disable_irq_nosync(wm831x_ts->data_irq);
+
+ /* Don't need data any more */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+ WM831X_TCH_Z_ENA, 0);
+
+ /* Flush any final samples that arrived while reading */
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+ wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
+
+ if (wm831x_ts->pressure)
+ input_report_abs(wm831x_ts->input_dev,
+ ABS_PRESSURE, 0);
+
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
+
+ schedule_work(&wm831x_ts->pd_data_work);
+ } else {
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
+ }
+
+ input_sync(wm831x_ts->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
+{
+ struct wm831x_ts *wm831x_ts = irq_data;
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+ int ena = 0;
+
+ if (wm831x_ts->pen_down)
+ return IRQ_HANDLED;
+
+ disable_irq_nosync(wm831x_ts->pd_irq);
+
+ /* Start collecting data */
+ if (wm831x_ts->pressure)
+ ena |= WM831X_TCH_Z_ENA;
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
+
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
+
+ wm831x_ts->pen_down = true;
+
+ /* Switch from pen down to data */
+ dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
+ schedule_work(&wm831x_ts->pd_data_work);
+
+ return IRQ_HANDLED;
+}
+
+static int wm831x_ts_input_open(struct input_dev *idev)
+{
+ struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+ WM831X_TCH_Z_ENA, WM831X_TCH_ENA);
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
+
+ return 0;
+}
+
+static void wm831x_ts_input_close(struct input_dev *idev)
+{
+ struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+
+ /* Shut the controller down, disabling all other functionality too */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_ENA | WM831X_TCH_X_ENA |
+ WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
+
+ /* Make sure any pending IRQs are done, the above will prevent
+ * new ones firing.
+ */
+ synchronize_irq(wm831x_ts->data_irq);
+ synchronize_irq(wm831x_ts->pd_irq);
+
+ /* Make sure the IRQ completion work is quiesced */
+ flush_work_sync(&wm831x_ts->pd_data_work);
+
+ /* If we ended up with the pen down then make sure we revert back
+ * to pen detection state for the next time we start up.
+ */
+ if (wm831x_ts->pen_down) {
+ disable_irq(wm831x_ts->data_irq);
+ enable_irq(wm831x_ts->pd_irq);
+ wm831x_ts->pen_down = false;
+ }
+}
+
+static __devinit int wm831x_ts_probe(struct platform_device *pdev)
+{
+ struct wm831x_ts *wm831x_ts;
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
+ struct wm831x_touch_pdata *pdata = NULL;
+ struct input_dev *input_dev;
+ int error, irqf;
+
+ if (core_pdata)
+ pdata = core_pdata->touch;
+
+ wm831x_ts = kzalloc(sizeof(struct wm831x_ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!wm831x_ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_alloc;
+ }
+
+ wm831x_ts->wm831x = wm831x;
+ wm831x_ts->input_dev = input_dev;
+ INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
+
+ /*
+ * If we have a direct IRQ use it, otherwise use the interrupt
+ * from the WM831x IRQ controller.
+ */
+ if (pdata && pdata->data_irq)
+ wm831x_ts->data_irq = pdata->data_irq;
+ else
+ wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA");
+
+ if (pdata && pdata->pd_irq)
+ wm831x_ts->pd_irq = pdata->pd_irq;
+ else
+ wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD");
+
+ if (pdata)
+ wm831x_ts->pressure = pdata->pressure;
+ else
+ wm831x_ts->pressure = true;
+
+ /* Five wire touchscreens can't report pressure */
+ if (pdata && pdata->fivewire) {
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
+
+ /* Pressure measurements are not possible for five wire mode */
+ WARN_ON(pdata->pressure && pdata->fivewire);
+ wm831x_ts->pressure = false;
+ } else {
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_5WIRE, 0);
+ }
+
+ if (pdata) {
+ switch (pdata->isel) {
+ default:
+ dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
+ pdata->isel);
+ /* Fall through */
+ case 200:
+ case 0:
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_ISEL, 0);
+ break;
+ case 400:
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_ISEL, WM831X_TCH_ISEL);
+ break;
+ }
+ }
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_PDONLY, 0);
+
+ /* Default to 96 samples/sec */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_RATE_MASK, 6);
+
+ if (pdata && pdata->data_irqf)
+ irqf = pdata->data_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
+ error = request_threaded_irq(wm831x_ts->data_irq,
+ NULL, wm831x_ts_data_irq,
+ irqf | IRQF_ONESHOT,
+ "Touchscreen data", wm831x_ts);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
+ wm831x_ts->data_irq, error);
+ goto err_alloc;
+ }
+ disable_irq(wm831x_ts->data_irq);
+
+ if (pdata && pdata->pd_irqf)
+ irqf = pdata->pd_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
+ error = request_threaded_irq(wm831x_ts->pd_irq,
+ NULL, wm831x_ts_pen_down_irq,
+ irqf | IRQF_ONESHOT,
+ "Touchscreen pen down", wm831x_ts);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
+ wm831x_ts->pd_irq, error);
+ goto err_data_irq;
+ }
+
+ /* set up touch configuration */
+ input_dev->name = "WM831x touchscreen";
+ input_dev->phys = "wm831x";
+ input_dev->open = wm831x_ts_input_open;
+ input_dev->close = wm831x_ts_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
+ if (wm831x_ts->pressure)
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
+
+ input_set_drvdata(input_dev, wm831x_ts);
+ input_dev->dev.parent = &pdev->dev;
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_pd_irq;
+
+ platform_set_drvdata(pdev, wm831x_ts);
+ return 0;
+
+err_pd_irq:
+ free_irq(wm831x_ts->pd_irq, wm831x_ts);
+err_data_irq:
+ free_irq(wm831x_ts->data_irq, wm831x_ts);
+err_alloc:
+ input_free_device(input_dev);
+ kfree(wm831x_ts);
+
+ return error;
+}
+
+static __devexit int wm831x_ts_remove(struct platform_device *pdev)
+{
+ struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
+
+ free_irq(wm831x_ts->pd_irq, wm831x_ts);
+ free_irq(wm831x_ts->data_irq, wm831x_ts);
+ input_unregister_device(wm831x_ts->input_dev);
+ kfree(wm831x_ts);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver wm831x_ts_driver = {
+ .driver = {
+ .name = "wm831x-touch",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_ts_probe,
+ .remove = __devexit_p(wm831x_ts_remove),
+};
+module_platform_driver(wm831x_ts_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-touch");
diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
new file mode 100644
index 00000000..adc13a52
--- /dev/null
+++ b/drivers/input/touchscreen/wm9705.c
@@ -0,0 +1,350 @@
+/*
+ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME "wm97xx"
+#define WM9705_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ * pil = 1 to use 200uA and
+ * pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Pen detect comparator threshold.
+ *
+ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
+ * i.e. 1 = Vmid/15 threshold
+ * 15 = Vmid/1 threshold
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down events.
+ */
+static int pdd = 8;
+module_param(pdd, int, 0);
+MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+ 21, /* 1 AC97 Link frames */
+ 42, /* 2 */
+ 84, /* 4 */
+ 167, /* 8 */
+ 333, /* 16 */
+ 667, /* 32 */
+ 1000, /* 48 */
+ 1333, /* 64 */
+ 2000, /* 96 */
+ 2667, /* 128 */
+ 3333, /* 160 */
+ 4000, /* 192 */
+ 4667, /* 224 */
+ 5333, /* 256 */
+ 6000, /* 288 */
+ 0 /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9705
+ */
+static void wm9705_phy_init(struct wm97xx *wm)
+{
+ u16 dig1 = 0, dig2 = WM97XX_RPR;
+
+ /*
+ * mute VIDEO and AUX as they share X and Y touchscreen
+ * inputs on the WM9705
+ */
+ wm97xx_reg_write(wm, AC97_AUX, 0x8000);
+ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
+
+ /* touchpanel pressure current*/
+ if (pil == 2) {
+ dig2 |= WM9705_PIL;
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 400uA.");
+ } else if (pil)
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 200uA.");
+ if (!pil)
+ pressure = 0;
+
+ /* polling mode sample settling delay */
+ if (delay != 4) {
+ if (delay < 0 || delay > 15) {
+ dev_dbg(wm->dev, "supplied delay out of range.");
+ delay = 4;
+ }
+ }
+ dig1 &= 0xff0f;
+ dig1 |= WM97XX_DELAY(delay);
+ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
+ delay_table[delay]);
+
+ /* WM9705 pdd */
+ dig2 |= (pdd & 0x000f);
+ dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
+
+ /* mask */
+ dig2 |= ((mask & 0x3) << 4);
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9705_dig_enable(struct wm97xx *wm, int enable)
+{
+ if (enable) {
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ wm->dig[2] | WM97XX_PRP_DET_DIG);
+ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+ } else
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ wm->dig[2] & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_aux_prepare(struct wm97xx *wm)
+{
+ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_dig_restore(struct wm97xx *wm)
+{
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+ return wm->dig[2] & WM9705_PDEN;
+}
+
+/*
+ * Read a sample from the WM9705 adc in polling mode.
+ */
+static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+ int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+ if (wants_pen && !wm->pen_probably_down) {
+ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(adcsel);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+ | WM97XX_POLL | WM97XX_DELAY(delay));
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(adcsel);
+
+ /* check we have correct sample */
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
+ return RC_PENUP;
+ }
+
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+
+ return RC_VALID;
+}
+
+/*
+ * Sample the WM9705 touchscreen in polling mode
+ */
+static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int rc;
+
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
+ if (rc != RC_VALID)
+ return rc;
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
+ if (rc != RC_VALID)
+ return rc;
+ if (pil) {
+ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);
+ if (rc != RC_VALID)
+ return rc;
+ } else
+ data->p = DEFAULT_PRESSURE;
+
+ return RC_VALID;
+}
+
+/*
+ * Enable WM9705 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9705_acc_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig1, dig2;
+ int ret = 0;
+
+ dig1 = wm->dig[1];
+ dig2 = wm->dig[2];
+
+ if (enable) {
+ /* continuous mode */
+ if (wm->mach_ops->acc_startup &&
+ (ret = wm->mach_ops->acc_startup(wm)) < 0)
+ return ret;
+ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+ WM97XX_DELAY(delay) |
+ WM97XX_SLT(wm->acc_slot) |
+ WM97XX_RATE(wm->acc_rate);
+ if (pil)
+ dig1 |= WM97XX_ADCSEL_PRES;
+ dig2 |= WM9705_PDEN;
+ } else {
+ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+ dig2 &= ~WM9705_PDEN;
+ if (wm->mach_ops->acc_shutdown)
+ wm->mach_ops->acc_shutdown(wm);
+ }
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+ return ret;
+}
+
+struct wm97xx_codec_drv wm9705_codec = {
+ .id = WM9705_ID2,
+ .name = "wm9705",
+ .poll_sample = wm9705_poll_sample,
+ .poll_touch = wm9705_poll_touch,
+ .acc_enable = wm9705_acc_enable,
+ .phy_init = wm9705_phy_init,
+ .dig_enable = wm9705_dig_enable,
+ .dig_restore = wm9705_dig_restore,
+ .aux_prepare = wm9705_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9705_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
new file mode 100644
index 00000000..6e743e3d
--- /dev/null
+++ b/drivers/input/touchscreen/wm9712.c
@@ -0,0 +1,467 @@
+/*
+ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME "wm97xx"
+#define WM9712_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ * pil = 1 to use 200uA and
+ * pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 3;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+ 21, /* 1 AC97 Link frames */
+ 42, /* 2 */
+ 84, /* 4 */
+ 167, /* 8 */
+ 333, /* 16 */
+ 667, /* 32 */
+ 1000, /* 48 */
+ 1333, /* 64 */
+ 2000, /* 96 */
+ 2667, /* 128 */
+ 3333, /* 160 */
+ 4000, /* 192 */
+ 4667, /* 224 */
+ 5333, /* 256 */
+ 6000, /* 288 */
+ 0 /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9712
+ */
+static void wm9712_phy_init(struct wm97xx *wm)
+{
+ u16 dig1 = 0;
+ u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
+
+ /* WM9712 rpu */
+ if (rpu) {
+ dig2 &= 0xffc0;
+ dig2 |= WM9712_RPU(rpu);
+ dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms",
+ 64000 / rpu);
+ }
+
+ /* WM9712 five wire */
+ if (five_wire) {
+ dig2 |= WM9712_45W;
+ dev_dbg(wm->dev, "setting 5-wire touchscreen mode.");
+
+ if (pil) {
+ dev_warn(wm->dev, "pressure measurement is not "
+ "supported in 5-wire mode\n");
+ pil = 0;
+ }
+ }
+
+ /* touchpanel pressure current*/
+ if (pil == 2) {
+ dig2 |= WM9712_PIL;
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 400uA.");
+ } else if (pil)
+ dev_dbg(wm->dev,
+ "setting pressure measurement current to 200uA.");
+ if (!pil)
+ pressure = 0;
+
+ /* polling mode sample settling delay */
+ if (delay < 0 || delay > 15) {
+ dev_dbg(wm->dev, "supplied delay out of range.");
+ delay = 4;
+ }
+ dig1 &= 0xff0f;
+ dig1 |= WM97XX_DELAY(delay);
+ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
+ delay_table[delay]);
+
+ /* mask */
+ dig2 |= ((mask & 0x3) << 6);
+ if (mask) {
+ u16 reg;
+ /* Set GPIO4 as Mask Pin*/
+ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
+ }
+
+ /* wait - coord mode */
+ if (coord)
+ dig2 |= WM9712_WAIT;
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9712_dig_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig2 = wm->dig[2];
+
+ if (enable) {
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ dig2 | WM97XX_PRP_DET_DIG);
+ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+ } else
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+ dig2 & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_aux_prepare(struct wm97xx *wm)
+{
+ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_dig_restore(struct wm97xx *wm)
+{
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+ return wm->dig[2] & WM9712_PDEN;
+}
+
+/*
+ * Read a sample from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+ int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+ if (wants_pen && !wm->pen_probably_down) {
+ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(adcsel);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+ | WM97XX_POLL | WM97XX_DELAY(delay));
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(adcsel);
+
+ /* check we have correct sample */
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
+ return RC_PENUP;
+ }
+
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+
+ return RC_VALID;
+}
+
+/*
+ * Read a coord from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int timeout = 5 * delay;
+
+ if (!wm->pen_probably_down) {
+ u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data_rd & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
+ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
+
+ /* wait 3 AC97 time slots + delay for conversion and read x */
+ poll_delay(delay);
+ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ /* read back y data */
+ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (pil)
+ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ else
+ data->p = DEFAULT_PRESSURE;
+
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+ /* check we have correct sample */
+ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+ goto err;
+ if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+ goto err;
+
+ if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+ return RC_VALID;
+err:
+ return 0;
+}
+
+/*
+ * Sample the WM9712 touchscreen in polling mode
+ */
+static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int rc;
+
+ if (coord) {
+ rc = wm9712_poll_coord(wm, data);
+ if (rc != RC_VALID)
+ return rc;
+ } else {
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN,
+ &data->x);
+ if (rc != RC_VALID)
+ return rc;
+
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN,
+ &data->y);
+ if (rc != RC_VALID)
+ return rc;
+
+ if (pil && !five_wire) {
+ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
+ &data->p);
+ if (rc != RC_VALID)
+ return rc;
+ } else
+ data->p = DEFAULT_PRESSURE;
+ }
+ return RC_VALID;
+}
+
+/*
+ * Enable WM9712 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9712_acc_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig1, dig2;
+ int ret = 0;
+
+ dig1 = wm->dig[1];
+ dig2 = wm->dig[2];
+
+ if (enable) {
+ /* continuous mode */
+ if (wm->mach_ops->acc_startup) {
+ ret = wm->mach_ops->acc_startup(wm);
+ if (ret < 0)
+ return ret;
+ }
+ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+ WM97XX_DELAY(delay) |
+ WM97XX_SLT(wm->acc_slot) |
+ WM97XX_RATE(wm->acc_rate);
+ if (pil)
+ dig1 |= WM97XX_ADCSEL_PRES;
+ dig2 |= WM9712_PDEN;
+ } else {
+ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+ dig2 &= ~WM9712_PDEN;
+ if (wm->mach_ops->acc_shutdown)
+ wm->mach_ops->acc_shutdown(wm);
+ }
+
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+ return 0;
+}
+
+struct wm97xx_codec_drv wm9712_codec = {
+ .id = WM9712_ID2,
+ .name = "wm9712",
+ .poll_sample = wm9712_poll_sample,
+ .poll_touch = wm9712_poll_touch,
+ .acc_enable = wm9712_acc_enable,
+ .phy_init = wm9712_phy_init,
+ .dig_enable = wm9712_dig_enable,
+ .dig_restore = wm9712_dig_restore,
+ .aux_prepare = wm9712_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9712_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
new file mode 100644
index 00000000..74053531
--- /dev/null
+++ b/drivers/input/touchscreen/wm9713.c
@@ -0,0 +1,481 @@
+/*
+ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME "wm97xx"
+#define WM9713_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ * pil = 1 to use 200uA and
+ * pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+ 21, /* 1 AC97 Link frames */
+ 42, /* 2 */
+ 84, /* 4 */
+ 167, /* 8 */
+ 333, /* 16 */
+ 667, /* 32 */
+ 1000, /* 48 */
+ 1333, /* 64 */
+ 2000, /* 96 */
+ 2667, /* 128 */
+ 3333, /* 160 */
+ 4000, /* 192 */
+ 4667, /* 224 */
+ 5333, /* 256 */
+ 6000, /* 288 */
+ 0 /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9713
+ */
+static void wm9713_phy_init(struct wm97xx *wm)
+{
+ u16 dig1 = 0, dig2, dig3;
+
+ /* default values */
+ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5);
+ dig3 = WM9712_RPU(1);
+
+ /* rpu */
+ if (rpu) {
+ dig3 &= 0xffc0;
+ dig3 |= WM9712_RPU(rpu);
+ dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n",
+ 64000 / rpu);
+ }
+
+ /* Five wire panel? */
+ if (five_wire) {
+ dig3 |= WM9713_45W;
+ dev_info(wm->dev, "setting 5-wire touchscreen mode.");
+
+ if (pil) {
+ dev_warn(wm->dev,
+ "Pressure measurement not supported in 5 "
+ "wire mode, disabling\n");
+ pil = 0;
+ }
+ }
+
+ /* touchpanel pressure */
+ if (pil == 2) {
+ dig3 |= WM9712_PIL;
+ dev_info(wm->dev,
+ "setting pressure measurement current to 400uA.");
+ } else if (pil)
+ dev_info(wm->dev,
+ "setting pressure measurement current to 200uA.");
+ if (!pil)
+ pressure = 0;
+
+ /* sample settling delay */
+ if (delay < 0 || delay > 15) {
+ dev_info(wm->dev, "supplied delay out of range.");
+ delay = 4;
+ dev_info(wm->dev, "setting adc sample delay to %d u Secs.",
+ delay_table[delay]);
+ }
+ dig2 &= 0xff0f;
+ dig2 |= WM97XX_DELAY(delay);
+
+ /* mask */
+ dig3 |= ((mask & 0x3) << 4);
+ if (coord)
+ dig3 |= WM9713_WAIT;
+
+ wm->misc = wm97xx_reg_read(wm, 0x5a);
+
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
+}
+
+static void wm9713_dig_enable(struct wm97xx *wm, int enable)
+{
+ u16 val;
+
+ if (enable) {
+ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] |
+ WM97XX_PRP_DET_DIG);
+ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+ } else {
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] &
+ ~WM97XX_PRP_DET_DIG);
+ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
+ }
+}
+
+static void wm9713_dig_restore(struct wm97xx *wm)
+{
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
+}
+
+static void wm9713_aux_prepare(struct wm97xx *wm)
+{
+ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+ return wm->dig[2] & WM9713_PDEN;
+}
+
+/*
+ * Read a sample from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+ u16 dig1;
+ int timeout = 5 * delay;
+ bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+ if (wants_pen && !wm->pen_probably_down) {
+ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(data & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+ dig1 &= ~WM9713_ADCSEL_MASK;
+ /* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */
+ dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12);
+
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(adcsel);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL);
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) &&
+ timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(adcsel);
+
+ /* check we have correct sample */
+ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+ dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+ adcsel & WM97XX_ADCSEL_MASK,
+ *sample & WM97XX_ADCSEL_MASK);
+ return RC_PENUP;
+ }
+
+ if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+
+ return RC_VALID;
+}
+
+/*
+ * Read a coordinate from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ u16 dig1;
+ int timeout = 5 * delay;
+
+ if (!wm->pen_probably_down) {
+ u16 val = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (!(val & WM97XX_PEN_DOWN))
+ return RC_PENUP;
+ wm->pen_probably_down = 1;
+ }
+
+ /* set up digitiser */
+ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+ dig1 &= ~WM9713_ADCSEL_MASK;
+ if (pil)
+ dig1 |= WM9713_ADCSEL_PRES;
+
+ if (wm->mach_ops && wm->mach_ops->pre_sample)
+ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1,
+ dig1 | WM9713_POLL | WM9713_COO);
+
+ /* wait 3 AC97 time slots + delay for conversion */
+ poll_delay(delay);
+ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ /* wait for POLL to go low */
+ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL)
+ && timeout) {
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout <= 0) {
+ /* If PDEN is set, we can get a timeout when pen goes up */
+ if (is_pden(wm))
+ wm->pen_probably_down = 0;
+ else
+ dev_dbg(wm->dev, "adc sample timeout");
+ return RC_PENUP;
+ }
+
+ /* read back data */
+ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ if (pil)
+ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+ else
+ data->p = DEFAULT_PRESSURE;
+
+ if (wm->mach_ops && wm->mach_ops->post_sample)
+ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+ /* check we have correct sample */
+ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+ goto err;
+ if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+ goto err;
+
+ if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+ wm->pen_probably_down = 0;
+ return RC_PENUP;
+ }
+ return RC_VALID;
+err:
+ return 0;
+}
+
+/*
+ * Sample the WM9713 touchscreen in polling mode
+ */
+static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+ int rc;
+
+ if (coord) {
+ rc = wm9713_poll_coord(wm, data);
+ if (rc != RC_VALID)
+ return rc;
+ } else {
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
+ if (rc != RC_VALID)
+ return rc;
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
+ if (rc != RC_VALID)
+ return rc;
+ if (pil) {
+ rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
+ &data->p);
+ if (rc != RC_VALID)
+ return rc;
+ } else
+ data->p = DEFAULT_PRESSURE;
+ }
+ return RC_VALID;
+}
+
+/*
+ * Enable WM9713 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9713_acc_enable(struct wm97xx *wm, int enable)
+{
+ u16 dig1, dig2, dig3;
+ int ret = 0;
+
+ dig1 = wm->dig[0];
+ dig2 = wm->dig[1];
+ dig3 = wm->dig[2];
+
+ if (enable) {
+ /* continuous mode */
+ if (wm->mach_ops->acc_startup &&
+ (ret = wm->mach_ops->acc_startup(wm)) < 0)
+ return ret;
+
+ dig1 &= ~WM9713_ADCSEL_MASK;
+ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X |
+ WM9713_ADCSEL_Y;
+ if (pil)
+ dig1 |= WM9713_ADCSEL_PRES;
+ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK |
+ WM97XX_CM_RATE_MASK);
+ dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) |
+ WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate);
+ dig3 |= WM9713_PDEN;
+ } else {
+ dig1 &= ~(WM9713_CTC | WM9713_COO);
+ dig2 &= ~WM97XX_SLEN;
+ dig3 &= ~WM9713_PDEN;
+ if (wm->mach_ops->acc_shutdown)
+ wm->mach_ops->acc_shutdown(wm);
+ }
+
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+
+ return ret;
+}
+
+struct wm97xx_codec_drv wm9713_codec = {
+ .id = WM9713_ID2,
+ .name = "wm9713",
+ .poll_sample = wm9713_poll_sample,
+ .poll_touch = wm9713_poll_touch,
+ .acc_enable = wm9713_acc_enable,
+ .phy_init = wm9713_phy_init,
+ .dig_enable = wm9713_dig_enable,
+ .dig_restore = wm9713_dig_restore,
+ .aux_prepare = wm9713_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9713_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
new file mode 100644
index 00000000..5dbe73af
--- /dev/null
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -0,0 +1,848 @@
+/*
+ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712
+ * and WM9713 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * 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.
+ *
+ * Notes:
+ *
+ * Features:
+ * - supports WM9705, WM9712, WM9713
+ * - polling mode
+ * - continuous mode (arch-dependent)
+ * - adjustable rpu/dpp settings
+ * - adjustable pressure current
+ * - adjustable sample settle delay
+ * - 4 and 5 wire touchscreens (5 wire is WM9712 only)
+ * - pen down detection
+ * - battery monitor
+ * - sample AUX adcs
+ * - power management
+ * - codec GPIO
+ * - codec event notification
+ * Todo
+ * - Support for async sampling control for noisy LCDs.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/wm97xx.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define TS_NAME "wm97xx"
+#define WM_CORE_VERSION "1.00"
+#define DEFAULT_PRESSURE 0xb0c0
+
+
+/*
+ * Touchscreen absolute values
+ *
+ * These parameters are used to help the input layer discard out of
+ * range readings and reduce jitter etc.
+ *
+ * o min, max:- indicate the min and max values your touch screen returns
+ * o fuzz:- use a higher number to reduce jitter
+ *
+ * The default values correspond to Mainstone II in QVGA mode
+ *
+ * Please read
+ * Documentation/input/input-programming.txt for more details.
+ */
+
+static int abs_x[3] = {350, 3900, 5};
+module_param_array(abs_x, int, NULL, 0);
+MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
+
+static int abs_y[3] = {320, 3750, 40};
+module_param_array(abs_y, int, NULL, 0);
+MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
+
+static int abs_p[3] = {0, 150, 4};
+module_param_array(abs_p, int, NULL, 0);
+MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
+
+/*
+ * wm97xx IO access, all IO locking done by AC97 layer
+ */
+int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
+{
+ if (wm->ac97)
+ return wm->ac97->bus->ops->read(wm->ac97, reg);
+ else
+ return -1;
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_read);
+
+void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
+{
+ /* cache digitiser registers */
+ if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
+ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
+
+ /* cache gpio regs */
+ if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
+ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
+
+ /* wm9713 irq reg */
+ if (reg == 0x5a)
+ wm->misc = val;
+
+ if (wm->ac97)
+ wm->ac97->bus->ops->write(wm->ac97, reg, val);
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_write);
+
+/**
+ * wm97xx_read_aux_adc - Read the aux adc.
+ * @wm: wm97xx device.
+ * @adcsel: codec ADC to be read
+ *
+ * Reads the selected AUX ADC.
+ */
+
+int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
+{
+ int power_adc = 0, auxval;
+ u16 power = 0;
+ int rc = 0;
+ int timeout = 0;
+
+ /* get codec */
+ mutex_lock(&wm->codec_mutex);
+
+ /* When the touchscreen is not in use, we may have to power up
+ * the AUX ADC before we can use sample the AUX inputs->
+ */
+ if (wm->id == WM9713_ID2 &&
+ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
+ power_adc = 1;
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
+ }
+
+ /* Prepare the codec for AUX reading */
+ wm->codec->aux_prepare(wm);
+
+ /* Turn polling mode on to read AUX ADC */
+ wm->pen_probably_down = 1;
+
+ while (rc != RC_VALID && timeout++ < 5)
+ rc = wm->codec->poll_sample(wm, adcsel, &auxval);
+
+ if (power_adc)
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
+
+ wm->codec->dig_restore(wm);
+
+ wm->pen_probably_down = 0;
+
+ if (timeout >= 5) {
+ dev_err(wm->dev,
+ "timeout reading auxadc %d, disabling digitiser\n",
+ adcsel);
+ wm->codec->dig_enable(wm, false);
+ }
+
+ mutex_unlock(&wm->codec_mutex);
+ return (rc == RC_VALID ? auxval & 0xfff : -EBUSY);
+}
+EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
+
+/**
+ * wm97xx_get_gpio - Get the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ * Get the status of a codec GPIO pin
+ */
+
+enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
+{
+ u16 status;
+ enum wm97xx_gpio_status ret;
+
+ mutex_lock(&wm->codec_mutex);
+ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+ if (status & gpio)
+ ret = WM97XX_GPIO_HIGH;
+ else
+ ret = WM97XX_GPIO_LOW;
+
+ mutex_unlock(&wm->codec_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
+
+/**
+ * wm97xx_set_gpio - Set the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ *
+ * Set the status of a codec GPIO pin
+ */
+
+void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
+ enum wm97xx_gpio_status status)
+{
+ u16 reg;
+
+ mutex_lock(&wm->codec_mutex);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+ if (status == WM97XX_GPIO_HIGH)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
+ else
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
+ mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
+
+/*
+ * Codec GPIO pin configuration, this sets pin direction, polarity,
+ * stickyness and wake up.
+ */
+void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir,
+ enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky,
+ enum wm97xx_gpio_wake wake)
+{
+ u16 reg;
+
+ mutex_lock(&wm->codec_mutex);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+ if (pol == WM97XX_GPIO_POL_HIGH)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+
+ if (sticky == WM97XX_GPIO_STICKY)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+
+ if (wake == WM97XX_GPIO_WAKE)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
+ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+
+ if (dir == WM97XX_GPIO_IN)
+ reg |= gpio;
+ else
+ reg &= ~gpio;
+
+ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
+ mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
+
+/*
+ * Configure the WM97XX_PRP value to use while system is suspended.
+ * If a value other than 0 is set then WM97xx pen detection will be
+ * left enabled in the configured mode while the system is in suspend,
+ * the device has users and suspend has not been disabled via the
+ * wakeup sysfs entries.
+ *
+ * @wm: WM97xx device to configure
+ * @mode: WM97XX_PRP value to configure while suspended
+ */
+void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode)
+{
+ wm->suspend_mode = mode;
+ device_init_wakeup(&wm->input_dev->dev, mode != 0);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_suspend_mode);
+
+/*
+ * Handle a pen down interrupt.
+ */
+static void wm97xx_pen_irq_worker(struct work_struct *work)
+{
+ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
+ int pen_was_down = wm->pen_is_down;
+
+ /* do we need to enable the touch panel reader */
+ if (wm->id == WM9705_ID2) {
+ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) &
+ WM97XX_PEN_DOWN)
+ wm->pen_is_down = 1;
+ else
+ wm->pen_is_down = 0;
+ } else {
+ u16 status, pol;
+ mutex_lock(&wm->codec_mutex);
+ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+ if (WM97XX_GPIO_13 & pol & status) {
+ wm->pen_is_down = 1;
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol &
+ ~WM97XX_GPIO_13);
+ } else {
+ wm->pen_is_down = 0;
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol |
+ WM97XX_GPIO_13);
+ }
+
+ if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status &
+ ~WM97XX_GPIO_13) << 1);
+ else
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status &
+ ~WM97XX_GPIO_13);
+ mutex_unlock(&wm->codec_mutex);
+ }
+
+ /* If the system is not using continuous mode or it provides a
+ * pen down operation then we need to schedule polls while the
+ * pen is down. Otherwise the machine driver is responsible
+ * for scheduling reads.
+ */
+ if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) {
+ if (wm->pen_is_down && !pen_was_down) {
+ /* Data is not available immediately on pen down */
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1);
+ }
+
+ /* Let ts_reader report the pen up for debounce. */
+ if (!wm->pen_is_down && pen_was_down)
+ wm->pen_is_down = 1;
+ }
+
+ if (!wm->pen_is_down && wm->mach_ops->acc_enabled)
+ wm->mach_ops->acc_pen_up(wm);
+
+ wm->mach_ops->irq_enable(wm, 1);
+}
+
+/*
+ * Codec PENDOWN irq handler
+ *
+ * We have to disable the codec interrupt in the handler because it
+ * can take up to 1ms to clear the interrupt source. We schedule a task
+ * in a work queue to do the actual interaction with the chip. The
+ * interrupt is then enabled again in the slow handler when the source
+ * has been cleared.
+ */
+static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+{
+ struct wm97xx *wm = dev_id;
+
+ if (!work_pending(&wm->pen_event_work)) {
+ wm->mach_ops->irq_enable(wm, 0);
+ queue_work(wm->ts_workq, &wm->pen_event_work);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * initialise pen IRQ handler and workqueue
+ */
+static int wm97xx_init_pen_irq(struct wm97xx *wm)
+{
+ u16 reg;
+
+ /* If an interrupt is supplied an IRQ enable operation must also be
+ * provided. */
+ BUG_ON(!wm->mach_ops->irq_enable);
+
+ if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED,
+ "wm97xx-pen", wm)) {
+ dev_err(wm->dev,
+ "Failed to register pen down interrupt, polling");
+ wm->pen_irq = 0;
+ return -EINVAL;
+ }
+
+ /* Configure GPIO as interrupt source on WM971x */
+ if (wm->id != WM9705_ID2) {
+ BUG_ON(!wm->mach_ops->irq_gpio);
+ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+ wm97xx_reg_write(wm, AC97_MISC_AFE,
+ reg & ~(wm->mach_ops->irq_gpio));
+ reg = wm97xx_reg_read(wm, 0x5a);
+ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
+ }
+
+ return 0;
+}
+
+static int wm97xx_read_samples(struct wm97xx *wm)
+{
+ struct wm97xx_data data;
+ int rc;
+
+ mutex_lock(&wm->codec_mutex);
+
+ if (wm->mach_ops && wm->mach_ops->acc_enabled)
+ rc = wm->mach_ops->acc_pen_down(wm);
+ else
+ rc = wm->codec->poll_touch(wm, &data);
+
+ if (rc & RC_PENUP) {
+ if (wm->pen_is_down) {
+ wm->pen_is_down = 0;
+ dev_dbg(wm->dev, "pen up\n");
+ input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
+ input_report_key(wm->input_dev, BTN_TOUCH, 0);
+ input_sync(wm->input_dev);
+ } else if (!(rc & RC_AGAIN)) {
+ /* We need high frequency updates only while
+ * pen is down, the user never will be able to
+ * touch screen faster than a few times per
+ * second... On the other hand, when the user
+ * is actively working with the touchscreen we
+ * don't want to lose the quick response. So we
+ * will slowly increase sleep time after the
+ * pen is up and quicky restore it to ~one task
+ * switch when pen is down again.
+ */
+ if (wm->ts_reader_interval < HZ / 10)
+ wm->ts_reader_interval++;
+ }
+
+ } else if (rc & RC_VALID) {
+ dev_dbg(wm->dev,
+ "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
+ data.x >> 12, data.x & 0xfff, data.y >> 12,
+ data.y & 0xfff, data.p >> 12, data.p & 0xfff);
+ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
+ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
+ input_report_key(wm->input_dev, BTN_TOUCH, 1);
+ input_sync(wm->input_dev);
+ wm->pen_is_down = 1;
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+ } else if (rc & RC_PENDOWN) {
+ dev_dbg(wm->dev, "pen down\n");
+ wm->pen_is_down = 1;
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+ }
+
+ mutex_unlock(&wm->codec_mutex);
+ return rc;
+}
+
+/*
+* The touchscreen sample reader.
+*/
+static void wm97xx_ts_reader(struct work_struct *work)
+{
+ int rc;
+ struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work);
+
+ BUG_ON(!wm->codec);
+
+ do {
+ rc = wm97xx_read_samples(wm);
+ } while (rc & RC_AGAIN);
+
+ if (wm->pen_is_down || !wm->pen_irq)
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+ wm->ts_reader_interval);
+}
+
+/**
+ * wm97xx_ts_input_open - Open the touch screen input device.
+ * @idev: Input device to be opened.
+ *
+ * Called by the input sub system to open a wm97xx touchscreen device.
+ * Starts the touchscreen thread and touch digitiser.
+ */
+static int wm97xx_ts_input_open(struct input_dev *idev)
+{
+ struct wm97xx *wm = input_get_drvdata(idev);
+
+ wm->ts_workq = create_singlethread_workqueue("kwm97xx");
+ if (wm->ts_workq == NULL) {
+ dev_err(wm->dev,
+ "Failed to create workqueue\n");
+ return -EINVAL;
+ }
+
+ /* start digitiser */
+ if (wm->mach_ops && wm->mach_ops->acc_enabled)
+ wm->codec->acc_enable(wm, 1);
+ wm->codec->dig_enable(wm, 1);
+
+ INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader);
+ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
+
+ wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1;
+ if (wm->ts_reader_min_interval < 1)
+ wm->ts_reader_min_interval = 1;
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+
+ wm->pen_is_down = 0;
+ if (wm->pen_irq)
+ wm97xx_init_pen_irq(wm);
+ else
+ dev_err(wm->dev, "No IRQ specified\n");
+
+ /* If we either don't have an interrupt for pen down events or
+ * failed to acquire it then we need to poll.
+ */
+ if (wm->pen_irq == 0)
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+ wm->ts_reader_interval);
+
+ return 0;
+}
+
+/**
+ * wm97xx_ts_input_close - Close the touch screen input device.
+ * @idev: Input device to be closed.
+ *
+ * Called by the input sub system to close a wm97xx touchscreen
+ * device. Kills the touchscreen thread and stops the touch
+ * digitiser.
+ */
+
+static void wm97xx_ts_input_close(struct input_dev *idev)
+{
+ struct wm97xx *wm = input_get_drvdata(idev);
+ u16 reg;
+
+ if (wm->pen_irq) {
+ /* Return the interrupt to GPIO usage (disabling it) */
+ if (wm->id != WM9705_ID2) {
+ BUG_ON(!wm->mach_ops->irq_gpio);
+ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+ wm97xx_reg_write(wm, AC97_MISC_AFE,
+ reg | wm->mach_ops->irq_gpio);
+ }
+
+ free_irq(wm->pen_irq, wm);
+ }
+
+ wm->pen_is_down = 0;
+
+ /* Balance out interrupt disables/enables */
+ if (cancel_work_sync(&wm->pen_event_work))
+ wm->mach_ops->irq_enable(wm, 1);
+
+ /* ts_reader rearms itself so we need to explicitly stop it
+ * before we destroy the workqueue.
+ */
+ cancel_delayed_work_sync(&wm->ts_reader);
+
+ destroy_workqueue(wm->ts_workq);
+
+ /* stop digitiser */
+ wm->codec->dig_enable(wm, 0);
+ if (wm->mach_ops && wm->mach_ops->acc_enabled)
+ wm->codec->acc_enable(wm, 0);
+}
+
+static int wm97xx_probe(struct device *dev)
+{
+ struct wm97xx *wm;
+ struct wm97xx_pdata *pdata = dev->platform_data;
+ int ret = 0, id = 0;
+
+ wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL);
+ if (!wm)
+ return -ENOMEM;
+ mutex_init(&wm->codec_mutex);
+
+ wm->dev = dev;
+ dev_set_drvdata(dev, wm);
+ wm->ac97 = to_ac97_t(dev);
+
+ /* check that we have a supported codec */
+ id = wm97xx_reg_read(wm, AC97_VENDOR_ID1);
+ if (id != WM97XX_ID1) {
+ dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id);
+ ret = -ENODEV;
+ goto alloc_err;
+ }
+
+ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
+
+ wm->variant = WM97xx_GENERIC;
+
+ dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff);
+
+ switch (wm->id & 0xff) {
+#ifdef CONFIG_TOUCHSCREEN_WM9705
+ case 0x05:
+ wm->codec = &wm9705_codec;
+ break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9712
+ case 0x12:
+ wm->codec = &wm9712_codec;
+ break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9713
+ case 0x13:
+ wm->codec = &wm9713_codec;
+ break;
+#endif
+ default:
+ dev_err(wm->dev, "Support for wm97%02x not compiled in.\n",
+ wm->id & 0xff);
+ ret = -ENODEV;
+ goto alloc_err;
+ }
+
+ /* set up physical characteristics */
+ wm->codec->phy_init(wm);
+
+ /* load gpio cache */
+ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
+
+ wm->input_dev = input_allocate_device();
+ if (wm->input_dev == NULL) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+
+ /* set up touch configuration */
+ wm->input_dev->name = "wm97xx touchscreen";
+ wm->input_dev->phys = "wm97xx";
+ wm->input_dev->open = wm97xx_ts_input_open;
+ wm->input_dev->close = wm97xx_ts_input_close;
+
+ __set_bit(EV_ABS, wm->input_dev->evbit);
+ __set_bit(EV_KEY, wm->input_dev->evbit);
+ __set_bit(BTN_TOUCH, wm->input_dev->keybit);
+
+ input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
+ abs_x[2], 0);
+ input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
+ abs_y[2], 0);
+ input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
+ abs_p[2], 0);
+
+ input_set_drvdata(wm->input_dev, wm);
+ wm->input_dev->dev.parent = dev;
+
+ ret = input_register_device(wm->input_dev);
+ if (ret < 0)
+ goto dev_alloc_err;
+
+ /* register our battery device */
+ wm->battery_dev = platform_device_alloc("wm97xx-battery", -1);
+ if (!wm->battery_dev) {
+ ret = -ENOMEM;
+ goto batt_err;
+ }
+ platform_set_drvdata(wm->battery_dev, wm);
+ wm->battery_dev->dev.parent = dev;
+ wm->battery_dev->dev.platform_data = pdata;
+ ret = platform_device_add(wm->battery_dev);
+ if (ret < 0)
+ goto batt_reg_err;
+
+ /* register our extended touch device (for machine specific
+ * extensions) */
+ wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
+ if (!wm->touch_dev) {
+ ret = -ENOMEM;
+ goto touch_err;
+ }
+ platform_set_drvdata(wm->touch_dev, wm);
+ wm->touch_dev->dev.parent = dev;
+ wm->touch_dev->dev.platform_data = pdata;
+ ret = platform_device_add(wm->touch_dev);
+ if (ret < 0)
+ goto touch_reg_err;
+
+ return ret;
+
+ touch_reg_err:
+ platform_device_put(wm->touch_dev);
+ touch_err:
+ platform_device_del(wm->battery_dev);
+ batt_reg_err:
+ platform_device_put(wm->battery_dev);
+ batt_err:
+ input_unregister_device(wm->input_dev);
+ wm->input_dev = NULL;
+ dev_alloc_err:
+ input_free_device(wm->input_dev);
+ alloc_err:
+ kfree(wm);
+
+ return ret;
+}
+
+static int wm97xx_remove(struct device *dev)
+{
+ struct wm97xx *wm = dev_get_drvdata(dev);
+
+ platform_device_unregister(wm->battery_dev);
+ platform_device_unregister(wm->touch_dev);
+ input_unregister_device(wm->input_dev);
+ kfree(wm);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm97xx_suspend(struct device *dev, pm_message_t state)
+{
+ struct wm97xx *wm = dev_get_drvdata(dev);
+ u16 reg;
+ int suspend_mode;
+
+ if (device_may_wakeup(&wm->input_dev->dev))
+ suspend_mode = wm->suspend_mode;
+ else
+ suspend_mode = 0;
+
+ if (wm->input_dev->users)
+ cancel_delayed_work_sync(&wm->ts_reader);
+
+ /* Power down the digitiser (bypassing the cache for resume) */
+ reg = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER2);
+ reg &= ~WM97XX_PRP_DET_DIG;
+ if (wm->input_dev->users)
+ reg |= suspend_mode;
+ wm->ac97->bus->ops->write(wm->ac97, AC97_WM97XX_DIGITISER2, reg);
+
+ /* WM9713 has an additional power bit - turn it off if there
+ * are no users or if suspend mode is zero. */
+ if (wm->id == WM9713_ID2 &&
+ (!wm->input_dev->users || !suspend_mode)) {
+ reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000;
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+ }
+
+ return 0;
+}
+
+static int wm97xx_resume(struct device *dev)
+{
+ struct wm97xx *wm = dev_get_drvdata(dev);
+
+ /* restore digitiser and gpios */
+ if (wm->id == WM9713_ID2) {
+ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
+ wm97xx_reg_write(wm, 0x5a, wm->misc);
+ if (wm->input_dev->users) {
+ u16 reg;
+ reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
+ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+ }
+ }
+
+ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
+ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
+
+ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
+ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
+ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
+ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
+ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
+ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
+
+ if (wm->input_dev->users && !wm->pen_irq) {
+ wm->ts_reader_interval = wm->ts_reader_min_interval;
+ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+ wm->ts_reader_interval);
+ }
+
+ return 0;
+}
+
+#else
+#define wm97xx_suspend NULL
+#define wm97xx_resume NULL
+#endif
+
+/*
+ * Machine specific operations
+ */
+int wm97xx_register_mach_ops(struct wm97xx *wm,
+ struct wm97xx_mach_ops *mach_ops)
+{
+ mutex_lock(&wm->codec_mutex);
+ if (wm->mach_ops) {
+ mutex_unlock(&wm->codec_mutex);
+ return -EINVAL;
+ }
+ wm->mach_ops = mach_ops;
+ mutex_unlock(&wm->codec_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
+
+void wm97xx_unregister_mach_ops(struct wm97xx *wm)
+{
+ mutex_lock(&wm->codec_mutex);
+ wm->mach_ops = NULL;
+ mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
+
+static struct device_driver wm97xx_driver = {
+ .name = "wm97xx-ts",
+ .bus = &ac97_bus_type,
+ .owner = THIS_MODULE,
+ .probe = wm97xx_probe,
+ .remove = wm97xx_remove,
+ .suspend = wm97xx_suspend,
+ .resume = wm97xx_resume,
+};
+
+static int __init wm97xx_init(void)
+{
+ return driver_register(&wm97xx_driver);
+}
+
+static void __exit wm97xx_exit(void)
+{
+ driver_unregister(&wm97xx_driver);
+}
+
+module_init(wm97xx_init);
+module_exit(wm97xx_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/zet6221_ts/Kconfig b/drivers/input/touchscreen/zet6221_ts/Kconfig
new file mode 100755
index 00000000..3bf6dcc3
--- /dev/null
+++ b/drivers/input/touchscreen/zet6221_ts/Kconfig
@@ -0,0 +1,16 @@
+#
+# ZET6221 capacity touch screen driver configuration
+#
+config TOUCHSCREEN_ZET6221
+ tristate "ZEITEC ZET6221 I2C Capacitive Touchscreen Input Driver Support"
+ depends on ARCH_WMT
+ default y
+ help
+ Say Y here if you have an WMT based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s_wmt_ts_zet6221
+
diff --git a/drivers/input/touchscreen/zet6221_ts/Makefile b/drivers/input/touchscreen/zet6221_ts/Makefile
new file mode 100755
index 00000000..102fb212
--- /dev/null
+++ b/drivers/input/touchscreen/zet6221_ts/Makefile
@@ -0,0 +1,32 @@
+KERNELDIR=../../../../
+CROSS = arm_1103_le-
+CC= $(CROSS)gcc
+LD= $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEBUG = n
+
+# Add your debugging flag (or not) to EXTRA_CFLAGS
+ifeq ($(DEBUG),y)
+# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+
+else
+ DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+
+
+MY_MODULE_NAME=s_wmt_ts_zet6221
+
+obj-m := $(MY_MODULE_NAME).o
+$(MY_MODULE_NAME)-objs := zet6221_i2c.o wmt_ts.o zet6221_downloader.o
+
+default:
+ $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+ $(STRIP) --strip-debug $(MY_MODULE_NAME).ko
+ rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions *.order *.symvers modules.builtin
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers modules.builtin \ No newline at end of file
diff --git a/drivers/input/touchscreen/zet6221_ts/wmt_ts.c b/drivers/input/touchscreen/zet6221_ts/wmt_ts.c
new file mode 100755
index 00000000..bfc65e7f
--- /dev/null
+++ b/drivers/input/touchscreen/zet6221_ts/wmt_ts.c
@@ -0,0 +1,833 @@
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+//#include <asm/semaphore.h>
+#include <linux/proc_fs.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/firmware.h>
+
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+
+#include "wmt_ts.h"
+#include "zet6221_ts.h"
+
+/////////////////////////////////////////////////////////////////
+
+// commands for ui
+#define TS_IOC_MAGIC 't'
+
+#define TS_IOCTL_CAL_START _IO(TS_IOC_MAGIC, 1)
+#define TS_IOCTL_CAL_DONE _IOW(TS_IOC_MAGIC, 2, int*)
+#define TS_IOCTL_GET_RAWDATA _IOR(TS_IOC_MAGIC, 3, int*)
+#define TS_IOCTL_CAL_QUIT _IOW(TS_IOC_MAGIC, 4, int*)
+#define TS_IOCTL_AUTO_CALIBRATION _IOW(TS_IOC_MAGIC, 5, int*)
+#define TS_IOC_MAXNR 5
+
+#define TP_INFOR_ARRAY_SIZE (sizeof(l_tpinfor)/sizeof(l_tpinfor[1]))
+//
+#define TS_MAJOR 11
+#define TS_DRIVER_NAME "wmtts_touch"
+#define TS_NAME "wmtts"
+#define WMTTS_PROC_NAME "wmtts_config"
+
+#define EXT_GPIO0 0
+#define EXT_GPIO1 1
+#define EXT_GPIO2 2
+#define EXT_GPIO3 3
+#define EXT_GPIO4 4
+#define EXT_GPIO5 5
+#define EXT_GPIO6 6
+#define EXT_GPIO7 7
+
+
+
+typedef struct {
+ int a1;
+ int b1;
+ int c1;
+ int a2;
+ int b2;
+ int c2;
+ int delta;
+}CALIBRATION_PARAMETER, *PCALIBRATION_PARAMETER;
+
+
+static int irq_gpio;
+static int rst_gpio;
+static int panelres_x;
+static int panelres_y;
+static int s_download_option;
+static int s_high_Impendence_mode;
+static int lcd_exchg = 0;
+
+
+static DECLARE_WAIT_QUEUE_HEAD(queue);
+static DECLARE_WAIT_QUEUE_HEAD(ts_penup_wait_queue);
+
+extern struct wmtts_device zet6221_tsdev;
+static struct wmtts_device* l_tsdev = &zet6221_tsdev;
+struct proc_dir_entry* l_tsproc = NULL;
+static struct i2c_client *l_client=NULL;
+static int l_penup = 1; // 1-pen up,0-pen down
+int earlysus_en = 0;
+
+struct tp_infor
+{
+ //enum tp_type type;
+ char name[64];
+ //unsigned int i2caddr;
+ unsigned int xaxis; //0: x,1: x swap with y
+ unsigned int xdir; // 1: positive,-1: revert
+ unsigned int ydir; // 1: positive,-1: revert
+ unsigned int max_finger_num;
+};
+
+static int l_tpindex = -1;
+static struct tp_infor l_tpinfor[1];
+
+/////////////////////////////////////////////////////
+// function declare
+/////////////////////////////////////////////////////
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+extern int wmt_setsyspara(char *varname, unsigned char *varval);
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data );
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+///////////////////////////////////////////////////////////////////////
+
+void wmt_ts_get_firmwname(char* firmname)
+{
+ int offset = 0;
+ offset = strlen(l_tsdev->ts_id);
+ switch(ic_model){
+ case ZET6223:
+ l_tpinfor[l_tpindex].name[offset] = '2';
+ l_tpinfor[l_tpindex].name[offset+1] = '3';
+ break;
+ case ZET6231:
+ l_tpinfor[l_tpindex].name[offset] = '3';
+ l_tpinfor[l_tpindex].name[offset+1] = '1';
+ break;
+ case ZET6251:
+ l_tpinfor[l_tpindex].name[offset] = '5';
+ l_tpinfor[l_tpindex].name[offset+1] = '1';
+ break;
+ case ZET6221:
+ default:
+ l_tpinfor[l_tpindex].name[offset] = '2';
+ l_tpinfor[l_tpindex].name[offset+1] = '1';
+ break;
+ }
+ sprintf(firmname,"%s_fw.bin",l_tpinfor[l_tpindex].name);
+}
+
+unsigned int wmt_ts_get_xaxis(void)
+{
+ return l_tpinfor[l_tpindex].xaxis;
+}
+
+unsigned int wmt_ts_get_xdir(void)
+{
+ return l_tpinfor[l_tpindex].xdir;
+}
+
+unsigned int wmt_ts_get_ydir(void)
+{
+ return l_tpinfor[l_tpindex].ydir;
+}
+
+unsigned int wmt_ts_get_maxfingernum(void)
+{
+ return l_tpinfor[l_tpindex].max_finger_num;
+}
+
+
+int wmt_ts_load_firmware(char* firmwarename, unsigned char** firmdata, int* fwlen)
+{
+ const struct firmware *fw_entry;
+ if(request_firmware(&fw_entry, firmwarename, &l_client->dev)!=0) {
+ printk(KERN_ERR "cat't request firmware\n");
+ return -1;
+ }
+ if (fw_entry->size <= 0) {
+ printk(KERN_ERR "load firmware error\n");
+ release_firmware(fw_entry);
+ return -1;
+ }
+
+ //*firmdata = kzalloc(fw_entry->size + 1, GFP_KERNEL);
+ memcpy(*firmdata, fw_entry->data, fw_entry->size);
+ *fwlen = fw_entry->size;
+ release_firmware(fw_entry);
+
+ return 0;
+}
+
+
+
+ int wmt_ts_get_gpionum(void)
+{
+ return irq_gpio;
+}
+
+int wmt_ts_get_resetgpnum(void)
+{
+ return rst_gpio;
+}
+
+int wmt_ts_get_lcdexchg(void)
+{
+ return lcd_exchg;
+}
+
+int wmt_ts_get_resolvX(void)
+{
+ return panelres_x;
+}
+
+int wmt_ts_get_resolvY(void)
+{
+ return panelres_y;
+}
+
+//up:1-pen up,0-pen down
+void wmt_ts_set_penup(int up)
+{
+ l_penup = up;
+}
+
+//
+int wmt_ts_wait_penup(void)
+{
+ int ret = wait_event_interruptible(
+ ts_penup_wait_queue,
+ (1==l_penup));
+ return ret;
+}
+
+// return:1-pen up,0-pen dwon
+int wmt_ts_ispenup(void)
+{
+ return l_penup;
+}
+
+
+void wmt_ts_wakeup_penup(void)
+{
+ wake_up(&ts_penup_wait_queue);
+}
+
+int wmt_is_tsirq_enable(void)
+{
+ int val = 0;
+ int num = irq_gpio;
+
+ if(num > 11)
+ return 0;
+
+ if(num<4)
+ val = REG32_VAL(__GPIO_BASE+0x0300) & (1<<(num*8+7));
+ else if(num >= 4 && num < 8)
+ val = REG32_VAL(__GPIO_BASE+0x0304) & (1<<((num-4)*8+7));
+ else
+ val = REG32_VAL(__GPIO_BASE+0x0308) & (1<<((num-8)*8+7));
+
+ return val?1:0;
+
+}
+
+int wmt_is_tsint(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return 0;
+ }
+ return (REG32_VAL(__GPIO_BASE+0x0360) & (1<<num)) ? 1: 0;
+}
+
+void wmt_clr_int(void)
+{
+ int num = irq_gpio;
+
+ if (num > 11)
+ {
+ return;
+ }
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num;
+}
+
+void wmt_tsreset_init(void)
+{
+ int num = rst_gpio;
+
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num);//&= ~(1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<num); // out low
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<num); //output enable
+ msleep(10);
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<num); // out high
+}
+
+// enable:0-disable,1-enable
+void wmt_enable_rst_pull(int enable)
+{
+ if (enable)
+ {
+ REG32_VAL(__GPIO_BASE+0x0480) |= (1<<rst_gpio); //enable pull up/down
+ } else {
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<rst_gpio); //disable pull up/down
+ }
+}
+
+// up:0-pull down,1-pull up
+void wmt_set_rst_pull(int up)
+{
+ if (up)
+ {
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<rst_gpio); //pull up
+ } else {
+ REG32_VAL(__GPIO_BASE+0x04c0) &= ~(1<<rst_gpio); //pull down
+ }
+}
+
+// high:0-low level,1-high level
+void wmt_rst_output(int high)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ if (high)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<rst_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<rst_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<rst_gpio); //set output
+}
+
+void wmt_rst_input(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<rst_gpio); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<rst_gpio); //set input
+}
+
+void wmt_set_intasgp(void)
+{
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<irq_gpio); //enable gpio
+}
+
+// val:1--high,0-low
+void wmt_intgp_out(int val)
+{
+ if (val)
+ {
+ REG32_VAL(__GPIO_BASE+0x00C0) |= (1<<irq_gpio); // high
+ } else {
+ REG32_VAL(__GPIO_BASE+0x00C0) &= ~(1<<irq_gpio); // low
+ }
+ REG32_VAL(__GPIO_BASE+0x0080) |= (1<<irq_gpio); //set output
+}
+
+void wmt_ts_set_irqinput(void)
+{
+ int num = irq_gpio;
+
+ REG32_VAL(__GPIO_BASE+0x0040) |= (1<<num); //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+}
+
+unsigned int wmt_ts_irqinval(void)
+{
+ return REG32_VAL(__GPIO_BASE+0x0000)&(1<<irq_gpio);
+}
+
+int wmt_set_gpirq(int type)
+{
+ int shift;
+ int offset;
+ unsigned long reg;
+ int num = irq_gpio;
+
+ if(num >11)
+ return -1;
+ //if (num > 9)
+ //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; // gpio10,11 as gpio
+ REG32_VAL(__GPIO_BASE+0x0040) &= ~(1<<num);//|=(1<<num);// //enable gpio
+ REG32_VAL(__GPIO_BASE+0x0080) &= ~(1<<num); //set input
+ REG32_VAL(__GPIO_BASE+0x04c0) |= (1<<num); //pull down
+ REG32_VAL(__GPIO_BASE+0x0480) &= ~(1<<num); //enable pull up/down
+
+ //set gpio irq triger type
+ if(num < 4){//[0,3]
+ shift = num;
+ offset = 0x0300;
+ }else if(num >= 4 && num < 8){//[4,7]
+ shift = num-4;
+ offset = 0x0304;
+ }else{// [8,11]
+ shift = num-8;
+ offset = 0x0308;
+ }
+
+ reg = REG32_VAL(__GPIO_BASE + offset);
+
+ switch(type){
+ case IRQ_TYPE_LEVEL_LOW:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~(1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ reg &= ~(1<<(shift*8+2));
+ reg |= (1<<(shift*8+1));
+ reg |= (1<<(shift*8));
+ break;
+ default://both edge
+ reg |= (1<<(shift*8+2));
+ reg &= ~(1<<(shift*8+1));
+ reg &= ~(1<<(shift*8));
+ break;
+
+ }
+ //reg |= 1<<(shift*8+7);//enable interrupt
+ reg &= ~(1<<(shift*8+7)); //disable int
+
+ REG32_VAL(__GPIO_BASE + offset) = reg;
+ REG32_VAL(__GPIO_BASE+0x0360) = 1<<num; //clear interrupt status
+ msleep(5);
+ return 0;
+}
+
+int wmt_enable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) |= 1<<(num*8+7); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) |= 1<<((num-4)*8+7); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) |= 1<<((num-8)*8+7); //enable interrupt
+
+ return 0;
+}
+
+int wmt_disable_gpirq(void)
+{
+ int num = irq_gpio;
+
+ if(num > 11)
+ return -1;
+
+ if(num<4)
+ REG32_VAL(__GPIO_BASE+0x0300) &= ~(1<<(num*8+7)); //enable interrupt
+ else if(num >= 4 && num < 8)
+ REG32_VAL(__GPIO_BASE+0x0304) &= ~(1<<((num-4)*8+7)); //enable interrupt
+ else
+ REG32_VAL(__GPIO_BASE+0x0308) &= ~(1<<((num-8)*8+7)); //enable interrupt
+
+ return 0;
+}
+
+
+int wmt_get_tsirqnum(void)
+{
+ return IRQ_GPIO;
+}
+
+static void wmt_ts_platform_release(struct device *device)
+{
+ return;
+}
+
+static struct platform_device wmt_ts_plt_device = {
+ .name = TS_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = wmt_ts_platform_release,
+ },
+// .num_resources = ARRAY_SIZE(wm9715_ts_resources),
+// .resource = wm9715_ts_resources,
+};
+
+static int wmt_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dbg("ts suspend....\n");
+ return l_tsdev->suspend(pdev, state);
+}
+static int wmt_ts_resume(struct platform_device *pdev)
+{
+ dbg("ts resume....\n");
+ return l_tsdev->resume(pdev);
+}
+
+static int wmt_ts_probe(struct platform_device *pdev)
+{
+ l_tsproc= create_proc_entry(WMTTS_PROC_NAME, 0666, NULL/*&proc_root*/);
+ if (l_tsproc != NULL)
+ {
+ l_tsproc->read_proc = ts_readproc;
+ l_tsproc->write_proc = ts_writeproc;
+ }
+
+ if (l_tsdev->probe != NULL)
+ return l_tsdev->probe(pdev);
+ else
+ return 0;
+}
+
+static int wmt_ts_remove(struct platform_device *pdev)
+{
+ if (l_tsproc != NULL)
+ {
+ remove_proc_entry(WMTTS_PROC_NAME, NULL);
+ l_tsproc = NULL;
+ }
+
+ if (l_tsdev->remove != NULL)
+ return l_tsdev->remove(pdev);
+ else
+ return 0;
+}
+
+static struct platform_driver wmt_ts_plt_driver = {
+ .driver = {
+ .name = TS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = wmt_ts_probe,
+ .remove = wmt_ts_remove,
+ .suspend = wmt_ts_suspend,
+ .resume = wmt_ts_resume,
+};
+
+static int ts_writeproc( struct file *file,
+ const char *buffer,
+ unsigned long count,
+ void *data )
+{
+ int calibrate = 0;
+ int val = 0;
+
+ if (sscanf(buffer, "calibrate=%d\n", &calibrate))
+ {
+ if (1 == calibrate)
+ {
+ if((l_tsdev->capacitance_calibrate != NULL) &&
+ (0 == l_tsdev->capacitance_calibrate()))
+ {
+ printk(KERN_ALERT "%s calibration successfully!\n", l_tsdev->ts_id);
+ } else {
+ printk(KERN_ALERT "%s calibration failed!\n", l_tsdev->ts_id);
+ }
+ }
+ } else if (sscanf(buffer, "reset=%d\n", &val))
+ {
+
+ }
+ return count;
+}
+
+static int ts_readproc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len = sprintf(page,
+ "echo calibrate=1 > /proc/wmtts_config to calibrate ts.\n");
+ return len;
+}
+
+
+int is_high_impendence_mode(void)
+{
+ return s_high_Impendence_mode;
+}
+
+int get_download_option(void)
+{
+ return s_download_option;
+}
+
+
+static int wmt_check_touch_env(void)
+{
+ int ret = 0;
+ int len = 96, i = 0;
+ char retval[200] = {0},*p=NULL,*s=NULL;
+ int Enable=0;
+ int val,val1;
+
+ // Get u-boot parameter
+ ret = wmt_getsyspara("wmt.io.touch", retval, &len);
+ if(ret){
+ errlog("Read wmt.io.touch Failed.\n");
+ return -EIO;
+ }
+ memset(l_tpinfor,0,sizeof(l_tpinfor[0]));
+
+ p = retval;
+ sscanf(p,"%d:", &Enable);
+ p = strchr(p,':');p++;
+ s = strchr(p,':');
+ strncpy(l_tpinfor[0].name,p, (s-p));
+ p = s+1;
+ dbg("ts_name=%s\n", l_tpinfor[0].name);
+
+ ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &irq_gpio,&panelres_x,&panelres_y,&rst_gpio,
+ &(l_tpinfor[0].xaxis),&(l_tpinfor[0].xdir),&(l_tpinfor[0].ydir),
+ &(l_tpinfor[0].max_finger_num),&s_high_Impendence_mode,&s_download_option);
+
+ if (ret < 8)
+ {
+ dbg("Wrong format ts u-boot param(%d)!\nwmt.io.touch=%s\n",ret,retval);
+ return -ENODEV;
+ }
+
+ //check touch enable
+ if(Enable == 0){
+ errlog("Touch Screen Is Disabled.\n");
+ return -ENODEV;
+ }
+
+
+ /*p = strchr(retval,':');
+ p++;
+ if(strncmp(p, l_tsdev->ts_id,strlen(l_tsdev->ts_id))){//check touch ID
+ //errlog(" %s!=====\n", l_tsdev->ts_id);
+ return -ENODEV;
+ }*/
+
+ //sscanf(p,"%s:", );
+ if (strstr(l_tpinfor[0].name, l_tsdev->ts_id) == NULL)
+ {
+ errlog("Can't find %s%s!\n", l_tsdev->ts_id,"xx");
+ return -ENODEV;
+ }
+ l_tpindex = 0;
+
+/*
+ p = strchr(p,':');
+ p++;
+ sscanf(p,"%d:%d:%d:%d",&irq_gpio,&panelres_x,&panelres_y,&rst_gpio);
+
+ */
+ klog("p.x = %d, p.y = %d, gpio=%d, resetgpio=%d,xaxis=%d,xdir=%d,ydri=%d,maxfingernum=%d,high_Impendence_mode=%d,s_download_option=%d\n",
+ panelres_x, panelres_y, irq_gpio, rst_gpio,
+ l_tpinfor[0].xaxis,l_tpinfor[0].xdir,l_tpinfor[0].ydir,
+ l_tpinfor[0].max_finger_num,s_high_Impendence_mode,s_download_option);
+
+ // parse touch key param
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.tskeyindex", retval, &len);
+ if(ret){
+ dbg("no touch key!\n");
+ //return -EIO;
+ } else {
+ p = retval;
+ // the number of touch key
+ sscanf(retval,"%d:", &val);
+ dbg("tskey num:%d\n",val);
+ p = strchr(p,':');
+ p++;
+ // touch key range
+ for (i=0;i<val;i++)
+ {
+ sscanf(p,"%d:",&val1);
+ p = strchr(p,':');
+ p++;
+ zet6221_set_tskey(i, val1);
+ dbg("key%d:(%d)\n",i,val1);
+ };
+ }
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.touch.earlysus", retval, &len);
+ if(!ret) {
+ p = retval;
+ sscanf(p, "%d", &earlysus_en);
+ }
+
+
+ memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.display.fb0", retval, &len);
+ if (!ret) {
+ int tmp[6];
+ p = retval;
+ sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
+ if (tmp[4] > tmp[5])
+ lcd_exchg = 1;
+ }
+
+/* memset(retval,0,sizeof(retval));
+ ret = wmt_getsyspara("wmt.io.ts.2dcal", retval, &len);
+ if(ret){
+ errlog("Read env wmt.io.ts.2dcal Failed.\n ");
+ //return -EIO;
+ }
+ i = 0;
+ while(i < sizeof(retval)){
+ if(retval[i]==' ' || retval[i]==',' || retval[i]==':')
+ retval[i] = '\0';
+ i++;
+ }
+
+ i = 0;
+ p = retval;
+ while(i<7 && p < (retval + sizeof(retval))){
+ if(*p == '\0')
+ p++;
+ else{
+ sscanf(p,"%d",&nBuff[i]);
+ //printk("%d\n",nBuff[i]);
+ p=p+strlen(p);
+ i++;
+ }
+ }
+ //sscanf(retval,"%d %d %d %d %d %d %d %d",&nBuff[0],&nBuff[1],&nBuff[2],&nBuff[3],&nBuff[4],&nBuff[5],&nBuff[6]);
+ dbg("Tsc calibrate init data: [%d %d %d %d %d %d %d]\n",nBuff[0],nBuff[1],nBuff[2],nBuff[3],nBuff[4],nBuff[5],nBuff[6]);
+
+ g_CalcParam.a1 = nBuff[0];
+ g_CalcParam.b1 = nBuff[1];
+ g_CalcParam.c1 = nBuff[2];
+ g_CalcParam.a2 = nBuff[3];
+ g_CalcParam.b2 = nBuff[4];
+ g_CalcParam.c2 = nBuff[5];
+ g_CalcParam.delta = nBuff[6];
+
+ if(g_CalcParam.delta == 0)
+ g_CalcParam.delta =1;//avoid divide by zero
+*/
+ return 0;
+}
+
+struct i2c_board_info ts_i2c_board_info = {
+ .type = WMT_TS_I2C_NAME,
+ .flags = 0x00,
+ .addr = WMT_TS_I2C_ADDR,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+static int ts_i2c_register_device (void)
+{
+ struct i2c_board_info *ts_i2c_bi;
+ struct i2c_adapter *adapter = NULL;
+ //struct i2c_client *client = NULL;
+ ts_i2c_bi = &ts_i2c_board_info;
+ adapter = i2c_get_adapter(1);/*in bus 1*/
+
+ if (NULL == adapter) {
+ printk("can not get i2c adapter, client address error\n");
+ return -1;
+ }
+ l_client = i2c_new_device(adapter, ts_i2c_bi);
+ if (l_client == NULL) {
+ printk("allocate i2c client failed\n");
+ return -1;
+ }
+ i2c_put_adapter(adapter);
+ return 0;
+}
+
+static void ts_i2c_unregister_device(void)
+{
+ if (l_client != NULL)
+ {
+ i2c_unregister_device(l_client);
+ l_client = NULL;
+ }
+}
+
+struct i2c_client* ts_get_i2c_client(void)
+{
+ return l_client;
+}
+
+static int __init wmt_ts_init(void)
+{
+ int ret = 0;
+
+ if(wmt_check_touch_env())
+ return -ENODEV;
+
+ //ts_i2c_board_info.addr = l_tpinfor[l_tpindex].i2caddr;
+ if (ts_i2c_register_device()<0)
+ {
+ dbg("Error to run ts_i2c_register_device()!\n");
+ return -1;
+ }
+
+ if (l_tsdev->init() < 0){
+ dbg("Errors to init %s ts IC!!!\n", l_tsdev->ts_id);
+ ret = -1;
+ goto err_init;
+ }
+
+ // register device and driver of platform
+ ret = platform_device_register(&wmt_ts_plt_device);
+ if(ret){
+ errlog("wmt ts plat device register failed!\n");
+ return ret;
+ }
+ ret = platform_driver_register(&wmt_ts_plt_driver);
+ if(ret){
+ errlog("can not register platform_driver_register\n");
+ platform_device_unregister(&wmt_ts_plt_device);
+ return ret;
+ }
+
+ klog("%s driver init ok!\n",l_tsdev->ts_id);
+ return 0;
+err_init:
+ ts_i2c_unregister_device();
+ return ret;
+}
+
+static void __exit wmt_ts_exit(void)
+{
+ dbg("%s\n",__FUNCTION__);
+
+ l_tsdev->exit();
+ platform_driver_unregister(&wmt_ts_plt_driver);
+ platform_device_unregister(&wmt_ts_plt_device);
+ ts_i2c_unregister_device();
+}
+
+
+module_init(wmt_ts_init);
+module_exit(wmt_ts_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/zet6221_ts/wmt_ts.h b/drivers/input/touchscreen/zet6221_ts/wmt_ts.h
new file mode 100755
index 00000000..cab70586
--- /dev/null
+++ b/drivers/input/touchscreen/zet6221_ts/wmt_ts.h
@@ -0,0 +1,149 @@
+
+#ifndef WMT_TSH_201010191758
+#define WMT_TSH_201010191758
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <linux/i2c.h>
+
+
+//#define DEBUG_WMT_TS
+#ifdef DEBUG_WMT_TS
+#undef dbg
+#define dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args)
+
+//#define dbg(fmt, args...) if (kpadall_isrundbg()) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args)
+
+#else
+#define dbg(fmt, args...)
+#endif
+
+#undef errlog
+#undef klog
+#define errlog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+#define klog(fmt, args...) printk("[%s]: " fmt, __FUNCTION__, ## args)
+
+#define WMT_TS_I2C_NAME "zet6221-ts"
+#define WMT_TS_I2C_ADDR 0x76
+
+
+#ifndef dim
+#define dim(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
+extern int earlysus_en;
+
+//////////////////////////////data type///////////////////////////
+typedef struct {
+ short pressure;
+ short x;
+ short y;
+ //short millisecs;
+} TS_EVENT;
+
+struct wmtts_device
+{
+ //data
+ char* driver_name;
+ char* ts_id;
+ //function
+ int (*init)(void);
+ int (*probe)(struct platform_device *platdev);
+ int (*remove)(struct platform_device *pdev);
+ void (*exit)(void);
+ int (*suspend)(struct platform_device *pdev, pm_message_t state);
+ int (*resume)(struct platform_device *pdev);
+ int (*capacitance_calibrate)(void);
+ int (*wait_penup)(struct wmtts_device*tsdev); // waiting untill penup
+ int penup; // 0--pendown;1--penup
+
+};
+
+enum {
+ ZET6221 = 0,
+ ZET6231,
+ ZET6223,
+ ZET6251,
+};
+extern u8 ic_model;
+extern unsigned char* flash_buffer;
+extern int l_fwlen;
+
+
+//////////////////////////function interface/////////////////////////
+extern int wmt_ts_get_gpionum(void);
+extern int wmt_ts_iscalibrating(void);
+extern int wmt_ts_get_resolvX(void);
+extern int wmt_ts_get_resolvY(void);
+extern int wmt_set_gpirq(int type);
+extern int wmt_get_tsirqnum(void);
+extern int wmt_disable_gpirq(void);
+extern int wmt_enable_gpirq(void);
+extern int wmt_is_tsirq_enable(void);
+extern int wmt_is_tsint(void);
+extern void wmt_clr_int(void);
+extern void wmt_tsreset_init(void);
+extern int wmt_ts_get_resetgpnum(void);
+extern int wmt_ts_get_lcdexchg(void);
+extern void wmt_enable_rst_pull(int enable);
+extern void wmt_set_rst_pull(int up);
+extern void wmt_rst_output(int high);
+extern void wmt_rst_input(void);
+extern void wmt_set_intasgp(void);
+extern void wmt_intgp_out(int val);
+extern void wmt_ts_set_irqinput(void);
+extern unsigned int wmt_ts_irqinval(void);
+extern void wmt_ts_set_penup(int up);
+extern int wmt_ts_wait_penup(void);
+extern void wmt_ts_wakeup_penup(void);
+extern struct i2c_client* ts_get_i2c_client(void);
+extern int wmt_ts_ispenup(void);
+extern unsigned int wmt_ts_get_maxfingernum(void);
+extern unsigned int wmt_ts_get_ictype(void);
+extern unsigned int wmt_ts_get_xaxis(void);
+extern unsigned int wmt_ts_get_xdir(void);
+extern unsigned int wmt_ts_get_ydir(void);
+// short
+extern unsigned int wmt_ts_get_touchheight(void);
+// long
+extern unsigned int wmt_ts_get_touchwidth(void);
+extern void wmt_ts_get_firmwname(char* firmname);
+extern int wmt_ts_load_firmware(char* firmwarename, unsigned char** firmdata, int* fwlen);
+
+
+
+
+extern void TouchPanelCalibrateAPoint(
+ int UncalX, //@PARM The uncalibrated X coordinate
+ int UncalY, //@PARM The uncalibrated Y coordinate
+ int *pCalX, //@PARM The calibrated X coordinate
+ int *pCalY //@PARM The calibrated Y coordinate
+ );
+
+//filepath:the path of firmware file;
+//firmdata:store the data from firmware file;
+//maxlen: the max len of firmdata;
+//return:the length of firmware data,negative-parsing error.
+//extern
+u8 zet6221_ts_sfr(struct i2c_client *client);
+int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+
+#define HIGH_IMPENDENCE_MODE 0
+#define NOT_HIGH_IMPENDENCE_MODE 1
+
+extern int is_high_impendence_mode(void);
+
+
+#define FORCE_DOWNLOAD 1
+#define FORCE_CANCEL_DOWNLOAD 2
+extern int get_download_option(void);
+
+
+
+#endif
+
+
+
diff --git a/drivers/input/touchscreen/zet6221_ts/zet6221_downloader.c b/drivers/input/touchscreen/zet6221_ts/zet6221_downloader.c
new file mode 100755
index 00000000..ac51aa21
--- /dev/null
+++ b/drivers/input/touchscreen/zet6221_ts/zet6221_downloader.c
@@ -0,0 +1,1209 @@
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+//#include "zet6221_fw.h"
+
+#include "wmt_ts.h"
+
+#define ZET6221_DOWNLOADER_NAME "zet6221_downloader"
+#define FEATURE_FW_CHECK_SUM 1
+//#define High_Impendence_Mode
+
+#define TS_INT_GPIO S3C64XX_GPN(9) /*s3c6410*/
+#define TS_RST_GPIO S3C64XX_GPN(10) /*s3c6410*/
+#define RSTPIN_ENABLE
+
+#define GPIO_LOW 0
+#define GPIO_HIGH 1
+
+//static u8 fw_version0;
+//static u8 fw_version1;
+
+//#define debug_mode 1
+//#define DPRINTK(fmt,args...) do { if (debug_mode) printk(KERN_EMERG "[%s][%d] "fmt"\n", __FUNCTION__, __LINE__, ##args);} while(0)
+
+static unsigned char zeitec_zet6221_page[131] __initdata;
+static unsigned char zeitec_zet6221_page_in[131] __initdata;
+unsigned char* flash_buffer = NULL;
+int l_fwlen = 0;
+
+//static u16 fb[8] = {0x3EEA,0x3EED,0x3EF0,0x3EF3,0x3EF6,0x3EF9,0x3EFC,0x3EFF};
+static u16 fb[8] = {0x3DF1,0x3DF4,0x3DF7,0x3DFA,0x3EF6,0x3EF9,0x3EFC,0x3EFF};
+static u16 fb21[8] = {0x3DF1,0x3DF4,0x3DF7,0x3DFA,0x3EF6,0x3EF9,0x3EFC,0x3EFF};
+static u16 fb23[8] = {0x7BFC,0x7BFD,0x7BFE,0x7BFF,0x7C04,0x7C05,0x7C06,0x7C07};
+u8 ic_model = 0;
+
+extern int zet6221_i2c_write_tsdata(struct i2c_client *client, u8 *data, u8 length);
+extern int zet6221_i2c_read_tsdata(struct i2c_client *client, u8 *data, u8 length);
+extern u8 pc[];
+
+
+
+
+/************************load firmwre data from file************************/
+int zet6221_load_fw(void)
+{
+ char fwname[256] = {0};
+ int ret = -1;
+ wmt_ts_get_firmwname(fwname);
+ ret = wmt_ts_load_firmware(fwname, &flash_buffer, &l_fwlen);
+ if(!ret) {
+ printk("Success load fw_file: %s, length %d\n", fwname, l_fwlen);
+ printk("%x,%x,%x,%x\n", flash_buffer[0], flash_buffer[1], flash_buffer[2], flash_buffer[3]);
+ printk("%x,%x,%x,%x\n", flash_buffer[l_fwlen-4], flash_buffer[l_fwlen-3], flash_buffer[l_fwlen-2], flash_buffer[l_fwlen-1]);
+ }
+ return ret;
+}
+
+/***********************free firmware memory*******************************/
+int zet6221_free_fwmem(void)
+{
+ if (l_fwlen > 0 && flash_buffer != NULL )
+ {
+ kfree(flash_buffer);
+ flash_buffer = NULL;
+ l_fwlen = 0;
+ }
+ return 0;
+}
+//#define I2C_CTPM_ADDRESS (0x76)
+
+/***********************************************************************
+[function]:
+ callback: write data to ctpm by i2c interface,implemented by special user;
+[parameters]:
+ client[in] :i2c client structure;
+ bt_ctpm_addr[in] :the address of the ctpm;
+ pbt_buf[in] :data buffer;
+ dw_lenth[in] :the length of the data buffer;
+[return]:
+ 1 :success;
+ 0 :fail;
+************************************************************************/
+int i2c_write_interface(struct i2c_client *client, u8 bt_ctpm_addr, u8* pbt_buf, u16 dw_lenth)
+{
+ struct i2c_msg msg;
+ msg.addr = bt_ctpm_addr;
+ msg.flags = 0;
+ msg.len = dw_lenth;
+ msg.buf = pbt_buf;
+ return i2c_transfer(client->adapter,&msg, 1);
+}
+
+/***********************************************************************
+[function]:
+ callback: read data from ctpm by i2c interface,implemented by special user;
+[parameters]:
+ client[in] :i2c client structure;
+ bt_ctpm_addr[in] :the address of the ctpm;
+ pbt_buf[out] :data buffer;
+ dw_lenth[in] :the length of the data buffer;
+[return]:
+ 1 :success;
+ 0 :fail;
+************************************************************************/
+int i2c_read_interface(struct i2c_client *client, u8 bt_ctpm_addr, u8* pbt_buf, u16 dw_lenth)
+{
+ struct i2c_msg msg;
+ msg.addr = bt_ctpm_addr;
+ msg.flags = I2C_M_RD;
+ msg.len = dw_lenth;
+ msg.buf = pbt_buf;
+ return i2c_transfer(client->adapter,&msg, 1);
+}
+
+/***********************************************************************
+ [function]:
+ callback: check version;
+ [parameters]:
+ void
+
+ [return]:
+ 0: different 1: same;
+************************************************************************/
+u8 zet6221_ts_version(void)
+{
+ int i;
+
+ if(pc == NULL){
+ errlog(" pc is NULL\n");
+ return 0;
+ }
+ if( flash_buffer == NULL ){
+ errlog("flash_buffer \n");
+ return 0;
+ }
+
+#if 1
+ dbg("pc: ");
+ for(i=0;i<8;i++){
+ dbg("%02x ",pc[i]);
+ }
+ dbg("\n");
+
+ dbg("src: ");
+ for(i=0;i<8;i++){
+ dbg("%02x ", flash_buffer[fb[i]]);
+ }
+ dbg("\n");
+#endif
+
+ mdelay(20);
+
+ for(i=0;i<8;i++)
+ if(pc[i]!= flash_buffer[fb[i]])
+ return 0;
+ return 1;
+}
+
+
+/***********************************************************************
+ [function]:
+ callback: send password 1K (ZET6223)
+ [parameters]:
+ client[in]: struct i2c_client — represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_sndpwd_1k(struct i2c_client *client)
+{
+ u8 ts_sndpwd_cmd[3] = {0x20,0xB9,0xA3};
+
+ int ret;
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_sndpwd_cmd, 3);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_sndpwd_cmd, 3);
+#endif
+
+
+ return 1;
+}
+
+
+/***********************************************************************
+ [function]:
+ callback: send password;
+ [parameters]:
+ client[in]: struct i2c_client ???represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_sndpwd(struct i2c_client *client)
+{
+ u8 ts_sndpwd_cmd[3] = {0x20,0xC5,0x9D};
+
+ int ret;
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_sndpwd_cmd, 3);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_sndpwd_cmd, 3);
+#endif
+
+ return 1;
+}
+
+u8 zet622x_ts_option(struct i2c_client *client)
+{
+ u8 ts_cmd[1] = {0x27};
+ u8 ts_cmd_erase[1] = {0x28};
+ u8 ts_in_data[16] = {0};
+ u8 ts_out_data[18] = {0};
+ int ret;
+ u16 model;
+ int i;
+ u8 high_impendence_data = 0;
+ const u8 HIGH_IMPENDENCE_MODE_DATA = 0xf1;
+ const u8 NOT_HIGH_IMPENDENCE_MODE_DATA = 0xf2;
+
+
+ dbg("zet622x_ts_option++\n");
+
+ wmt_rst_output(0);
+ msleep(10);
+ //send password
+ zet6221_ts_sndpwd(client);
+ msleep(100);
+
+
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd, dim(ts_cmd));
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd, dim(ts_cmd));
+#endif
+ msleep(2);
+
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_read_interface(client, I2C_CTPM_ADDRESS, ts_in_data, dim(ts_in_data));
+#else
+ ret=zet6221_i2c_read_tsdata(client, ts_in_data, dim(ts_in_data));
+#endif
+ //msleep(2);
+
+ dbg("command %02x recv:\n",ts_cmd[0]);
+ for(i=0;i<16;i++)
+ {
+ dbg("%02x ",ts_in_data[i]);
+ }
+ dbg("\n");
+ // zet6231 recv: ff ff fc 30 ff 80 31 62 ff ff ff ff ff ff ff ff
+
+ model = 0x0;
+ model = ts_in_data[7];
+ model = (model << 8) | ts_in_data[6];
+
+ switch(model) {
+ case 0xFFFF:
+ ret = 1;
+ ic_model = ZET6221;
+ for(i=0;i<8;i++)
+ {
+ fb[i]=fb21[i];
+ }
+
+ if( is_high_impendence_mode() == HIGH_IMPENDENCE_MODE ){
+ high_impendence_data = HIGH_IMPENDENCE_MODE_DATA;
+ }else if( is_high_impendence_mode() == NOT_HIGH_IMPENDENCE_MODE ) {
+ high_impendence_data = NOT_HIGH_IMPENDENCE_MODE_DATA;
+ }
+
+ //#if defined(High_Impendence_Mode)
+ if(ts_in_data[2] != high_impendence_data)
+ {
+
+ if(zet6221_ts_sfr(client)==0)
+ {
+ return 0;
+ }
+
+ #if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd_erase, dim(ts_cmd_erase));
+ #else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd_erase, dim(ts_cmd_erase));
+ #endif
+
+ msleep(100);
+ dbg("erase ret=%d \n",ret);
+
+
+ for(i=2;i<18;i++)
+ {
+ ts_out_data[i]=ts_in_data[i-2];
+ }
+ ts_out_data[0] = 0x21;
+ ts_out_data[1] = 0xc5;
+ ts_out_data[4] = high_impendence_data;
+
+ #if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_out_data, 18);
+ #else
+ ret=zet6221_i2c_write_tsdata(client, ts_out_data, 18);
+ #endif
+
+ msleep(100);
+ dbg("write out data, ret=%d\n",ret);
+
+
+
+ #if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd, 1);
+ #else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd, 1);
+ #endif
+
+ msleep(2);
+
+ dbg("send %02x\n",ts_cmd[0]);
+
+
+ #if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_read_interface(client, I2C_CTPM_ADDRESS, ts_in_data, 16);
+ #else
+ ret=zet6221_i2c_read_tsdata(client, ts_in_data, 16);
+ #endif
+ //msleep(2);
+ dbg("command %02x recv:\n",ts_cmd[0]);
+ for(i=0;i<16;i++)
+ {
+ dbg("%02x ",ts_in_data[i]);
+ }
+ dbg("\n");
+
+ }
+
+ //#endif
+
+
+
+ break;
+ case 0x6223:
+ ret = 1;
+ ic_model = ZET6223;
+ for(i=0;i<8;i++)
+ {
+ fb[i]=fb23[i];
+ }
+ break;
+ case 0x6231:
+ ret = 1;
+ ic_model = ZET6231;
+ for(i=0;i<8;i++)
+ {
+ fb[i]=fb23[i];
+ }
+ break;
+ case 0x6251:
+ ic_model = ZET6251;
+ for(i=0;i<8;i++)
+ {
+ fb[i] = fb23[i];
+ }
+ break;
+ default:
+ errlog("Notice: can't detect the TP IC,use ZET6231 default\n");
+ ret = 1;
+ ic_model = ZET6231;
+ for(i=0;i<8;i++)
+ {
+ fb[i]=fb23[i];
+ }
+ break;
+
+ }
+
+ wmt_rst_output(1);
+ msleep(10);
+
+ dbg("zet622x_ts_option-- ret:%d\n",ret);
+ return ret;
+}
+/***********************************************************************
+ [function]:
+ callback: set/check sfr information;
+ [parameters]:
+ client[in]: struct i2c_client ???represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_sfr(struct i2c_client *client)
+{
+ u8 ts_cmd[1] = {0x2C};
+ u8 ts_in_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ u8 ts_cmd17[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ //u8 ts_sfr_data[16] = {0x18,0x76,0x27,0x27,0xFF,0x03,0x8E,0x14,0x00,0x38,0x82,0xEC,0x00,0x00,0x7d,0x03};
+ int ret;
+ int i;
+
+ dbg("zet6221_ts_sfr++");
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd, 1);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd, 1);
+#endif
+ msleep(10);
+ dbg("sfr cmd : 0x%02x \n",ts_cmd[0]);
+
+
+
+ dbg("sfr rcv : \n");
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_read_interface(client, I2C_CTPM_ADDRESS, ts_in_data, 16);
+#else
+ ret=zet6221_i2c_read_tsdata(client, ts_in_data, 16);
+#endif
+ msleep(10);
+
+ if(ts_in_data[14]!=0x3D && ts_in_data[14]!=0x7D)
+ {
+ return 0;
+ }
+
+ for(i=0;i<16;i++)
+ {
+ ts_cmd17[i+1]=ts_in_data[i];
+ dbg("[%d]%02x\n",i,ts_in_data[i]);
+ }
+
+ dbg("\n");
+
+ // need to check 0x3D to open write function
+ if(ts_in_data[14]!=0x3D)
+ {
+ ts_cmd17[15]=0x3D;
+
+ ts_cmd17[0]=0x2B;
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd17, 17);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd17, 17);
+#endif
+
+ if(ret<0)
+ {
+ errlog("enable sfr(0x3D) failed!\n");
+ return 0;
+ }
+
+ }
+ dbg("zet6221_ts_sfr--");
+ return 1;
+}
+
+/***********************************************************************
+ [function]:
+ callback: mass erase flash;
+ [parameters]:
+ client[in]: struct i2c_client ???represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_masserase(struct i2c_client *client)
+{
+ u8 ts_cmd[1] = {0x24};
+
+ int ret;
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd, 1);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd, 1);
+#endif
+
+ return 1;
+}
+
+/***********************************************************************
+ [function]:
+ callback: erase flash by page;
+ [parameters]:
+ client[in]: struct i2c_client ???represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_pageerase(struct i2c_client *client,int npage)
+{
+ u8 ts_cmd[3] = {0x23,0x00,0x00};
+ u8 len = 0;
+ int ret;
+
+ switch(ic_model)
+ {
+ case ZET6221:
+ ts_cmd[1]=npage;
+ len=2;
+ break;
+ case ZET6231:
+ case ZET6223:
+ case ZET6251:
+ ts_cmd[1]=npage & 0xff;
+ ts_cmd[2]=npage >> 8;
+ len=3;
+ break;
+ default:
+ ts_cmd[1]=npage & 0xff;
+ ts_cmd[2]=npage >> 8;
+ len=3;
+ break;
+ }
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd, len);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd, len);
+#endif
+ msleep(2);
+
+ return 1;
+}
+
+/***********************************************************************
+ [function]:
+ callback: reset mcu;
+ [parameters]:
+ client[in]: struct i2c_client ???represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_resetmcu(struct i2c_client *client)
+{
+ u8 ts_cmd[1] = {0x29};
+
+ int ret;
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd, 1);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd, 1);
+#endif
+
+ return 1;
+}
+
+
+#define CMD_PROG_CHECK_SUM (0x36)
+#define CMD_PROG_GET_CHECK_SUM (0x37)
+///***********************************************************************
+/// [function]: zet622x_cmd_read_check_sum
+/// [parameters]: client, page_id, buf
+/// [return]: int
+///************************************************************************
+int zet622x_cmd_read_check_sum(struct i2c_client *client, int page_id, u8 * buf)
+{
+ int ret;
+ int cmd_len = 3;
+
+ buf[0]= CMD_PROG_CHECK_SUM;
+ buf[1]= (u8)(page_id) & 0xff;
+ buf[2]= (u8)(page_id >> 8);
+ ret=zet6221_i2c_write_tsdata(client, buf, cmd_len);
+ if(ret<=0)
+ {
+ printk("[ZET]: Read check sum fail");
+ return ret;
+ }
+
+ buf[0]= CMD_PROG_GET_CHECK_SUM;
+ cmd_len = 1;
+ ret=zet6221_i2c_write_tsdata(client, buf, cmd_len);
+ if(ret<=0)
+ {
+ printk("[ZET]: Read check sum fail");
+ return ret;
+ }
+
+ cmd_len = 1;
+ ret = zet6221_i2c_read_tsdata(client, buf, cmd_len);
+ if(ret<=0)
+ {
+ printk("[ZET]: Read check sum fail");
+ return ret;
+ }
+ return 1;
+}
+
+
+/***********************************************************************
+ [function]:
+ callback: start HW function;
+ [parameters]:
+ client[in]: struct i2c_client ???represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_hwcmd(struct i2c_client *client)
+{
+ u8 ts_cmd[1] = {0xB9};
+
+ int ret;
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, ts_cmd, 1);
+#else
+ ret=zet6221_i2c_write_tsdata(client, ts_cmd, 1);
+#endif
+
+ return 1;
+}
+
+/***********************************************************************
+update FW
+************************************************************************/
+int __init zet6221_downloader( struct i2c_client *client )
+{
+ int BufLen=0;
+ int BufPage=0;
+ int BufIndex=0;
+ int ret;
+ int i;
+
+ int nowBufLen=0;
+ int nowBufPage=0;
+ int nowBufIndex=0;
+ int retryCount=0;
+ int retryTimes = 0;
+
+ int i2cLength=0;
+ int bufOffset=0;
+
+ dbg("zet6221_downloader++\n");
+
+begin_download:
+
+#if defined(RSTPIN_ENABLE)
+ //reset mcu
+ //gpio_direction_output(TS_RST_GPIO, GPIO_LOW);
+ wmt_rst_output(0);
+ msleep(5);
+#else
+ zet6221_ts_hwcmd(client);
+ msleep(200);
+#endif
+ //send password
+ //send password
+ ret = zet6221_ts_sndpwd(client);
+ dbg("zet6221_ts_sndpwd ret=%d\n",ret);
+ msleep(100);
+
+/*****compare version*******/
+
+ //0~3
+ memset(zeitec_zet6221_page_in,0x00,131);
+ switch(ic_model)
+ {
+ case ZET6221:
+ zeitec_zet6221_page_in[0]=0x25;
+ zeitec_zet6221_page_in[1]=(fb[0] >> 7);//(fb[0]/128);
+
+ i2cLength=2;
+ break;
+ case ZET6231:
+ case ZET6223:
+ case ZET6251:
+ default:
+ zeitec_zet6221_page_in[0]=0x25;
+ zeitec_zet6221_page_in[1]=(fb[0] >> 7) & 0xff; //(fb[0]/128);
+ zeitec_zet6221_page_in[2]=(fb[0] >> 7) >> 8; //(fb[0]/128);
+
+ i2cLength=3;
+ break;
+ }
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, i2cLength);
+#else
+ ret=zet6221_i2c_write_tsdata(client, zeitec_zet6221_page_in, i2cLength);
+ dbg("write_ret =%d, i2caddr=0x%x\n", ret, client->addr);
+#endif
+ msleep(2);
+
+ zeitec_zet6221_page_in[0]=0x0;
+ zeitec_zet6221_page_in[1]=0x0;
+ zeitec_zet6221_page_in[2]=0x0;
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_read_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, 128);
+#else
+ ret=zet6221_i2c_read_tsdata(client, zeitec_zet6221_page_in, 128);
+ dbg("read_ret =%d, i2caddr=0x%x\n", ret, client->addr);
+#endif
+
+ //printk("page=%d ",(fb[0] >> 7)); //(fb[0]/128));
+ for(i=0;i<4;i++)
+ {
+ pc[i]=zeitec_zet6221_page_in[(fb[i] & 0x7f)]; //[(fb[i]%128)];
+ dbg("offset[%d]=%d ",i,(fb[i] & 0x7f)); //(fb[i]%128));
+ }
+ dbg("\n");
+
+
+ // 4~7
+ memset(zeitec_zet6221_page_in,0x00,131);
+ switch(ic_model)
+ {
+ case ZET6221:
+ zeitec_zet6221_page_in[0]=0x25;
+ zeitec_zet6221_page_in[1]=(fb[4] >> 7);//(fb[4]/128);
+
+ i2cLength=2;
+ break;
+ case ZET6231:
+ case ZET6223:
+ case ZET6251:
+ zeitec_zet6221_page_in[0]=0x25;
+ zeitec_zet6221_page_in[1]=(fb[4] >> 7) & 0xff; //(fb[4]/128);
+ zeitec_zet6221_page_in[2]=(fb[4] >> 7) >> 8; //(fb[4]/128);
+
+ i2cLength=3;
+ break;
+ }
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, i2cLength);
+#else
+ ret=zet6221_i2c_write_tsdata(client, zeitec_zet6221_page_in, i2cLength);
+ dbg("write_ret =%d, i2caddr=0x%x\n", ret, client->addr);
+#endif
+
+ zeitec_zet6221_page_in[0]=0x0;
+ zeitec_zet6221_page_in[1]=0x0;
+ zeitec_zet6221_page_in[2]=0x0;
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_read_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, 128);
+#else
+ ret=zet6221_i2c_read_tsdata(client, zeitec_zet6221_page_in, 128);
+ dbg("read_ret =%d, i2caddr=0x%x\n", ret, client->addr);
+#endif
+
+ //printk("page=%d ",(fb[4] >> 7)); //(fb[4]/128));
+ for(i=4;i<8;i++)
+ {
+ pc[i]=zeitec_zet6221_page_in[(fb[i] & 0x7f)]; //[(fb[i]%128)];
+ dbg("offset[%d]=%d ",i,(fb[i] & 0x7f)); //(fb[i]%128));
+ }
+ dbg("\n");
+
+#if 1 // need to check
+ //page 127
+ memset(zeitec_zet6221_page_in,0x00,130);
+ zeitec_zet6221_page_in[0]=0x25;
+ zeitec_zet6221_page_in[1]=127;
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, 2);
+#else
+ ret=zet6221_i2c_write_tsdata(client, zeitec_zet6221_page_in, 2);
+#endif
+
+ zeitec_zet6221_page_in[0]=0x0;
+ zeitec_zet6221_page_in[1]=0x0;
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_read_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, 128);
+#else
+ ret=zet6221_i2c_read_tsdata(client, zeitec_zet6221_page_in, 128);
+#endif
+
+ for(i=0;i<128;i++)
+ {
+ // 0x3F80 = 16256 = 128x127, means skipped the first 127 page (0-126) ,use the page 127.
+ if(0x3F80+i < l_fwlen/*sizeof(flash_buffer)/sizeof(char)*/) //l_fwlen: the bytes of data read from firmware file
+ {
+ if(zeitec_zet6221_page_in[i]!=flash_buffer[0x3F80+i])
+ {
+ errlog("page 127 [%d] doesn't match! continue to download! retry times:%d\n",i,retryTimes);
+ if( retryTimes++ >= 20){ // retry 20 times ,quit
+ errlog("May be I2C comunication is error\n");
+ goto exit_download;
+ }
+ goto proc_sfr;
+ }
+ }
+ }
+
+#endif
+
+ if( get_download_option() == FORCE_DOWNLOAD ){
+ errlog("FORCE_DOWNLOAD\n");
+ goto proc_sfr;
+ }
+ if( get_download_option() == FORCE_CANCEL_DOWNLOAD ){
+ errlog("FORCE_CANCEL_DOWNLOAD\n");
+ goto exit_download;
+ }
+ if(zet6221_ts_version()!=0){
+ klog("tp version is the same,no need to download\n");
+ goto exit_download;
+ }
+
+
+
+/*****compare version*******/
+proc_sfr:
+ //sfr
+ if(zet6221_ts_sfr(client)==0)
+ {
+
+#if 1
+
+#if defined(RSTPIN_ENABLE)
+
+ //gpio_direction_output(TS_RST_GPIO, GPIO_HIGH);
+ wmt_rst_output(1);
+ msleep(20);
+
+ //gpio_direction_output(TS_RST_GPIO, GPIO_LOW);
+ wmt_rst_output(0);
+ msleep(20);
+
+ //gpio_direction_output(TS_RST_GPIO, GPIO_HIGH);
+ wmt_rst_output(1);
+#else
+ zet6221_ts_resetmcu(client);
+#endif
+ msleep(20);
+ errlog("zet6221_ts_sfr error, download again\n");
+ goto begin_download;
+
+#endif
+
+ }
+ msleep(20);
+
+ /// Fix the bug that page#504~#512 failed to write
+ if(ic_model == ZET6223)
+ {
+ zet6221_ts_sndpwd_1k(client);
+ }
+
+ //erase
+ if(BufLen==0)
+ {
+ //mass erase
+ dbg( "mass erase\n");
+ zet6221_ts_masserase(client);
+ msleep(200);
+
+ BufLen=l_fwlen;/*sizeof(flash_buffer)/sizeof(char)*/;
+ }else
+ {
+ zet6221_ts_pageerase(client,BufPage);
+ msleep(200);
+ }
+
+
+ while(BufLen>0)
+ {
+download_page:
+
+ memset(zeitec_zet6221_page,0x00,131);
+
+ klog( "Start: write page%d\n",BufPage);
+ nowBufIndex=BufIndex;
+ nowBufLen=BufLen;
+ nowBufPage=BufPage;
+
+ switch(ic_model)
+ {
+ case ZET6221:
+ bufOffset = 2;
+ i2cLength=130;
+
+ zeitec_zet6221_page[0]=0x22;
+ zeitec_zet6221_page[1]=BufPage;
+ break;
+ case ZET6231:
+ case ZET6223:
+ case ZET6251:
+ default:
+ bufOffset = 3;
+ i2cLength=131;
+
+ zeitec_zet6221_page[0]=0x22;
+ zeitec_zet6221_page[1]=BufPage & 0xff;
+ zeitec_zet6221_page[2]=BufPage >> 8;
+ break;
+ }
+ if(BufLen>128)
+ {
+ for(i=0;i<128;i++)
+ {
+ zeitec_zet6221_page[i+bufOffset]=flash_buffer[BufIndex];
+ BufIndex+=1;
+ }
+ zeitec_zet6221_page[0]=0x22;
+ zeitec_zet6221_page[1]=BufPage;
+ BufLen-=128;
+ }
+ else
+ {
+ for(i=0;i<BufLen;i++)
+ {
+ zeitec_zet6221_page[i+bufOffset]=flash_buffer[BufIndex];
+ BufIndex+=1;
+ }
+ zeitec_zet6221_page[0]=0x22;
+ zeitec_zet6221_page[1]=BufPage;
+ BufLen=0;
+ }
+
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page, i2cLength);
+#else
+ ret=zet6221_i2c_write_tsdata(client, zeitec_zet6221_page, i2cLength);
+#endif
+ //msleep(200);
+ msleep(2);
+
+#if 1
+
+ memset(zeitec_zet6221_page_in,0x00,131);
+ switch(ic_model)
+ {
+ case ZET6221:
+ zeitec_zet6221_page_in[0]=0x25;
+ zeitec_zet6221_page_in[1]=BufPage;
+
+ i2cLength=2;
+ break;
+ case ZET6231:
+ case ZET6223:
+ case ZET6251:
+ default:
+ zeitec_zet6221_page_in[0]=0x25;
+ zeitec_zet6221_page_in[1]=BufPage & 0xff;
+ zeitec_zet6221_page_in[2]=BufPage >> 8;
+
+ i2cLength=3;
+ break;
+ }
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_write_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, i2cLength);
+#else
+ ret=zet6221_i2c_write_tsdata(client, zeitec_zet6221_page_in, i2cLength);
+#endif
+ msleep(2);
+
+ zeitec_zet6221_page_in[0]=0x0;
+ zeitec_zet6221_page_in[1]=0x0;
+ zeitec_zet6221_page_in[2]=0x0;
+#if defined(I2C_CTPM_ADDRESS)
+ ret=i2c_read_interface(client, I2C_CTPM_ADDRESS, zeitec_zet6221_page_in, 128);
+#else
+ ret=zet6221_i2c_read_tsdata(client, zeitec_zet6221_page_in, 128);
+#endif
+
+ for(i=0;i<128;i++)
+ {
+ if(i < nowBufLen)
+ {
+ if(zeitec_zet6221_page[i+bufOffset]!=zeitec_zet6221_page_in[i])
+ {
+ BufIndex=nowBufIndex;
+ BufLen=nowBufLen;
+ BufPage=nowBufPage;
+
+ if(retryCount < 5)
+ {
+ retryCount++;
+ goto download_page;
+ }else
+ {
+ //BufIndex=0;
+ //BufLen=0;
+ //BufPage=0;
+ retryCount=0;
+
+#if defined(RSTPIN_ENABLE)
+ //gpio_direction_output(TS_RST_GPIO, GPIO_HIGH);
+ wmt_rst_output(1);
+ msleep(20);
+
+ //gpio_direction_output(TS_RST_GPIO, GPIO_LOW);
+ wmt_rst_output(0);
+ msleep(20);
+
+ //gpio_direction_output(TS_RST_GPIO, GPIO_HIGH);
+ wmt_rst_output(1);
+#else
+ zet6221_ts_resetmcu(client);
+#endif
+ msleep(20);
+ goto begin_download;
+ }
+
+ }
+ }
+ }
+
+#endif
+ retryCount=0;
+ BufPage+=1;
+ }
+
+exit_download:
+
+#if defined(RSTPIN_ENABLE)
+ //gpio_direction_output(TS_RST_GPIO, GPIO_HIGH);
+ wmt_rst_output(1);
+ msleep(100);
+#endif
+
+ zet6221_ts_resetmcu(client);
+ msleep(100);
+
+ dbg("zet6221_downloader--\n");
+ return 1;
+
+
+}
+
+int zet622x_resume_downloader(struct i2c_client *client)
+{
+ int ret = 0;
+
+ int BufLen=0;
+ int BufPage=0;
+ int BufIndex=0;
+ int bufOffset = 0;
+
+ int nowBufLen=0;
+ int nowBufPage=0;
+ int nowBufIndex=0;
+
+ int i2cLength = 0;
+
+ int i = 0;
+
+ u8 bPageBuf[256];
+
+#ifdef FEATURE_FW_CHECK_SUM
+ u8 get_check_sum = 0;
+ u8 check_sum = 0;
+ int retry_count = 0;
+ u8 tmp_data[16];
+#endif ///< for FEATURE_FW_CHECK_SUM
+
+
+ ///-------------------------------------------------------------///
+ /// 1. Set RST=LOW
+ ///-------------------------------------------------------------///
+ wmt_rst_output(0);
+ msleep(20);
+ //printk("RST = LOW\n");
+
+ ///-------------------------------------------------------------///
+ /// Send password
+ ///-------------------------------------------------------------///
+ ret = zet6221_ts_sndpwd(client);
+ if(ret<=0)
+ {
+ return ret;
+ }
+
+ //printk("AAA\n");
+ BufLen=l_fwlen;/*sizeof(flash_buffer)/sizeof(char)*/;
+ //printk("BBB%d\n",BufLen);
+
+ while(BufLen>0)
+ {
+ /// memset(zeitec_zet622x_page, 0x00, 131);
+ nowBufIndex=BufIndex;
+ nowBufLen=BufLen;
+ nowBufPage=BufPage;
+
+ switch(ic_model)
+ {
+ case ZET6251:
+ bufOffset = 3;
+ i2cLength=131;
+
+ bPageBuf[0]=0x22;
+ bPageBuf[1]=BufPage & 0xff;
+ bPageBuf[2]=BufPage >> 8;
+ break;
+ }
+
+ if(BufLen>128)
+ {
+ for(i=0;i<128;i++)
+ {
+ bPageBuf[i + bufOffset] = flash_buffer[BufIndex];
+ BufIndex += 1;
+ }
+
+ BufLen = BufLen - 128;
+ }
+ else
+ {
+ for(i=0;i<BufLen;i++)
+ {
+ bPageBuf[i+bufOffset]=flash_buffer[BufIndex];
+ BufIndex+=1;
+ }
+
+ BufLen=0;
+ }
+
+#ifdef FEATURE_FW_CHECK_SUM
+LABEL_RETRY_DOWNLOAD_PAGE:
+#endif ///< for FEATURE_FW_CHECK_SUM
+
+ ret=zet6221_i2c_write_tsdata(client, bPageBuf, i2cLength);
+
+ if(ic_model!= ZET6251)
+ {
+ msleep(50);
+ }
+
+#ifdef FEATURE_FW_CHECK_SUM
+ ///---------------------------------///
+ /// Get check sum
+ ///---------------------------------///
+ for(i=0;i<128;i++)
+ {
+ if(i == 0)
+ {
+ check_sum = bPageBuf[i + bufOffset];
+ }
+ else
+ {
+ check_sum = check_sum ^ bPageBuf[i + bufOffset];
+ }
+ }
+
+ ///---------------------------------///
+ /// Read check sum
+ ///---------------------------------///
+ memset(tmp_data, 0, 16);
+ ret = zet622x_cmd_read_check_sum(client, BufPage, &tmp_data[0]);
+ if(ret<=0)
+ {
+ return ret;
+ }
+ get_check_sum = tmp_data[0];
+
+ //printk("[ZET]: page=%3d ,Check sum : %x ,get check sum : %x\n", BufPage, check_sum, get_check_sum);
+ if(check_sum != get_check_sum)
+ {
+
+ if(retry_count < 5)
+ {
+ retry_count++;
+ goto LABEL_RETRY_DOWNLOAD_PAGE;
+ }
+ else
+ {
+ retry_count = 0;
+ wmt_rst_output(1);
+ msleep(20);
+ wmt_rst_output(0);
+ msleep(20);
+ wmt_rst_output(1);
+ msleep(20);
+ printk("[ZET] zet622x_resume_downloader fail\n");
+ return ret;
+ }
+
+ }
+ retry_count = 0;
+#endif ///< for FEATURE_FW_CHECK_SUM
+
+ BufPage+=1;
+ }
+
+ printk("[ZET] zet622x_resume_downloader OK\n");
+ //printk("RST = HIGH\n");
+
+ ///-------------------------------------------------------------///
+ /// reset_mcu command
+ ///-------------------------------------------------------------///
+ zet6221_ts_resetmcu(client);
+ msleep(10);
+
+ ///-------------------------------------------------------------///
+ /// SET RST=HIGH
+ ///-------------------------------------------------------------///
+ wmt_rst_output(1);
+ msleep(20);
+
+ ///-------------------------------------------------------------///
+ /// RST toggle
+ ///-------------------------------------------------------------///
+ wmt_rst_output(0);
+ msleep(2);
+
+ wmt_rst_output(1);
+ msleep(2);
+
+ return ret;
+}
+
diff --git a/drivers/input/touchscreen/zet6221_ts/zet6221_i2c.c b/drivers/input/touchscreen/zet6221_ts/zet6221_i2c.c
new file mode 100755
index 00000000..31818510
--- /dev/null
+++ b/drivers/input/touchscreen/zet6221_ts/zet6221_i2c.c
@@ -0,0 +1,2786 @@
+/* drivers/input/touchscreen/zet6221_i2c.c
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ZEITEC Semiconductor Co., Ltd
+ * Tel: +886-3-579-0045
+ * Fax: +886-3-579-9960
+ * http://www.zeitecsemi.com
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <asm/uaccess.h>
+
+#include <linux/input/mt.h>
+#include "wmt_ts.h"
+#include <linux/power/wmt_battery.h>
+#include "../../../video/backlight/wmt_bl.h"
+
+
+//fw update.
+//#include "zet6221_fw.h"
+
+/* -------------- global variable definition -----------*/
+#define _MACH_MSM_TOUCH_H_
+
+#define ZET_TS_ID_NAME "zet6221-ts"
+
+#define MJ5_TS_NAME "touch_zet6221"
+
+//#define TS_INT_GPIO S3C64XX_GPN(9) /*s3c6410*/
+//#define TS1_INT_GPIO AT91_PIN_PB17 /*AT91SAM9G45 external*/
+//#define TS1_INT_GPIO AT91_PIN_PA27 /*AT91SAM9G45 internal*/
+//#define TS_RST_GPIO S3C64XX_GPN(10)
+
+//#define MT_TYPE_B
+
+#define TS_RST_GPIO
+#define X_MAX 800 //1024
+#define Y_MAX 480 //576
+#define FINGER_NUMBER 5
+#define KEY_NUMBER 3 //0
+//#define P_MAX 1
+#define P_MAX 255 //modify 2013-1-1
+#define D_POLLING_TIME 25000
+#define U_POLLING_TIME 25000
+#define S_POLLING_TIME 100
+#define REPORT_POLLING_TIME 3
+#define RETRY_DOWNLOAD_TIMES 2
+
+#define MAX_KEY_NUMBER 8
+#define MAX_FINGER_NUMBER 16
+#define TRUE 1
+#define FALSE 0
+
+//#define debug_mode 1
+//#define DPRINTK(fmt,args...) do { if (debug_mode) printk(KERN_EMERG "[%s][%d] "fmt"\n", __FUNCTION__, __LINE__, ##args);} while(0)
+
+//#define TRANSLATE_ENABLE 1
+#define TOPRIGHT 0
+#define TOPLEFT 1
+#define BOTTOMRIGHT 2
+#define BOTTOMLEFT 3
+#define ORIGIN BOTTOMRIGHT
+
+#define TIME_CHECK_CHARGE 3000
+
+#define I2C_MAJOR 126
+#define I2C_MINORS 256
+
+
+///=============================================================================================///
+/// IOCTL control Definition
+///=============================================================================================///
+#define ZET_IOCTL_CMD_FLASH_READ (20)
+#define ZET_IOCTL_CMD_FLASH_WRITE (21)
+#define ZET_IOCTL_CMD_RST (22)
+#define ZET_IOCTL_CMD_RST_HIGH (23)
+#define ZET_IOCTL_CMD_RST_LOW (24)
+
+#define ZET_IOCTL_CMD_DYNAMIC (25)
+
+#define ZET_IOCTL_CMD_FW_FILE_PATH_GET (26)
+#define ZET_IOCTL_CMD_FW_FILE_PATH_SET (27)
+
+#define ZET_IOCTL_CMD_MDEV (28)
+#define ZET_IOCTL_CMD_MDEV_GET (29)
+
+#define ZET_IOCTL_CMD_TRAN_TYPE_PATH_GET (30)
+#define ZET_IOCTL_CMD_TRAN_TYPE_PATH_SET (31)
+
+#define ZET_IOCTL_CMD_IDEV (32)
+#define ZET_IOCTL_CMD_IDEV_GET (33)
+
+#define ZET_IOCTL_CMD_MBASE (34)
+#define ZET_IOCTL_CMD_MBASE_GET (35)
+
+#define ZET_IOCTL_CMD_INFO_SET (36)
+#define ZET_IOCTL_CMD_INFO_GET (37)
+
+#define ZET_IOCTL_CMD_TRACE_X_SET (38)
+#define ZET_IOCTL_CMD_TRACE_X_GET (39)
+
+#define ZET_IOCTL_CMD_TRACE_Y_SET (40)
+#define ZET_IOCTL_CMD_TRACE_Y_GET (41)
+
+#define ZET_IOCTL_CMD_IBASE (42)
+#define ZET_IOCTL_CMD_IBASE_GET (43)
+
+#define ZET_IOCTL_CMD_DRIVER_VER_GET (44)
+#define ZET_IOCTL_CMD_MBASE_EXTERN_GET (45)
+
+#define IOCTL_MAX_BUF_SIZE (1024)
+
+///----------------------------------------------------///
+/// IOCTL ACTION
+///----------------------------------------------------///
+#define IOCTL_ACTION_NONE (0)
+#define IOCTL_ACTION_FLASH_DUMP (1<<0)
+
+static int ioctl_action = IOCTL_ACTION_NONE;
+
+///=============================================================================================///
+/// Transfer type
+///=============================================================================================///
+#define TRAN_TYPE_DYNAMIC (0x00)
+#define TRAN_TYPE_MUTUAL_SCAN_BASE (0x01)
+#define TRAN_TYPE_MUTUAL_SCAN_DEV (0x02)
+#define TRAN_TYPE_INIT_SCAN_BASE (0x03)
+#define TRAN_TYPE_INIT_SCAN_DEV (0x04)
+#define TRAN_TYPE_KEY_MUTUAL_SCAN_BASE (0x05)
+#define TRAN_TYPE_KEY_MUTUAL_SCAN_DEV (0x06)
+#define TRAN_TYPE_KEY_DATA (0x07)
+#define TRAN_TYPE_MTK_TYPE (0x0A)
+#define TRAN_TYPE_FOCAL_TYPE (0x0B)
+
+///=============================================================================================///
+/// TP Trace
+///=============================================================================================///
+#define TP_DEFAULT_ROW (10)
+#define TP_DEFAULT_COL (15)
+
+#define DRIVER_VERSION "$Revision: 44 $"
+//static char const *revision="$Revision: 44 $";
+
+///=============================================================================================///
+/// Macro Definition
+///=============================================================================================///
+#define MAX_FLASH_BUF_SIZE (0x10000)
+
+///---------------------------------------------------------------------------------///
+/// 18. IOCTRL Debug
+///---------------------------------------------------------------------------------///
+#define FEATURE_IDEV_OUT_ENABLE
+#define FEATURE_MBASE_OUT_ENABLE
+#define FEATURE_MDEV_OUT_ENABLE
+#define FEATURE_INFO_OUT_EANBLE
+#define FEATURE_IBASE_OUT_ENABLE
+
+
+
+///-------------------------------------///
+/// firmware save / load
+///-------------------------------------///
+u32 data_offset = 0;
+struct inode *inode = NULL;
+mm_segment_t old_fs;
+
+char driver_version[128];
+
+//#define FW_FILE_NAME "/vendor/modules/zet62xx.bin"
+char fw_file_name[128];
+///-------------------------------------///
+/// Transmit Type Mode Path parameters
+///-------------------------------------///
+/// External SD-Card could be
+/// "/mnt/sdcard/"
+/// "/mnt/extsd/"
+///-------------------------------------///
+
+// It should be the path where adb tools can push files in
+#define TRAN_MODE_FILE_PATH "/data/local/tmp/"
+char tran_type_mode_file_name[128];
+u8 *tran_data = NULL;
+
+///-------------------------------------///
+/// Mutual Dev Mode parameters
+///-------------------------------------///
+/// External SD-Card could be
+/// "/mnt/sdcard/zetmdev"
+/// "/mnt/extsd/zetmdev"
+///-------------------------------------///
+#ifdef FEATURE_MDEV_OUT_ENABLE
+ #define MDEV_FILE_NAME "zetmdev"
+ #define MDEV_MAX_FILE_ID (10)
+ #define MDEV_MAX_DATA_SIZE (2048)
+///-------------------------------------///
+/// mutual dev variables
+///-------------------------------------///
+ u8 *mdev_data = NULL;
+ int mdev_file_id = 0;
+#endif ///< FEATURE_MDEV_OUT_ENABLE
+
+///-------------------------------------///
+/// Initial Base Mode parameters
+///-------------------------------------///
+/// External SD-Card could be
+/// "/mnt/sdcard/zetibase"
+/// "/mnt/extsd/zetibase"
+///-------------------------------------///
+#ifdef FEATURE_IBASE_OUT_ENABLE
+ #define IBASE_FILE_NAME "zetibase"
+ #define IBASE_MAX_FILE_ID (10)
+ #define IBASE_MAX_DATA_SIZE (512)
+
+///-------------------------------------///
+/// initial base variables
+///-------------------------------------///
+ u8 *ibase_data = NULL;
+ int ibase_file_id = 0;
+#endif ///< FEATURE_IBASE_OUT_ENABLE
+
+///-------------------------------------///
+/// Initial Dev Mode parameters
+///-------------------------------------///
+/// External SD-Card could be
+/// "/mnt/sdcard/zetidev"
+/// "/mnt/extsd/zetidev"
+///-------------------------------------///
+#ifdef FEATURE_IDEV_OUT_ENABLE
+ #define IDEV_FILE_NAME "zetidev"
+ #define IDEV_MAX_FILE_ID (10)
+ #define IDEV_MAX_DATA_SIZE (512)
+
+///-------------------------------------///
+/// initial dev variables
+///-------------------------------------///
+ u8 *idev_data = NULL;
+ int idev_file_id = 0;
+#endif ///< FEATURE_IDEV_OUT_ENABLE
+
+///-------------------------------------///
+/// Mutual Base Mode parameters
+///-------------------------------------///
+/// External SD-Card could be
+/// "/mnt/sdcard/zetmbase"
+/// "/mnt/extsd/zetmbase"
+///-------------------------------------///
+#ifdef FEATURE_MBASE_OUT_ENABLE
+ #define MBASE_FILE_NAME "zetmbase"
+ #define MBASE_MAX_FILE_ID (10)
+ #define MBASE_MAX_DATA_SIZE (2048)
+
+///-------------------------------------///
+/// mutual base variables
+///-------------------------------------///
+ u8 *mbase_data = NULL;
+ int mbase_file_id = 0;
+#endif ///< FEATURE_MBASE_OUT_ENABLE
+
+///-------------------------------------///
+/// infomation variables
+///-------------------------------------///
+#ifdef FEATURE_INFO_OUT_EANBLE
+ #define INFO_MAX_DATA_SIZE (64)
+ #define INFO_DATA_SIZE (17)
+ #define ZET6221_INFO (0x00)
+ #define ZET6231_INFO (0x0B)
+ #define ZET6223_INFO (0x0D)
+ #define ZET6251_INFO (0x0C)
+ #define UNKNOW_INFO (0xFF)
+ u8 *info_data = NULL;
+#endif ///< FEATURE_INFO_OUT_EANBLE
+///-------------------------------------///
+/// Default transfer type
+///-------------------------------------///
+u8 transfer_type = TRAN_TYPE_DYNAMIC;
+
+///-------------------------------------///
+/// Default TP TRACE
+///-------------------------------------///
+int row = TP_DEFAULT_ROW;
+int col = TP_DEFAULT_COL;
+
+struct msm_ts_platform_data {
+ unsigned int x_max;
+ unsigned int y_max;
+ unsigned int pressure_max;
+};
+
+struct zet6221_tsdrv {
+ struct i2c_client *i2c_ts;
+ struct work_struct work1;
+ struct input_dev *input;
+ struct timer_list polling_timer;
+ struct delayed_work work; // for polling
+ struct workqueue_struct *queue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ unsigned int gpio; /* GPIO used for interrupt of TS1*/
+ unsigned int irq;
+ unsigned int x_max;
+ unsigned int y_max;
+ unsigned int pressure_max;
+};
+
+struct i2c_dev
+{
+ struct list_head list;
+ struct i2c_adapter *adap;
+ struct device *dev;
+};
+
+static struct i2c_dev *zet_i2c_dev;
+static struct class *i2c_dev_class;
+static LIST_HEAD (i2c_dev_list);
+static DEFINE_SPINLOCK(i2c_dev_list_lock);
+
+struct zet6221_tsdrv * l_ts = NULL;
+static int l_suspend = 0; // 1:suspend, 0:normal state
+
+//static int resetCount = 0; //albert++ 20120807
+
+
+//static u16 polling_time = S_POLLING_TIME;
+
+static int l_powermode = -1;
+static struct mutex i2c_mutex;
+static struct wake_lock downloadWakeLock;
+
+
+//static int __devinit zet6221_ts_probe(struct i2c_client *client, const struct i2c_device_id *id);
+//static int __devexit zet6221_ts_remove(struct i2c_client *dev);
+extern int register_bl_notifier(struct notifier_block *nb);
+
+extern int unregister_bl_notifier(struct notifier_block *nb);
+
+extern int zet6221_downloader( struct i2c_client *client/*, unsigned short ver, unsigned char * data */);
+extern int zet622x_resume_downloader(struct i2c_client *client);
+extern u8 zet6221_ts_version(void);
+extern u8 zet6221_ts_get_report_mode_t(struct i2c_client *client);
+extern u8 zet622x_ts_option(struct i2c_client *client);
+extern int zet6221_load_fw(void);
+extern int zet6221_free_fwmem(void);
+
+void zet6221_ts_charger_mode_disable(void);
+void zet6221_ts_charger_mode(void);
+static int zet_fw_size(void);
+static void zet_fw_save(char *file_name);
+static void zet_fw_load(char *file_name);
+static void zet_fw_init(void);
+#ifdef FEATURE_MDEV_OUT_ENABLE
+static void zet_mdev_save(char *file_name);
+#endif ///< FEATURE_MDEV_OUT_ENABLE
+#ifdef FEATURE_IDEV_OUT_ENABLE
+static void zet_idev_save(char *file_name);
+#endif ///< FEATURE_IDEV_OUT_ENABLE
+#ifdef FEATURE_IBASE_OUT_ENABLE
+static void zet_ibase_save(char *file_name);
+#endif ///< FEATURE_IBASE_OUT_ENABLE
+#ifdef FEATURE_MBASE_OUT_ENABLE
+static void zet_mbase_save(char *file_name);
+#endif ///< FEATURE_MBASE_OUT_ENABLE
+static void zet_information_save(char *file_name);
+
+static struct task_struct *resume_download_task;
+
+
+
+//static int filterCount = 0;
+//static u32 filterX[MAX_FINGER_NUMBER][2], filterY[MAX_FINGER_NUMBER][2];
+
+//static u8 key_menu_pressed = 0x1;
+//static u8 key_back_pressed = 0x1;
+//static u8 key_search_pressed = 0x1;
+
+static u16 ResolutionX=X_MAX;
+static u16 ResolutionY=Y_MAX;
+static u16 FingerNum=0;
+static u16 KeyNum=0;
+static int bufLength=0;
+static u8 xyExchange=0;
+static u16 inChargerMode = 0;
+static struct i2c_client *this_client;
+struct workqueue_struct *ts_wq = NULL;
+static int l_tskey[4][2] = {
+ {KEY_BACK,0},
+ {KEY_MENU,0},
+ {KEY_HOME,0},
+ {KEY_SEARCH,0},
+};
+
+u8 pc[8];
+// {IC Model, FW Version, FW version,Codebase Type=0x08, Customer ID, Project ID, Config Board No, Config Serial No}
+
+//Touch Screen
+/*static const struct i2c_device_id zet6221_ts_idtable[] = {
+ { ZET_TS_ID_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver zet6221_ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = ZET_TS_ID_NAME,
+ },
+ .probe = zet6221_ts_probe,
+ .remove = __devexit_p(zet6221_ts_remove),
+ .id_table = zet6221_ts_idtable,
+};
+*/
+
+void zet6221_set_tskey(int index,int key)
+{
+ l_tskey[index][0] = key;
+}
+
+
+void check_charger(void)
+{
+ mutex_lock(&i2c_mutex);
+ if (!wmt_charger_is_dc_plugin())
+ {
+ klog("disable_mode\n");
+ zet6221_ts_charger_mode_disable();
+ } else {
+ klog("charge mode\n");
+ zet6221_ts_charger_mode();
+ }
+ mutex_unlock(&i2c_mutex);
+ l_powermode = wmt_charger_is_dc_plugin();
+}
+
+
+void check_charger_polling(void)
+{
+ if(l_suspend == 1)
+ {
+ return;
+ }
+
+ if (wmt_charger_is_dc_plugin() != l_powermode)
+ {
+ check_charger();
+ }
+
+ ///-------------------------------------------------------------------///
+ /// IOCTL Action
+ ///-------------------------------------------------------------------///
+ if(ioctl_action & IOCTL_ACTION_FLASH_DUMP)
+ {
+ printk("[ZET]: IOCTL_ACTION: Dump flash\n");
+ zet_fw_save(fw_file_name);
+ ioctl_action &= ~IOCTL_ACTION_FLASH_DUMP;
+ }
+
+ return;
+}
+
+
+
+//extern unsigned int wmt_bat_is_batterypower(void);
+/***********************************************************************
+ [function]:
+ callback: Timer Function if there is no interrupt fuction;
+ [parameters]:
+ arg[in]: arguments;
+ [return]:
+ NULL;
+************************************************************************/
+
+static void polling_timer_func(struct work_struct *work)
+{
+ struct zet6221_tsdrv *ts = l_ts;
+ //schedule_work(&ts->work1);
+ //queue_work(ts_wq,&ts->work1);
+ //dbg("check mode!\n");
+/*
+ if (wmt_bat_is_batterypower() != l_powermode)
+ {
+ mutex_lock(&i2c_mutex);
+ if (wmt_bat_is_batterypower())
+ {
+ klog("disable_mode\n");
+ zet6221_ts_charger_mode_disable();
+ } else {
+ klog("charge mode\n");
+ zet6221_ts_charger_mode();
+ }
+ mutex_unlock(&i2c_mutex);
+ l_powermode = wmt_bat_is_batterypower();
+ }
+*/
+
+ check_charger_polling();
+ queue_delayed_work(ts->queue, &ts->work, msecs_to_jiffies(TIME_CHECK_CHARGE));
+
+
+ //mod_timer(&ts->polling_timer,jiffies + msecs_to_jiffies(TIME_CHECK_CHARGE));
+}
+
+
+
+///**********************************************************************
+/// [function]: zet622x_i2c_get_free_dev
+/// [parameters]: adap
+/// [return]: void
+///**********************************************************************
+static struct i2c_dev *zet622x_i2c_get_free_dev(struct i2c_adapter *adap)
+{
+ struct i2c_dev *i2c_dev;
+
+ if (adap->nr >= I2C_MINORS)
+ {
+ printk("[ZET] : i2c-dev:out of device minors (%d) \n",adap->nr);
+ return ERR_PTR (-ENODEV);
+ }
+
+ i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev)
+ {
+ return ERR_PTR(-ENOMEM);
+ }
+ i2c_dev->adap = adap;
+
+ spin_lock(&i2c_dev_list_lock);
+ list_add_tail(&i2c_dev->list, &i2c_dev_list);
+ spin_unlock(&i2c_dev_list_lock);
+
+ return i2c_dev;
+}
+
+///**********************************************************************
+/// [function]: zet622x_i2c_dev_get_by_minor
+/// [parameters]: index
+/// [return]: i2c_dev
+///**********************************************************************
+static struct i2c_dev *zet622x_i2c_dev_get_by_minor(unsigned index)
+{
+ struct i2c_dev *i2c_dev;
+ spin_lock(&i2c_dev_list_lock);
+
+ list_for_each_entry(i2c_dev, &i2c_dev_list, list)
+ {
+ printk(" [ZET] : line = %d ,i2c_dev->adapt->nr = %d,index = %d.\n",__LINE__,i2c_dev->adap->nr,index);
+ if(i2c_dev->adap->nr == index)
+ {
+ goto LABEL_FOUND;
+ }
+ }
+ i2c_dev = NULL;
+
+LABEL_FOUND:
+ spin_unlock(&i2c_dev_list_lock);
+
+ return i2c_dev ;
+}
+
+
+
+//extern int wmt_i2c_xfer_continue_if_4(struct i2c_msg *msg, unsigned int num,int bus_id);
+/***********************************************************************
+ [function]:
+ callback: read data by i2c interface;
+ [parameters]:
+ client[in]: struct i2c_client �represent an I2C slave device;
+ data [out]: data buffer to read;
+ length[in]: data length to read;
+ [return]:
+ Returns negative errno, else the number of messages executed;
+************************************************************************/
+int zet6221_i2c_read_tsdata(struct i2c_client *client, u8 *data, u8 length)
+{
+ struct i2c_msg msg;
+ msg.addr = client->addr;
+ msg.flags = I2C_M_RD;
+ msg.len = length;
+ msg.buf = data;
+ return i2c_transfer(client->adapter,&msg, 1);
+
+ /*int rc = 0;
+
+ memset(data, 0, length);
+ rc = i2c_master_recv(client, data, length);
+ if (rc <= 0)
+ {
+ errlog("error!\n");
+ return -EINVAL;
+ } else if (rc != length)
+ {
+ dbg("want:%d,real:%d\n", length, rc);
+ }
+ return rc;*/
+}
+
+/***********************************************************************
+ [function]:
+ callback: write data by i2c interface;
+ [parameters]:
+ client[in]: struct i2c_client �represent an I2C slave device;
+ data [out]: data buffer to write;
+ length[in]: data length to write;
+ [return]:
+ Returns negative errno, else the number of messages executed;
+************************************************************************/
+int zet6221_i2c_write_tsdata(struct i2c_client *client, u8 *data, u8 length)
+{
+ struct i2c_msg msg;
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = length;
+ msg.buf = data;
+ return i2c_transfer(client->adapter,&msg, 1);
+
+ /*int ret = i2c_master_recv(client, data, length);
+ if (ret <= 0)
+ {
+ errlog("error!\n");
+ }
+ return ret;
+ */
+}
+
+/***********************************************************************
+ [function]:
+ callback: coordinate traslating;
+ [parameters]:
+ px[out]: value of X axis;
+ py[out]: value of Y axis;
+ p [in]: pressed of released status of fingers;
+ [return]:
+ NULL;
+************************************************************************/
+void touch_coordinate_traslating(u32 *px, u32 *py, u8 p)
+{
+ int i;
+ u8 pressure;
+
+ #if ORIGIN == TOPRIGHT
+ for(i=0;i<MAX_FINGER_NUMBER;i++){
+ pressure = (p >> (MAX_FINGER_NUMBER-i-1)) & 0x1;
+ if(pressure)
+ {
+ px[i] = X_MAX - px[i];
+ }
+ }
+ #elif ORIGIN == BOTTOMRIGHT
+ for(i=0;i<MAX_FINGER_NUMBER;i++){
+ pressure = (p >> (MAX_FINGER_NUMBER-i-1)) & 0x1;
+ if(pressure)
+ {
+ px[i] = X_MAX - px[i];
+ py[i] = Y_MAX - py[i];
+ }
+ }
+ #elif ORIGIN == BOTTOMLEFT
+ for(i=0;i<MAX_FINGER_NUMBER;i++){
+ pressure = (p >> (MAX_FINGER_NUMBER-i-1)) & 0x1;
+ if(pressure)
+ {
+ py[i] = Y_MAX - py[i];
+ }
+ }
+ #endif
+}
+
+/***********************************************************************
+ [function]:
+ callback: reset function;
+ [parameters]:
+ void;
+ [return]:
+ void;
+************************************************************************/
+void ctp_reset(void)
+{
+#if defined(TS_RST_GPIO)
+ //reset mcu
+ /* gpio_direction_output(TS_RST_GPIO, 1);
+ msleep(1);
+ gpio_direction_output(TS_RST_GPIO, 0);
+ msleep(10);
+ gpio_direction_output(TS_RST_GPIO, 1);
+ msleep(20);*/
+ wmt_rst_output(1);
+ msleep(1);
+ wmt_rst_output(0);
+ msleep(10);
+ wmt_rst_output(1);
+ msleep(5);
+ dbg("has done\n");
+#else
+ u8 ts_reset_cmd[1] = {0xb0};
+ zet6221_i2c_write_tsdata(this_client, ts_reset_cmd, 1);
+#endif
+
+}
+
+
+///**********************************************************************
+/// [function]: zet622x_ts_parse_mutual_dev
+/// [parameters]: client
+/// [return]: u8
+///**********************************************************************
+#ifdef FEATURE_MDEV_OUT_ENABLE
+u8 zet622x_ts_parse_mutual_dev(struct i2c_client *client)
+{
+ int mdev_packet_size = (row+2) * (col + 2);
+ int ret = 0;
+ int idx = 0;
+ int len = mdev_packet_size;
+ char mdev_file_name_out[128];
+
+ int step_size = col + 2;
+
+ while(len > 0)
+ {
+ if(len < step_size)
+ {
+ step_size = len;
+ }
+
+ ret = zet6221_i2c_read_tsdata(client, &tran_data[idx], step_size);
+ len -= step_size;
+ idx += step_size;
+ }
+
+ sprintf(mdev_file_name_out, "%s%s%02d.bin", tran_type_mode_file_name, MDEV_FILE_NAME, mdev_file_id);
+ zet_mdev_save(mdev_file_name_out);
+ mdev_file_id = (mdev_file_id +1)% (MDEV_MAX_FILE_ID);
+ return ret;
+}
+#endif ///< FEATURE_MDEV_OUT_ENABLE
+
+///**********************************************************************
+/// [function]: zet622x_ts_parse_initial_base
+/// [parameters]: client
+/// [return]: u8
+///**********************************************************************
+#ifdef FEATURE_IBASE_OUT_ENABLE
+u8 zet622x_ts_parse_initial_base(struct i2c_client *client)
+{
+ int ibase_packet_size = (row + col) * 2;
+ int ret = 0;
+ int idx = 0;
+ int len = ibase_packet_size;
+ char ibase_file_name_out[128];
+
+ int step_size = ibase_packet_size;
+
+ while(len > 0)
+ {
+ ret = zet6221_i2c_read_tsdata(client, &tran_data[idx], step_size);
+ len -= step_size;
+ }
+ sprintf(ibase_file_name_out, "%s%s%02d.bin", tran_type_mode_file_name, IBASE_FILE_NAME, ibase_file_id);
+ zet_ibase_save(ibase_file_name_out);
+ ibase_file_id = (ibase_file_id +1)% (IBASE_MAX_FILE_ID);
+ return ret;
+}
+#endif ///< FEATURE_IBASE_OUT_ENABLE
+
+///**********************************************************************
+/// [function]: zet622x_ts_parse_initial_dev
+/// [parameters]: client
+/// [return]: u8
+///**********************************************************************
+#ifdef FEATURE_IDEV_OUT_ENABLE
+u8 zet622x_ts_parse_initial_dev(struct i2c_client *client)
+{
+ int idev_packet_size = (row + col);
+ int ret = 0;
+ int idx = 0;
+ int len = idev_packet_size;
+ char idev_file_name_out[128];
+
+ int step_size = idev_packet_size;
+
+ while(len > 0)
+ {
+ ret = zet6221_i2c_read_tsdata(client, &tran_data[idx], step_size);
+ len -= step_size;
+ }
+ sprintf(idev_file_name_out, "%s%s%02d.bin", tran_type_mode_file_name, IDEV_FILE_NAME, idev_file_id);
+ zet_idev_save(idev_file_name_out);
+ idev_file_id = (idev_file_id +1)% (IDEV_MAX_FILE_ID);
+ return ret;
+}
+#endif ///< FEATURE_IDEV_OUT_ENABLE
+
+///**********************************************************************
+/// [function]: zet622x_ts_parse_mutual_base
+/// [parameters]: client
+/// [return]: u8
+///**********************************************************************
+#ifdef FEATURE_MBASE_OUT_ENABLE
+u8 zet622x_ts_parse_mutual_base(struct i2c_client *client)
+{
+ int mbase_packet_size = (row * col * 2);
+ int ret = 0;
+ int idx = 0;
+ int len = mbase_packet_size;
+ char mbase_file_name_out[128];
+
+ int step_size = col*2;
+
+ while(len > 0)
+ {
+ if(len < step_size)
+ {
+ step_size = len;
+ }
+
+ ret = zet6221_i2c_read_tsdata(client, &tran_data[idx], step_size);
+ len -= step_size;
+ idx += step_size;
+ }
+ sprintf(mbase_file_name_out, "%s%s%02d.bin",tran_type_mode_file_name, MBASE_FILE_NAME, mbase_file_id);
+ zet_mbase_save(mbase_file_name_out);
+ mbase_file_id = (mbase_file_id +1)% (MBASE_MAX_FILE_ID);
+ return ret;
+}
+#endif ///< FEATURE_MBASE_OUT_ENABLE
+
+/***********************************************************************
+ [function]:
+ callback: read finger information from TP;
+ [parameters]:
+ client[in]: struct i2c_client �represent an I2C slave device;
+ x[out]: values of X axis;
+ y[out]: values of Y axis;
+ z[out]: values of Z axis;
+ pr[out]: pressed of released status of fingers;
+ ky[out]: pressed of released status of keys;
+ [return]:
+ Packet ID;
+************************************************************************/
+u8 zet6221_ts_get_xy_from_panel(struct i2c_client *client, u32 *x, u32 *y, u32 *z, u32 *pr, u32 *ky)
+{
+ u8 ts_data[70];
+ int ret;
+ int i;
+
+ memset(ts_data,0,70);
+
+ ret=zet6221_i2c_read_tsdata(client, ts_data, bufLength);
+
+ *pr = ts_data[1];
+ *pr = (*pr << 8) | ts_data[2];
+
+ for(i=0;i<FingerNum;i++)
+ {
+ x[i]=(u8)((ts_data[3+4*i])>>4)*256 + (u8)ts_data[(3+4*i)+1];
+ y[i]=(u8)((ts_data[3+4*i]) & 0x0f)*256 + (u8)ts_data[(3+4*i)+2];
+ z[i]=(u8)((ts_data[(3+4*i)+3]) & 0x0f);
+ }
+
+ //if key enable
+ if(KeyNum > 0)
+ *ky = ts_data[3+4*FingerNum];
+
+ return ts_data[0];
+}
+
+/***********************************************************************
+ [function]:
+ callback: get dynamic report information;
+ [parameters]:
+ client[in]: struct i2c_client �represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+u8 zet6221_ts_get_report_mode(struct i2c_client *client)
+{
+ u8 ts_report_cmd[1] = {0xb2};
+ //u8 ts_reset_cmd[1] = {0xb0};
+ u8 ts_in_data[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ int ret;
+ int i;
+ int count=0;
+
+ ret=zet6221_i2c_write_tsdata(client, ts_report_cmd, 1);
+
+ if (ret > 0)
+ {
+ while(1)
+ {
+ msleep(1);
+
+ //if (gpio_get_value(TS_INT_GPIO) == 0)
+ if (wmt_ts_irqinval() == 0)
+ {
+ dbg( "int low\n");
+ ret=zet6221_i2c_read_tsdata(client, ts_in_data, 17);
+
+ if(ret > 0)
+ {
+
+ for(i=0;i<8;i++)
+ {
+ pc[i]=ts_in_data[i] & 0xff;
+ }
+
+ if(pc[3] != 0x08)
+ {
+ errlog("=============== zet6221_ts_get_report_mode report error ===============\n");
+ return 0;
+ }
+
+ xyExchange = (ts_in_data[16] & 0x8) >> 3;
+ if(xyExchange == 1)
+ {
+ ResolutionY= ts_in_data[9] & 0xff;
+ ResolutionY= (ResolutionY << 8)|(ts_in_data[8] & 0xff);
+ ResolutionX= ts_in_data[11] & 0xff;
+ ResolutionX= (ResolutionX << 8) | (ts_in_data[10] & 0xff);
+ }
+ else
+ {
+ ResolutionX = ts_in_data[9] & 0xff;
+ ResolutionX = (ResolutionX << 8)|(ts_in_data[8] & 0xff);
+ ResolutionY = ts_in_data[11] & 0xff;
+ ResolutionY = (ResolutionY << 8) | (ts_in_data[10] & 0xff);
+ }
+
+ FingerNum = (ts_in_data[15] & 0x7f);
+ KeyNum = (ts_in_data[15] & 0x80);
+
+ if(KeyNum==0)
+ bufLength = 3+4*FingerNum;
+ else
+ bufLength = 3+4*FingerNum+1;
+
+ //DPRINTK( "bufLength=%d\n",bufLength);
+
+ break;
+
+ }else
+ {
+ errlog ("=============== zet6221_ts_get_report_mode read error ===============\n");
+ return 0;
+ }
+
+ }else
+ {
+ //DPRINTK( "int high\n");
+ if(count++ > 30)
+ {
+ errlog ("=============== zet6221_ts_get_report_mode time out ===============\n");
+ return 0;
+ }
+
+ }
+ }
+
+ }
+ return 1;
+}
+
+#if 0
+static int zet6221_is_ts(struct i2c_client *client)
+{
+ /*u8 ts_in_data[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ ctp_reset();
+ if (zet6221_i2c_read_tsdata(client, ts_in_data, 17) <= 0)
+ {
+ return 0;
+ }
+ return 1;*/
+ return 1;
+}
+#endif
+
+/***********************************************************************
+ [function]:
+ callback: get dynamic report information with timer delay;
+ [parameters]:
+ client[in]: struct i2c_client represent an I2C slave device;
+
+ [return]:
+ 1;
+************************************************************************/
+
+u8 zet6221_ts_get_report_mode_t(struct i2c_client *client)
+{
+ u8 ts_report_cmd[1] = {0xb2};
+ u8 ts_in_data[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ int ret;
+ int i;
+
+ ret=zet6221_i2c_write_tsdata(client, ts_report_cmd, 1);
+ msleep(10);
+
+ dbg("ret=%d,i2c_addr=0x%x\n", ret, client->addr);
+ if (ret > 0)
+ {
+ //mdelay(10);
+ //msleep(10);
+ dbg("=============== zet6221_ts_get_report_mode_t ===============\n");
+ ret=zet6221_i2c_read_tsdata(client, ts_in_data, 17);
+
+ if(ret > 0)
+ {
+
+ for(i=0;i<8;i++)
+ {
+ pc[i]=ts_in_data[i] & 0xff;
+ }
+
+ if(pc[3] != 0x08)
+ {
+ errlog("=============== zet6221_ts_get_report_mode_t report error ===============\n");
+ return 0;
+ }
+
+ xyExchange = (ts_in_data[16] & 0x8) >> 3;
+ if(xyExchange == 1)
+ {
+ ResolutionY= ts_in_data[9] & 0xff;
+ ResolutionY= (ResolutionY << 8)|(ts_in_data[8] & 0xff);
+ ResolutionX= ts_in_data[11] & 0xff;
+ ResolutionX= (ResolutionX << 8) | (ts_in_data[10] & 0xff);
+ }
+ else
+ {
+ ResolutionX = ts_in_data[9] & 0xff;
+ ResolutionX = (ResolutionX << 8)|(ts_in_data[8] & 0xff);
+ ResolutionY = ts_in_data[11] & 0xff;
+ ResolutionY = (ResolutionY << 8) | (ts_in_data[10] & 0xff);
+ }
+
+ FingerNum = (ts_in_data[15] & 0x7f);
+ KeyNum = (ts_in_data[15] & 0x80);
+ inChargerMode = (ts_in_data[16] & 0x2) >> 1;
+
+ if(KeyNum==0)
+ bufLength = 3+4*FingerNum;
+ else
+ bufLength = 3+4*FingerNum+1;
+
+ }else
+ {
+ errlog ("=============== zet6221_ts_get_report_mode_t READ ERROR ===============\n");
+ return 0;
+ }
+
+ }else
+ {
+ errlog("=============== zet6221_ts_get_report_mode_t WRITE ERROR ===============\n");
+ return 0;
+ }
+ return 1;
+}
+
+/***********************************************************************
+ [function]:
+ callback: interrupt function;
+ [parameters]:
+ irq[in]: irq value;
+ dev_id[in]: dev_id;
+
+ [return]:
+ NULL;
+************************************************************************/
+static irqreturn_t zet6221_ts_interrupt(int irq, void *dev_id)
+{
+ struct zet6221_tsdrv *ts_drv = dev_id;
+ int j = 0;
+ if (wmt_is_tsint())
+ {
+ wmt_clr_int();
+ if (wmt_is_tsirq_enable() && l_suspend == 0)
+ {
+ wmt_disable_gpirq();
+ dbg("begin..\n");
+ //if (!work_pending(&l_tsdata.pen_event_work))
+ if (wmt_ts_irqinval() == 0)
+ {
+ queue_work(ts_wq, &ts_drv->work1);
+ } else {
+ if(KeyNum > 0)
+ {
+ //if (0 == ky)
+ {
+ for (j=0;j<4;j++)
+ {
+ if (l_tskey[j][1] != 0)
+ {
+ l_tskey[j][1] = 0;
+ }
+ }
+ dbg("finish one key report!\n");
+ }
+ }
+ wmt_enable_gpirq();
+ }
+ }
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+
+ /*//polling_time = D_POLLING_TIME;
+
+ if (gpio_get_value(TS_INT_GPIO) == 0)
+ {
+ // IRQ is triggered by FALLING code here
+ struct zet6221_tsdrv *ts_drv = dev_id;
+ schedule_work(&ts_drv->work1);
+ //DPRINTK("TS1_INT_GPIO falling\n");
+ }else
+ {
+ //DPRINTK("TS1_INT_GPIO raising\n");
+ }
+
+ return IRQ_HANDLED;*/
+}
+
+/***********************************************************************
+ [function]:
+ callback: touch information handler;
+ [parameters]:
+ _work[in]: struct work_struct;
+
+ [return]:
+ NULL;
+************************************************************************/
+static void zet6221_ts_work(struct work_struct *_work)
+{
+ u32 x[MAX_FINGER_NUMBER], y[MAX_FINGER_NUMBER], z[MAX_FINGER_NUMBER], pr, ky, points;
+ u32 px,py,pz;
+ u8 ret;
+ u8 pressure;
+ int i,j;
+ int tx,ty;
+ int xmax,ymax;
+ int realnum = 0;
+ struct zet6221_tsdrv *ts =
+ container_of(_work, struct zet6221_tsdrv, work1);
+
+ struct i2c_client *tsclient1 = ts->i2c_ts;
+
+ if(l_suspend == 1)
+ {
+ return;
+ }
+
+ if (bufLength == 0)
+ {
+ wmt_enable_gpirq();
+ return;
+ }
+ /*if(resetCount == 1)
+ {
+ resetCount = 0;
+ wmt_enable_gpirq();
+ return;
+ }*/
+
+ //if (gpio_get_value(TS_INT_GPIO) != 0)
+ if (wmt_ts_irqinval() != 0)
+ {
+ /* do not read when IRQ is triggered by RASING*/
+ //DPRINTK("INT HIGH\n");
+ dbg("INT HIGH....\n");
+ wmt_enable_gpirq();
+ return;
+ }
+
+ ///-------------------------------------------///
+ /// Transfer Type : Mutual Dev Mode
+ ///-------------------------------------------///
+#ifdef FEATURE_MDEV_OUT_ENABLE
+ if(transfer_type == TRAN_TYPE_MUTUAL_SCAN_DEV)
+ {
+ zet622x_ts_parse_mutual_dev(tsclient1);
+ wmt_enable_gpirq();
+ return;
+ }
+#endif ///< FEATURE_MDEV_OUT_ENABLE
+
+ ///-------------------------------------------///
+ /// Transfer Type : Initial Base Mode
+ ///-------------------------------------------///
+#ifdef FEATURE_IBASE_OUT_ENABLE
+ if(transfer_type == TRAN_TYPE_INIT_SCAN_BASE)
+ {
+ zet622x_ts_parse_initial_base(tsclient1);
+ wmt_enable_gpirq();
+ return;
+ }
+#endif ///< FEATURE_IBASE_OUT_ENABLE
+
+ ///-------------------------------------------///
+ /// Transfer Type : Initial Dev Mode
+ ///-------------------------------------------///
+#ifdef FEATURE_IDEV_OUT_ENABLE
+ if(transfer_type == TRAN_TYPE_INIT_SCAN_DEV)
+ {
+ zet622x_ts_parse_initial_dev(tsclient1);
+ wmt_enable_gpirq();
+ return;
+ }
+#endif ///< TRAN_TYPE_INIT_SCAN_DEV
+
+ ///-------------------------------------------///
+ /// Transfer Type : Mutual Base Mode
+ ///-------------------------------------------///
+#ifdef FEATURE_MBASE_OUT_ENABLE
+ if(transfer_type == TRAN_TYPE_MUTUAL_SCAN_BASE)
+ {
+ zet622x_ts_parse_mutual_base(tsclient1);
+ wmt_enable_gpirq();
+ return;
+ }
+#endif ///< FEATURE_MBASE_OUT_ENABLE
+
+ mutex_lock(&i2c_mutex);
+ ret = zet6221_ts_get_xy_from_panel(tsclient1, x, y, z, &pr, &ky);
+ mutex_unlock(&i2c_mutex);
+
+ if(ret == 0x3C)
+ {
+
+ dbg( "x1= %d, y1= %d x2= %d, y2= %d [PR] = %d [KY] = %d\n", x[0], y[0], x[1], y[1], pr, ky);
+
+ points = pr;
+
+ #if defined(TRANSLATE_ENABLE)
+ touch_coordinate_traslating(x, y, points);
+ #endif
+ realnum = 0;
+
+ for(i=0;i<FingerNum;i++){
+ pressure = (points >> (MAX_FINGER_NUMBER-i-1)) & 0x1;
+ dbg( "valid=%d pressure[%d]= %d x= %d y= %d\n",points , i, pressure,x[i],y[i]);
+
+ if(pressure)
+ {
+ px = x[i];
+ py = y[i];
+ pz = z[i];
+
+ dbg("raw%d(%d,%d) xaxis:%d ResolutionX:%d ResolutionY:%d\n", i, px, py,wmt_ts_get_xaxis(),ResolutionX,ResolutionY);
+
+ //input_report_abs(ts->input, ABS_MT_TRACKING_ID, i);
+ //input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, P_MAX);
+ //input_report_abs(ts->input, ABS_MT_POSITION_X, x[i]);
+ //input_report_abs(ts->input, ABS_MT_POSITION_Y, y[i]);
+ if (wmt_ts_get_xaxis() == 0)
+ {
+ tx = px;
+ ty = py;
+ xmax = ResolutionX;
+ ymax = ResolutionY;
+ } else {
+ tx = py;
+ ty = px;
+ xmax = ResolutionY;
+ ymax = ResolutionX;
+ }
+ if (wmt_ts_get_xdir() == -1)
+ {
+ tx = xmax - tx;
+ }
+ if (wmt_ts_get_ydir() == -1)
+ {
+ ty = ymax - ty;
+ }
+ //tx = ResolutionY - py;
+ //ty = px;
+ dbg("rpt%d(%d,%d)\n", i, tx, ty);
+ //add for cross finger 2013-1-10
+ #ifdef MT_TYPE_B
+ input_mt_slot(ts->input, i);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,true);
+ #endif
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, i);
+ //input_report_key(ts->input, BTN_TOUCH, 1);
+ //input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, pz);
+ //*******************************
+
+ if (wmt_ts_get_lcdexchg()) {
+ int tmp;
+ tmp = tx;
+ tx = ty;
+ ty = ResolutionY - tmp;
+ }
+
+ input_report_abs(ts->input, ABS_MT_POSITION_X, tx /*px*/);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y, ty /*py*/);
+
+ #ifndef MT_TYPE_B
+ input_mt_sync(ts->input);
+ #endif
+ realnum++;
+ if (wmt_ts_ispenup())
+ {
+ wmt_ts_set_penup(0);
+ }
+
+ }else
+ {
+ //input_report_abs(ts->input, ABS_MT_TRACKING_ID, i);
+ //input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, 0);
+ //input_mt_sync(ts->input);
+ #ifdef MT_TYPE_B
+ input_mt_slot(ts->input, i);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,false);
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1);
+ #endif //add cross finger 2013-1-10
+ dbg("p%d not pen down\n",i);
+ }
+ }
+
+ #ifdef MT_TYPE_B
+ input_mt_report_pointer_emulation(ts->input, true);
+ #endif //add finger cross 2013-1-10
+ //printk("<<<realnum %d\n", realnum);
+ if (realnum != 0)
+ {
+ input_sync(ts->input);
+ dbg("report one point group\n");
+ } else if (!wmt_ts_ispenup())
+ {//********here no finger press 2013-1-10
+ //add 2013-1-10 cross finger issue!
+ #ifdef MT_TYPE_B
+ for(i=0;i<FingerNum;i++){
+ input_mt_slot(ts->input, i);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,false);
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1);
+ }
+ input_mt_report_pointer_emulation(ts->input, true);
+ #else
+ //input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, 0);
+ //input_mt_sync(ts->input);
+ //input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1);
+ //input_report_key(ts->input, BTN_TOUCH, 0);
+ #endif
+ //**********************************
+ input_mt_sync(ts->input);
+ input_sync(ts->input);
+ dbg("real pen up!\n");
+ wmt_ts_set_penup(1);
+ }
+
+ if(KeyNum > 0)
+ {
+ //for(i=0;i<MAX_KEY_NUMBER;i++)
+ if (0 == ky)
+ {
+ for (j=0;j<4;j++)
+ {
+ if (l_tskey[j][1] != 0)
+ {
+ l_tskey[j][1] = 0;
+ }
+ }
+ dbg("finish one key report!\n");
+ } else {
+ for(i=0;i<4;i++)
+ {
+ pressure = ky & ( 0x01 << i );
+ if (pressure)
+ {
+ dbg("key%d\n", i);
+ if (0 == l_tskey[i][1])
+ {
+ l_tskey[i][1] = 1; // key down
+ input_report_key(ts->input, l_tskey[i][0], 1);
+ input_report_key(ts->input, l_tskey[i][0], 0);
+ input_sync(ts->input);
+ dbg("report key_%d\n", l_tskey[i][0]);
+ break;
+ }
+ }
+
+ }
+ }
+ }
+
+ dbg("normal end...\n");
+ }else {
+ dbg("do nothing!\n");
+ if(KeyNum > 0)
+ {
+ //if (0 == ky)
+ {
+ for (j=0;j<4;j++)
+ {
+ if (l_tskey[j][1] != 0)
+ {
+ l_tskey[j][1] = 0;
+ }
+ }
+ dbg("finish one key report!\n");
+ }
+ }
+ }
+ wmt_enable_gpirq();
+
+}
+
+/***********************************************************************
+ [function]:
+ callback: charger mode enable;
+ [parameters]:
+ void
+
+ [return]:
+ void
+************************************************************************/
+void zet6221_ts_charger_mode()
+{
+ //struct zet6221_tsdrv *zet6221_ts;
+ u8 ts_write_charge_cmd[1] = {0xb5};
+ int ret=0;
+ ret=zet6221_i2c_write_tsdata(this_client, ts_write_charge_cmd, 1);
+}
+EXPORT_SYMBOL_GPL(zet6221_ts_charger_mode);
+
+/***********************************************************************
+ [function]:
+ callback: charger mode disable;
+ [parameters]:
+ void
+
+ [return]:
+ void
+************************************************************************/
+void zet6221_ts_charger_mode_disable(void)
+{
+ //struct zet6221_tsdrv *zet6221_ts;
+ u8 ts_write_cmd[1] = {0xb6};
+ int ret=0;
+ ret=zet6221_i2c_write_tsdata(this_client, ts_write_cmd, 1);
+}
+EXPORT_SYMBOL_GPL(zet6221_ts_charger_mode_disable);
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ts_early_suspend(struct early_suspend *handler)
+{
+ //Sleep Mode
+/* u8 ts_sleep_cmd[1] = {0xb1};
+ int ret=0;
+ ret=zet6221_i2c_write_tsdata(this_client, ts_sleep_cmd, 1);
+ return;
+ */
+ wmt_disable_gpirq();
+ l_suspend = 1;
+ //del_timer(&l_ts->polling_timer);
+
+}
+
+static void ts_late_resume(struct early_suspend *handler)
+{
+ resetCount = 1;
+ //if (l_suspend != 0)
+ {
+ //wmt_disable_gpirq();
+ //ctp_reset();
+ //wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);
+ wmt_enable_gpirq();
+ l_suspend = 0;
+ }
+ //l_powermode = -1;
+ //mod_timer(&l_ts->polling_timer,jiffies + msecs_to_jiffies(TIME_CHECK_CHARGE));
+
+}
+#endif
+static int zet_ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ wmt_disable_gpirq();
+ l_suspend = 1;
+ return 0;
+}
+
+
+/***********************************************************************
+ [function]:
+ resume_download_thread
+ [parameters]:
+ arg
+
+ [return]:
+ int;
+************************************************************************/
+int resume_download_thread(void *arg)
+{
+ char fw_name[64];
+ wake_lock(&downloadWakeLock);
+ sprintf(fw_name, "%szet62xx.bin", tran_type_mode_file_name);
+ zet_fw_load(fw_name);
+ //printk("Thread : Enter\n");
+// if((iRomType == ROM_TYPE_SRAM) ||
+// (iRomType == ROM_TYPE_OTP)) //SRAM,OTP
+ // {
+ zet622x_resume_downloader(this_client);
+ check_charger();
+ l_suspend = 0;
+ //printk("zet622x download OK\n");
+ // }
+ //printk("Thread : Leave\n");
+ wake_unlock(&downloadWakeLock);
+ return 0;
+}
+
+static int zet_ts_resume(struct platform_device *pdev)
+{
+ wmt_disable_gpirq();
+ ctp_reset();
+
+ if(ic_model == ZET6251) {
+ //upload bin to flash_buffer, just for debug
+ resume_download_task = kthread_create(resume_download_thread, NULL , "resume_download");
+ if(IS_ERR(resume_download_task)) {
+ errlog("cread thread failed\n");
+ }
+ wake_up_process(resume_download_task);
+ } else {
+ check_charger();
+ l_suspend = 0;
+ }
+
+ wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);
+ if (!earlysus_en)
+ wmt_enable_gpirq();
+
+ ///--------------------------------------///
+ /// Set transfer type to dynamic mode
+ ///--------------------------------------///
+ transfer_type = TRAN_TYPE_DYNAMIC;
+
+ return 0;
+}
+
+
+///**********************************************************************
+/// [function]: zet622x_ts_set_transfer_type
+/// [parameters]: void
+/// [return]: void
+///**********************************************************************
+int zet622x_ts_set_transfer_type(u8 bTransType)
+{
+ u8 ts_cmd[10] = {0xC1, 0x02, TRAN_TYPE_DYNAMIC, 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int ret = 0;
+ ts_cmd[2] = bTransType;
+ ret = zet6221_i2c_write_tsdata(this_client, ts_cmd, 10);
+ return ret;
+}
+
+
+///**********************************************************************
+/// [function]: zet622x_ts_set_transfer_type
+/// [parameters]: void
+/// [return]: void
+///**********************************************************************
+#ifdef FEATURE_INFO_OUT_EANBLE
+int zet622x_ts_set_info_type(void)
+{
+ int ret = 1;
+ char info_file_name_out[128];
+
+ /// ic type
+ switch(ic_model)
+ {
+ case ZET6221:
+ tran_data[0] = ZET6221_INFO;
+ break;
+ case ZET6223:
+ tran_data[0] = ZET6223_INFO;
+ break;
+ case ZET6231:
+ tran_data[0] = ZET6231_INFO;
+ break;
+ case ZET6251:
+ tran_data[0] = ZET6251_INFO;
+ break;
+ default:
+ tran_data[0] = UNKNOW_INFO;
+ break;
+ }
+
+ /// resolution
+ if(xyExchange== 1)
+ {
+ tran_data[16] = 0x8;
+ tran_data[9] = ((ResolutionY >> 8)&0xFF);
+ tran_data[8] = (ResolutionY &0xFF);
+ tran_data[11] = ((ResolutionX >> 8)&0xFF);
+ tran_data[10] = (ResolutionX &0xFF);
+ }
+ else
+ {
+ tran_data[16] = 0x00;
+ tran_data[9] = ((ResolutionX >> 8)&0xFF);
+ tran_data[8] = (ResolutionX &0xFF);
+ tran_data[11] = ((ResolutionY >> 8)&0xFF);
+ tran_data[10] = (ResolutionY &0xFF);
+ }
+
+ /// trace X
+ tran_data[13] = TP_DEFAULT_COL; ///< trace x
+ /// trace Y
+ tran_data[14] = TP_DEFAULT_ROW; ///< trace y
+
+ if(KeyNum > 0)
+ {
+ tran_data[15] = (0x80 | FingerNum);
+ }
+ else
+ {
+ tran_data[15] = FingerNum;
+ }
+
+ sprintf(info_file_name_out, "%sinfo.bin",tran_type_mode_file_name);
+ zet_information_save(info_file_name_out);
+
+ printk("[ZET] : ic:%d, traceX:%d, traceY:%d\n", tran_data[0],tran_data[13],tran_data[14]);
+ return ret;
+}
+#endif ///< FEATURE_INFO_OUT_EANBLE
+
+///***********************************************************************
+/// [function]: zet_mdev_save
+/// [parameters]: char *
+/// [return]: void
+///************************************************************************
+static void zet_mdev_save(char *file_name)
+{
+ struct file *fp;
+ int data_total_len = (row+2) * (col + 2);
+
+ ///-------------------------------------------------------///
+ /// create the file that stores the mutual dev data
+ ///-------------------------------------------------------///
+ fp = filp_open(file_name, O_RDWR | O_CREAT, 0644);
+ if(IS_ERR(fp))
+ {
+ printk("[ZET] : Failed to open %s\n", file_name);
+ return;
+ }
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ vfs_write(fp, tran_data, data_total_len, &(fp->f_pos));
+ memcpy(mdev_data, tran_data, data_total_len);
+ set_fs(old_fs);
+ filp_close(fp, 0);
+
+ return;
+}
+
+///***********************************************************************
+/// [function]: zet_idev_save
+/// [parameters]: char *
+/// [return]: void
+///************************************************************************
+#ifdef FEATURE_IDEV_OUT_ENABLE
+static void zet_idev_save(char *file_name)
+{
+ struct file *fp;
+ int data_total_len = (row + col);
+
+ ///-------------------------------------------------------///
+ /// create the file that stores the initial dev data
+ ///-------------------------------------------------------///
+ fp = filp_open(file_name, O_RDWR | O_CREAT, 0644);
+ if(IS_ERR(fp))
+ {
+ printk("[ZET] : Failed to open %s\n", file_name);
+ return;
+ }
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ vfs_write(fp, tran_data, data_total_len, &(fp->f_pos));
+ memcpy(idev_data, tran_data, data_total_len);
+ set_fs(old_fs);
+ filp_close(fp, 0);
+
+ return;
+}
+#endif ///< FEATURE_IDEV_OUT_ENABLE
+
+///***********************************************************************
+/// [function]: zet_ibase_save
+/// [parameters]: char *
+/// [return]: void
+///************************************************************************
+#ifdef FEATURE_IBASE_OUT_ENABLE
+static void zet_ibase_save(char *file_name)
+{
+ struct file *fp;
+ int data_total_len = (row + col) * 2;
+
+ ///-------------------------------------------------------///
+ /// create the file that stores the initial base data
+ ///-------------------------------------------------------///
+ fp = filp_open(file_name, O_RDWR | O_CREAT, 0644);
+ if(IS_ERR(fp))
+ {
+ printk("[ZET] : Failed to open %s\n", file_name);
+ return;
+ }
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ vfs_write(fp, tran_data, data_total_len, &(fp->f_pos));
+ memcpy(ibase_data, tran_data, data_total_len);
+ set_fs(old_fs);
+ filp_close(fp, 0);
+
+ return;
+}
+#endif ///< FEATURE_IBASE_OUT_ENABLE
+
+///***********************************************************************
+/// [function]: zet_mbase_save
+/// [parameters]: char *
+/// [return]: void
+///************************************************************************
+#ifdef FEATURE_MBASE_OUT_ENABLE
+static void zet_mbase_save(char *file_name)
+{
+ struct file *fp;
+ int data_total_len = (row * col * 2);
+
+ ///-------------------------------------------------------///
+ /// create the file that stores the mutual base data
+ ///-------------------------------------------------------///
+ fp = filp_open(file_name, O_RDWR | O_CREAT, 0644);
+ if(IS_ERR(fp))
+ {
+ printk("[ZET] : Failed to open %s\n", file_name);
+ return;
+ }
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ vfs_write(fp, tran_data, data_total_len, &(fp->f_pos));
+ memcpy(mbase_data, tran_data, data_total_len);
+ set_fs(old_fs);
+ filp_close(fp, 0);
+
+ return;
+}
+#endif ///< FEATURE_MBASE_OUT_ENABLE
+
+///***********************************************************************
+/// [function]: zet_information_save
+/// [parameters]: char *
+/// [return]: void
+///************************************************************************
+#ifdef FEATURE_INFO_OUT_EANBLE
+static void zet_information_save(char *file_name)
+{
+ struct file *fp;
+ int data_total_len = INFO_DATA_SIZE;
+
+ ///-------------------------------------------------------///
+ /// create the file that stores the mutual base data
+ ///-------------------------------------------------------///
+ fp = filp_open(file_name, O_RDWR | O_CREAT, 0644);
+ if(IS_ERR(fp))
+ {
+ printk("[ZET] : Failed to open %s\n", file_name);
+ return;
+ }
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ vfs_write(fp, tran_data, data_total_len, &(fp->f_pos));
+ memcpy(info_data, tran_data, data_total_len);
+ set_fs(old_fs);
+ filp_close(fp, 0);
+
+ return;
+}
+#endif ///< FEATURE_INFO_OUT_EANBLE
+
+///************************************************************************
+/// [function]: zet_dv_set_file_name
+/// [parameters]: void
+/// [return]: void
+///************************************************************************
+static void zet_dv_set_file_name(char *file_name)
+{
+ strcpy(driver_version, file_name);
+}
+
+///************************************************************************
+/// [function]: zet_fw_set_file_name
+/// [parameters]: void
+/// [return]: void
+///************************************************************************
+static void zet_fw_set_file_name(void)//char *file_name)
+{
+ char fwname[256] = {0};
+ wmt_ts_get_firmwname(fwname);
+ sprintf(fw_file_name,"/system/etc/firmware/%s",fwname);
+ //strcpy(fw_file_name, file_name);
+}
+
+///************************************************************************
+/// [function]: zet_mdev_set_file_name
+/// [parameters]: void
+/// [return]: void
+///************************************************************************
+static void zet_tran_type_set_file_name(char *file_name)
+{
+ strcpy(tran_type_mode_file_name, file_name);
+}
+
+
+///***********************************************************************
+/// [function]: zet_fw_size
+/// [parameters]: void
+/// [return]: void
+///************************************************************************
+static int zet_fw_size(void)
+{
+ int flash_total_len = 0x8000;
+
+ switch(ic_model)
+ {
+ case ZET6221:
+ flash_total_len = 0x4000;
+ break;
+ case ZET6223:
+ flash_total_len = 0x10000;
+ break;
+ case ZET6231:
+ case ZET6251:
+ default:
+ flash_total_len = 0x8000;
+ break;
+ }
+
+ return flash_total_len;
+}
+
+
+///***********************************************************************
+/// [function]: zet_fw_save
+/// [parameters]: file name
+/// [return]: void
+///************************************************************************
+static void zet_fw_save(char *file_name)
+{
+ struct file *fp;
+ int flash_total_len = 0;
+
+ fp = filp_open(file_name, O_RDWR | O_CREAT, 0644);
+ if(IS_ERR(fp))
+ {
+ printk("[ZET] : Failed to open %s\n", file_name);
+ return;
+ }
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ flash_total_len = zet_fw_size();
+ printk("[ZET] : flash_total_len = 0x%04x\n",flash_total_len );
+
+ vfs_write(fp, flash_buffer, flash_total_len, &(fp->f_pos));
+
+ set_fs(old_fs);
+
+ filp_close(fp, 0);
+
+
+ return;
+}
+
+///***********************************************************************
+/// [function]: zet_fw_load
+/// [parameters]: file name
+/// [return]: void
+///************************************************************************
+static void zet_fw_load(char *file_name)
+{
+ int file_length = 0;
+ struct file *fp;
+ loff_t *pos;
+
+ //printk("[ZET]: find %s\n", file_name);
+ fp = filp_open(file_name, O_RDONLY, 0644);
+ if(IS_ERR(fp))
+ {
+ //printk("[ZET]: No firmware file detected\n");
+ return;
+ }
+
+ ///----------------------------///
+ /// Load from file
+ ///----------------------------///
+ printk("[ZET]: Load from %s\n", file_name);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ /// Get file size
+ inode = fp->f_dentry->d_inode;
+ file_length = (int)inode->i_size;
+ //l_fwlen = file_length;
+
+ pos = &(fp->f_pos);
+
+ vfs_read(fp, &flash_buffer[0], file_length, pos);
+
+ //file_length
+ set_fs(old_fs);
+ filp_close(fp, 0);
+
+
+}
+
+///************************************************************************
+/// [function]: zet_fw_init
+/// [parameters]: void
+/// [return]: void
+///************************************************************************
+static void zet_fw_init(void)
+{
+ //int i;
+
+ if(flash_buffer == NULL)
+ {
+ flash_buffer = kmalloc(MAX_FLASH_BUF_SIZE, GFP_KERNEL);
+ }
+
+ ///---------------------------------------------///
+ /// Init the mutual dev buffer
+ ///---------------------------------------------///
+ if(mdev_data== NULL)
+ {
+ mdev_data = kmalloc(MDEV_MAX_DATA_SIZE, GFP_KERNEL);
+ }
+ if(idev_data== NULL)
+ {
+ idev_data = kmalloc(IDEV_MAX_DATA_SIZE, GFP_KERNEL);
+ }
+
+ if(mbase_data== NULL)
+ {
+ mbase_data = kmalloc(MBASE_MAX_DATA_SIZE, GFP_KERNEL);
+ }
+ if(ibase_data== NULL)
+ {
+ ibase_data = kmalloc(IBASE_MAX_DATA_SIZE, GFP_KERNEL);
+ }
+
+ if(tran_data == NULL)
+ {
+ tran_data = kmalloc(MBASE_MAX_DATA_SIZE, GFP_KERNEL);
+ }
+
+ if(info_data == NULL)
+ {
+ info_data = kmalloc(INFO_MAX_DATA_SIZE, GFP_KERNEL);
+ }
+
+ /*printk("[ZET]: Load from header\n");
+
+ if(ic_model == ZET6221)
+ {
+ for(i = 0 ; i < sizeof(zeitec_zet6221_firmware) ; i++)
+ {
+ flash_buffer[i] = zeitec_zet6221_firmware[i];
+ }
+ }
+ else if(ic_model == ZET6223)
+ {
+ for(i = 0 ; i < sizeof(zeitec_zet6223_firmware) ; i++)
+ {
+ flash_buffer[i] = zeitec_zet6223_firmware[i];
+ }
+ }
+ else if(ic_model == ZET6231)
+ {
+ for(i = 0 ; i < sizeof(zeitec_zet6231_firmware) ; i++)
+ {
+ flash_buffer[i] = zeitec_zet6231_firmware[i];
+ }
+ }
+ else if(ic_model == ZET6251)
+ {
+ for(i = 0 ; i < sizeof(zeitec_zet6251_firmware) ; i++)
+ {
+ flash_buffer[i] = zeitec_zet6251_firmware[i];
+ }
+ }
+
+ /// Load firmware from bin file
+ zet_fw_load(fw_file_name);*/
+}
+
+///************************************************************************
+/// [function]: zet_fw_exit
+/// [parameters]: void
+/// [return]: void
+///************************************************************************
+static void zet_fw_exit(void)
+{
+ ///---------------------------------------------///
+ /// free mdev_data
+ ///---------------------------------------------///
+ if(mdev_data!=NULL)
+ {
+ kfree(mdev_data);
+ mdev_data = NULL;
+ }
+
+ if(idev_data!=NULL)
+ {
+ kfree(idev_data);
+ idev_data = NULL;
+ }
+
+ if(mbase_data!=NULL)
+ {
+ kfree(mbase_data);
+ mbase_data = NULL;
+ }
+
+ if(ibase_data!=NULL)
+ {
+ kfree(ibase_data);
+ ibase_data = NULL;
+ }
+
+ if(tran_data != NULL)
+ {
+ kfree(tran_data);
+ tran_data = NULL;
+ }
+
+ if(info_data != NULL)
+ {
+ kfree(info_data);
+ info_data = NULL;
+ }
+
+
+ ///---------------------------------------------///
+ /// free flash buffer
+ ///---------------------------------------------///
+ if(flash_buffer!=NULL)
+ {
+ kfree(flash_buffer);
+ flash_buffer = NULL;
+}
+
+}
+
+///************************************************************************
+/// [function]: zet_fops_open
+/// [parameters]: file
+/// [return]: int
+///************************************************************************
+static int zet_fops_open(struct inode *inode, struct file *file)
+{
+ int subminor;
+ int ret = 0;
+ struct i2c_client *client;
+ struct i2c_adapter *adapter;
+ struct i2c_dev *i2c_dev;
+
+ subminor = iminor(inode);
+ printk("[ZET] : ZET_FOPS_OPEN , subminor=%d\n",subminor);
+
+ i2c_dev = zet622x_i2c_dev_get_by_minor(subminor);
+ if (!i2c_dev)
+ {
+ printk("error i2c_dev\n");
+ return -ENODEV;
+ }
+
+ adapter = i2c_get_adapter(i2c_dev->adap->nr);
+ if(!adapter)
+ {
+ return -ENODEV;
+ }
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+
+ if(!client)
+ {
+ i2c_put_adapter(adapter);
+ ret = -ENOMEM;
+ }
+ snprintf(client->name, I2C_NAME_SIZE, "pctp_i2c_ts%d", adapter->nr);
+ //client->driver = &zet622x_i2c_driver;
+ client->driver = this_client->driver;
+ client->adapter = adapter;
+ file->private_data = client;
+
+ return 0;
+}
+
+
+///************************************************************************
+/// [function]: zet_fops_release
+/// [parameters]: inode, file
+/// [return]: int
+///************************************************************************
+static int zet_fops_release (struct inode *inode, struct file *file)
+{
+ struct i2c_client *client = file->private_data;
+
+ printk("[ZET] : zet_fops_release -> line : %d\n",__LINE__ );
+
+ i2c_put_adapter(client->adapter);
+ kfree(client);
+ file->private_data = NULL;
+ return 0;
+}
+
+///************************************************************************
+/// [function]: zet_fops_read
+/// [parameters]: file, buf, count, ppos
+/// [return]: size_t
+///************************************************************************
+static ssize_t zet_fops_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int i;
+ int iCnt = 0;
+ char str[256];
+ int len = 0;
+
+ printk("[ZET] : zet_fops_read -> line : %d\n",__LINE__ );
+
+ ///-------------------------------///
+ /// Print message
+ ///-------------------------------///
+ sprintf(str, "Please check \"%s\"\n", fw_file_name);
+ len = strlen(str);
+
+ ///-------------------------------///
+ /// if read out
+ ///-------------------------------///
+ if(data_offset >= len)
+ {
+ return 0;
+ }
+
+ for(i = 0 ; i < count-1 ; i++)
+ {
+ buf[i] = str[data_offset];
+ buf[i+1] = 0;
+ iCnt++;
+ data_offset++;
+ if(data_offset >= len)
+ {
+ break;
+ }
+ }
+
+ ///-------------------------------///
+ /// Save file
+ ///-------------------------------///
+ if(data_offset == len)
+ {
+ zet_fw_save(fw_file_name);
+ }
+ return iCnt;
+}
+
+///************************************************************************
+/// [function]: zet_fops_write
+/// [parameters]: file, buf, count, ppos
+/// [return]: size_t
+///************************************************************************
+static ssize_t zet_fops_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ printk("[ZET]: zet_fops_write -> %s\n", buf);
+ data_offset = 0;
+ return count;
+}
+
+///************************************************************************
+/// [function]: ioctl
+/// [parameters]: file , cmd , arg
+/// [return]: long
+///************************************************************************
+static long zet_fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg )
+{
+ u8 __user * user_buf = (u8 __user *) arg;
+
+ u8 buf[IOCTL_MAX_BUF_SIZE];
+ int data_size;
+
+ if(copy_from_user(buf, user_buf, IOCTL_MAX_BUF_SIZE))
+ {
+ printk("[ZET]: zet_ioctl: copy_from_user fail\n");
+ return 0;
+ }
+
+ printk("[ZET]: zet_ioctl -> cmd = %d, %02x, %02x\n", cmd, buf[0], buf[1]);
+
+ if(cmd == ZET_IOCTL_CMD_FLASH_READ)
+ {
+ printk("[ZET]: zet_ioctl -> ZET_IOCTL_CMD_FLASH_DUMP cmd = %d, file=%s\n", cmd, (char *)buf);
+ ioctl_action |= IOCTL_ACTION_FLASH_DUMP;
+ }
+ else if(cmd == ZET_IOCTL_CMD_FLASH_WRITE)
+ {
+ printk("[ZET]: zet_ioctl -> ZET_IOCTL_CMD_FLASH_WRITE cmd = %d\n", cmd);
+ { //upload bin to flash_buffer
+ char fw_name[64];
+ sprintf(fw_name, "%szet62xx.bin", tran_type_mode_file_name);
+ zet_fw_load(fw_name);
+ }
+ zet622x_resume_downloader(this_client);
+ }
+ else if(cmd == ZET_IOCTL_CMD_RST)
+ {
+ printk("[ZET]: zet_ioctl -> ZET_IOCTL_CMD_RST cmd = %d\n", cmd);
+ //ctp_reset();
+ wmt_rst_output(1);
+
+ wmt_rst_output(0);
+ msleep(20);
+ wmt_rst_output(1);
+
+ transfer_type = TRAN_TYPE_DYNAMIC;
+ }
+ else if(cmd == ZET_IOCTL_CMD_RST_HIGH)
+ {
+ wmt_rst_output(1);
+ }
+ else if(cmd == ZET_IOCTL_CMD_RST_LOW)
+ {
+ wmt_rst_output(0);
+ }
+ else if(cmd == ZET_IOCTL_CMD_MDEV)
+ {
+ ///---------------------------------------------------///
+ /// set mutual dev mode
+ ///---------------------------------------------------///
+ zet622x_ts_set_transfer_type(TRAN_TYPE_MUTUAL_SCAN_DEV);
+ transfer_type = TRAN_TYPE_MUTUAL_SCAN_DEV;
+
+ }
+ else if(cmd == ZET_IOCTL_CMD_IBASE)
+ {
+ ///---------------------------------------------------///
+ /// set initial base mode
+ ///---------------------------------------------------///
+ zet622x_ts_set_transfer_type(TRAN_TYPE_INIT_SCAN_BASE);
+ transfer_type = TRAN_TYPE_INIT_SCAN_BASE;
+
+ }
+#ifdef FEATURE_IDEV_OUT_ENABLE
+ else if(cmd == ZET_IOCTL_CMD_IDEV)
+ {
+ ///---------------------------------------------------///
+ /// set initial dev mode
+ ///---------------------------------------------------///
+ zet622x_ts_set_transfer_type(TRAN_TYPE_INIT_SCAN_DEV);
+ transfer_type = TRAN_TYPE_INIT_SCAN_DEV;
+
+ }
+#endif ///< FEATURE_IDEV_OUT_ENABLE
+#ifdef FEATURE_MBASE_OUT_ENABLE
+ else if(cmd == ZET_IOCTL_CMD_MBASE)
+ {
+ ///---------------------------------------------------///
+ /// set Mutual Base mode
+ ///---------------------------------------------------///
+ zet622x_ts_set_transfer_type(TRAN_TYPE_MUTUAL_SCAN_BASE);
+ transfer_type = TRAN_TYPE_MUTUAL_SCAN_BASE;
+
+ }
+#endif ///< FEATURE_MBASE_OUT_ENABLE
+ else if(cmd == ZET_IOCTL_CMD_DYNAMIC)
+ {
+ zet622x_ts_set_transfer_type(TRAN_TYPE_DYNAMIC);
+ transfer_type = TRAN_TYPE_DYNAMIC;
+ }
+ else if(cmd == ZET_IOCTL_CMD_FW_FILE_PATH_GET)
+ {
+ memset(buf, 0x00, 64);
+ strcpy(buf, fw_file_name);
+ printk("[ZET]: zet_ioctl: Get FW_FILE_NAME = %s\n", buf);
+ }
+ else if(cmd == ZET_IOCTL_CMD_FW_FILE_PATH_SET)
+ {
+ strcpy(fw_file_name, buf);
+ printk("[ZET]: zet_ioctl: set FW_FILE_NAME = %s\n", buf);
+
+ }
+ else if(cmd == ZET_IOCTL_CMD_MDEV_GET)
+ {
+ data_size = (row+2)*(col+2);
+ memcpy(buf, mdev_data, data_size);
+ printk("[ZET]: zet_ioctl: Get MDEV data size=%d\n", data_size);
+ }
+ else if(cmd == ZET_IOCTL_CMD_TRAN_TYPE_PATH_SET)
+ {
+ strcpy(tran_type_mode_file_name, buf);
+ printk("[ZET]: zet_ioctl: Set ZET_IOCTL_CMD_TRAN_TYPE_PATH_ = %s\n", buf);
+ }
+ else if(cmd == ZET_IOCTL_CMD_TRAN_TYPE_PATH_GET)
+ {
+ memset(buf, 0x00, 64);
+ strcpy(buf, tran_type_mode_file_name);
+ printk("[ZET]: zet_ioctl: Get ZET_IOCTL_CMD_TRAN_TYPE_PATH = %s\n", buf);
+ }
+ else if(cmd == ZET_IOCTL_CMD_IDEV_GET)
+ {
+ data_size = (row + col);
+ memcpy(buf, idev_data, data_size);
+ printk("[ZET]: zet_ioctl: Get IDEV data size=%d\n", data_size);
+ }
+ else if(cmd == ZET_IOCTL_CMD_IBASE_GET)
+ {
+ data_size = (row + col)*2;
+ memcpy(buf, ibase_data, data_size);
+ printk("[ZET]: zet_ioctl: Get IBASE data size=%d\n", data_size);
+ }
+ else if(cmd == ZET_IOCTL_CMD_MBASE_GET)
+ {
+ data_size = (row*col*2);
+ if(data_size > IOCTL_MAX_BUF_SIZE)
+ {
+ data_size = IOCTL_MAX_BUF_SIZE;
+ }
+ memcpy(buf, mbase_data, data_size);
+ printk("[ZET]: zet_ioctl: Get MBASE data size=%d\n", data_size);
+ }
+ else if(cmd == ZET_IOCTL_CMD_INFO_SET)
+ {
+ printk("[ZET]: zet_ioctl: ZET_IOCTL_CMD_INFO_SET\n");
+ zet622x_ts_set_info_type();
+ }
+ else if(cmd == ZET_IOCTL_CMD_INFO_GET)
+ {
+ data_size = INFO_DATA_SIZE;
+ memcpy(buf, info_data, data_size);
+ printk("[ZET]: zet_ioctl: Get INFO data size=%d,IC: %x,X:%d,Y:%d\n", data_size, info_data[0], info_data[13], info_data[14]);
+ }
+ else if(cmd == ZET_IOCTL_CMD_TRACE_X_SET)
+ {
+ printk("[ZET]: zet_ioctl: ZET_IOCTL_CMD_TRACE_X_SET\n");
+ }
+ else if(cmd == ZET_IOCTL_CMD_TRACE_X_GET)
+ {
+ printk("[ZET]: zet_ioctl: Get TRACEX data\n");
+ }
+ else if(cmd == ZET_IOCTL_CMD_TRACE_Y_SET)
+ {
+ printk("[ZET]: zet_ioctl: ZET_IOCTL_CMD_TRACE_Y_SET\n");
+ }
+ else if(cmd == ZET_IOCTL_CMD_TRACE_Y_GET)
+ {
+ printk("[ZET]: zet_ioctl: Get TRACEY data \n");
+ }
+ else if(cmd == ZET_IOCTL_CMD_DRIVER_VER_GET)
+ {
+ memset(buf, 0x00, 64);
+ strcpy(buf, driver_version);
+ printk("[ZET]: zet_ioctl: Get DRIVER_VERSION = %s\n", buf);
+ printk("[ZET]: zet_ioctl: Get SVN = %s\n", DRIVER_VERSION);
+ }
+ else if(cmd == ZET_IOCTL_CMD_MBASE_EXTERN_GET)
+ {
+ data_size = (row*col*2) - IOCTL_MAX_BUF_SIZE;
+ if(data_size < 1)
+ {
+ data_size = 1;
+ }
+ memcpy(buf, (mbase_data+IOCTL_MAX_BUF_SIZE), data_size);
+ printk("[ZET]: zet_ioctl: Get MBASE extern data size=%d\n", data_size);
+ }
+
+ if(copy_to_user(user_buf, buf, IOCTL_MAX_BUF_SIZE))
+ {
+ printk("[ZET]: zet_ioctl: copy_to_user fail\n");
+ return 0;
+ }
+
+ return 0;
+}
+
+///************************************************************************
+/// file_operations
+///************************************************************************
+static const struct file_operations zet622x_ts_fops =
+{
+ .owner = THIS_MODULE,
+ .open = zet_fops_open,
+ .read = zet_fops_read,
+ .write = zet_fops_write,
+ .unlocked_ioctl = zet_fops_ioctl,
+ .compat_ioctl = zet_fops_ioctl,
+ .release = zet_fops_release,
+};
+
+static int zet6221_ts_probe(struct i2c_client *client/*, const struct i2c_device_id *id*/)
+{
+ int result = -1;
+ int count = 0;
+ int download_count = 0;
+ int download_ok = 0;
+ struct input_dev *input_dev;
+ struct device *dev;
+
+
+ struct zet6221_tsdrv *zet6221_ts;
+
+ dbg( "[TS] zet6221_ts_probe \n");
+
+ zet6221_ts = kzalloc(sizeof(struct zet6221_tsdrv), GFP_KERNEL);
+ l_ts = zet6221_ts;
+ zet6221_ts->i2c_ts = client;
+ //zet6221_ts->gpio = TS_INT_GPIO; /*s3c6410*/
+ //zet6221_ts->gpio = TS1_INT_GPIO;
+
+ this_client = client;
+
+ i2c_set_clientdata(client, zet6221_ts);
+
+ //client->driver = &zet6221_ts_driver;
+ ts_wq = create_singlethread_workqueue("zet6221ts_wq");
+ if (!ts_wq)
+ {
+ errlog("Failed to create workqueue!\n");
+ goto err_create_wq;
+ }
+
+ INIT_WORK(&zet6221_ts->work1, zet6221_ts_work);
+
+ input_dev = input_allocate_device();
+ if (!input_dev || !zet6221_ts) {
+ result = -ENOMEM;
+ goto fail_alloc_mem;
+ }
+
+ //i2c_set_clientdata(client, zet6221_ts);
+
+ input_dev->name = MJ5_TS_NAME;
+ input_dev->phys = "zet6221_touch/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0002;
+ input_dev->id.version = 0x0100;
+//bootloader
+ zet622x_ts_option(client);
+ msleep(100);
+
+ download_count = 0;
+ download_ok = 0;
+ zet_fw_init();
+ do{
+ if (zet6221_load_fw())
+ {
+ errlog("Can't load the firmware of zet62xx!\n");
+ } else {
+ zet6221_downloader(client);
+ //ctp_reset(); //cancel it? need to check
+ }
+ udelay(100);
+
+ count=0;
+ do{
+ ctp_reset();
+
+ if(zet6221_ts_get_report_mode_t(client)==0) //get IC info by delay
+ {
+ ResolutionX = X_MAX;
+ ResolutionY = Y_MAX;
+ FingerNum = FINGER_NUMBER;
+ KeyNum = KEY_NUMBER;
+ if(KeyNum==0)
+ bufLength = 3+4*FingerNum;
+ else
+ bufLength = 3+4*FingerNum+1;
+ errlog("[warning] zet6221_ts_get_report_mode_t report error!!use default value\n");
+ }else
+ {
+ if(zet6221_ts_version()==1) // zet6221_ts_version() depends on zet6221_downloader()
+ // cancel download firmware, need to comment it.
+ {
+ dbg("get report mode ok!\n");
+ download_ok = 1;
+ }
+ }
+ count++;
+ }while(count<REPORT_POLLING_TIME && download_ok != 1 );
+ download_count++;
+ }while( download_count < RETRY_DOWNLOAD_TIMES && download_ok != 1 );
+
+ errlog( "ResolutionX=%d ResolutionY=%d FingerNum=%d KeyNum=%d\n",ResolutionX,ResolutionY,FingerNum,KeyNum);
+
+ input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ if (wmt_ts_get_lcdexchg()) {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ResolutionX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ResolutionY, 0, 0);
+ } else {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ResolutionY, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ResolutionX, 0, 0);
+ }
+
+ set_bit(KEY_BACK, input_dev->keybit);
+ set_bit(KEY_HOME, input_dev->keybit);
+ set_bit(KEY_MENU, input_dev->keybit);
+
+ //*******************************add 2013-1-10
+ set_bit(ABS_MT_TRACKING_ID, input_dev->absbit);
+ //set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
+ input_set_abs_params(input_dev,ABS_MT_TRACKING_ID, 0, FingerNum, 0, 0);
+ //input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, P_MAX, 0, 0);
+ //set_bit(BTN_TOUCH, input_dev->keybit);
+
+ #ifdef MT_TYPE_B
+ input_mt_init_slots(input_dev, FingerNum);
+ #endif
+ //set_bit(KEY_SEARCH, input_dev->keybit);
+
+ //input_dev->evbit[0] = BIT(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ //input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ result = input_register_device(input_dev);
+ if (result)
+ goto fail_ip_reg;
+
+ zet6221_ts->input = input_dev;
+
+ input_set_drvdata(zet6221_ts->input, zet6221_ts);
+ mutex_init(&i2c_mutex);
+ wake_lock_init(&downloadWakeLock, WAKE_LOCK_SUSPEND, "resume_download");
+ zet6221_ts->queue = create_singlethread_workqueue("ts_check_charge_queue");
+ INIT_DELAYED_WORK(&zet6221_ts->work, polling_timer_func);
+
+ //setup_timer(&zet6221_ts->polling_timer, polling_timer_func, (unsigned long)zet6221_ts);
+ //mod_timer(&zet6221_ts->polling_timer,jiffies + msecs_to_jiffies(TIME_CHECK_CHARGE));
+
+
+ //s3c6410
+ //result = gpio_request(zet6221_ts->gpio, "GPN");
+ wmt_set_gpirq(IRQ_TYPE_EDGE_FALLING);
+ wmt_disable_gpirq();
+ /*result = gpio_request(zet6221_ts->gpio, "GPN");
+ if (result)
+ goto gpio_request_fail;
+ */
+
+ zet6221_ts->irq = wmt_get_tsirqnum();//gpio_to_irq(zet6221_ts->gpio);
+ dbg( "[TS] zet6221_ts_probe.gpid_to_irq [zet6221_ts->irq=%d]\n",zet6221_ts->irq);
+
+ result = request_irq(zet6221_ts->irq, zet6221_ts_interrupt,IRQF_SHARED /*IRQF_TRIGGER_FALLING*/,
+ ZET_TS_ID_NAME, zet6221_ts);
+ if (result)
+ {
+ errlog("Can't alloc ts irq=%d\n", zet6221_ts->irq);
+ goto request_irq_fail;
+ }
+
+
+ ///-----------------------------------------------///
+ /// Set the default firmware bin file name & mutual dev file name
+ ///-----------------------------------------------///
+ zet_dv_set_file_name(DRIVER_VERSION);
+ zet_fw_set_file_name();//FW_FILE_NAME);
+ zet_tran_type_set_file_name(TRAN_MODE_FILE_PATH);
+
+ ///---------------------------------///
+ /// Set file operations
+ ///---------------------------------///
+ result = register_chrdev(I2C_MAJOR, "zet_i2c_ts", &zet622x_ts_fops);
+ if(result)
+ {
+ printk(KERN_ERR "%s:register chrdev failed\n",__FILE__);
+ goto fail_register_chrdev;
+ }
+ ///---------------------------------///
+ /// Create device class
+ ///---------------------------------///
+ i2c_dev_class = class_create(THIS_MODULE,"zet_i2c_dev");
+ if(IS_ERR(i2c_dev_class))
+ {
+ result = PTR_ERR(i2c_dev_class);
+ goto fail_create_class;
+ }
+ ///--------------------------------------------///
+ /// Get a free i2c dev
+ ///--------------------------------------------///
+ zet_i2c_dev = zet622x_i2c_get_free_dev(client->adapter);
+ if(IS_ERR(zet_i2c_dev))
+ {
+ result = PTR_ERR(zet_i2c_dev);
+ goto fail_get_free_dev;
+ }
+ dev = device_create(i2c_dev_class, &client->adapter->dev,
+ MKDEV(I2C_MAJOR,client->adapter->nr), NULL, "zet62xx_ts%d", client->adapter->nr);
+ if(IS_ERR(dev))
+ {
+ result = PTR_ERR(dev);
+ goto fail_create_device;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ zet6221_ts->early_suspend.suspend = ts_early_suspend,
+ zet6221_ts->early_suspend.resume = ts_late_resume,
+ zet6221_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;//,EARLY_SUSPEND_LEVEL_DISABLE_FB + 2;
+ register_early_suspend(&zet6221_ts->early_suspend);
+#endif
+ //disable_irq(zet6221_ts->irq);
+ ctp_reset();
+ wmt_enable_gpirq();
+ queue_delayed_work(zet6221_ts->queue, &zet6221_ts->work, msecs_to_jiffies(TIME_CHECK_CHARGE));
+ //mod_timer(&zet6221_ts->polling_timer,jiffies + msecs_to_jiffies(TIME_CHECK_CHARGE));
+ dbg("ok\n");
+ return 0;
+
+fail_create_device:
+ kfree(zet_i2c_dev);
+fail_get_free_dev:
+ class_destroy(i2c_dev_class);
+fail_create_class:
+ unregister_chrdev(I2C_MAJOR, "zet_i2c_ts");
+fail_register_chrdev:
+ free_irq(zet6221_ts->irq, zet6221_ts);
+request_irq_fail:
+ destroy_workqueue(zet6221_ts->queue);
+ cancel_delayed_work_sync(&zet6221_ts->work);
+ //gpio_free(zet6221_ts->gpio);
+//gpio_request_fail:
+ free_irq(zet6221_ts->irq, zet6221_ts);
+ wake_lock_destroy(&downloadWakeLock);
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+fail_ip_reg:
+fail_alloc_mem:
+ input_free_device(input_dev);
+ destroy_workqueue(ts_wq);
+ cancel_work_sync(&zet6221_ts->work1);
+ zet_fw_exit();
+err_create_wq:
+ kfree(zet6221_ts);
+ return result;
+}
+
+static int zet6221_ts_remove(void /*struct i2c_client *dev*/)
+{
+ struct zet6221_tsdrv *zet6221_ts = l_ts;//i2c_get_clientdata(dev);
+
+ //del_timer(&zet6221_ts->polling_timer);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&zet6221_ts->early_suspend);
+#endif
+ wmt_disable_gpirq();
+ device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR,this_client->adapter->nr));
+ kfree(zet_i2c_dev);
+ class_destroy(i2c_dev_class);
+ unregister_chrdev(I2C_MAJOR, "zet_i2c_ts");
+ free_irq(zet6221_ts->irq, zet6221_ts);
+ //gpio_free(zet6221_ts->gpio);
+ //del_timer_sync(&zet6221_ts->polling_timer);
+ destroy_workqueue(zet6221_ts->queue);
+ cancel_delayed_work_sync(&zet6221_ts->work);
+ input_unregister_device(zet6221_ts->input);
+ wake_lock_destroy(&downloadWakeLock);
+ cancel_work_sync(&zet6221_ts->work1);
+ destroy_workqueue(ts_wq);
+ zet_fw_exit();
+ kfree(zet6221_ts);
+
+ return 0;
+}
+
+static int wmt_wakeup_bl_notify(struct notifier_block *nb, unsigned long event,
+ void *dummy)
+{
+ //printk("get notify\n");
+ switch (event) {
+ case BL_CLOSE:
+ l_suspend = 1;
+ //printk("\nclose backlight\n\n");
+ //printk("disable irq\n\n");
+ wmt_disable_gpirq();
+ break;
+ case BL_OPEN:
+ l_suspend = 0;
+ //printk("\nopen backlight\n\n");
+ //printk("enable irq\n\n");
+ wmt_enable_gpirq();
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wmt_bl_notify = {
+ .notifier_call = wmt_wakeup_bl_notify,
+};
+
+static int zet6221_ts_init(void)
+{
+ //u8 ts_data[70];
+ //int ret;
+
+ /*ctp_reset();
+ memset(ts_data,0,70);
+ ret=zet6221_i2c_read_tsdata(ts_get_i2c_client(), ts_data, 8);
+ if (ret <= 0)
+ {
+ dbg("Can't find zet6221!\n");
+ return -1;
+ }
+ if (!zet6221_is_ts(ts_get_i2c_client()))
+ {
+ dbg("isn't zet6221!\n");
+ return -1;
+ }*/
+ if (zet6221_ts_probe(ts_get_i2c_client()))
+ {
+ return -1;
+ }
+ if (earlysus_en)
+ register_bl_notifier(&wmt_bl_notify);
+ //i2c_add_driver(&zet6221_ts_driver);
+ return 0;
+}
+//module_init(zet6221_ts_init);
+
+static void zet6221_ts_exit(void)
+{
+ zet6221_ts_remove();
+ if (earlysus_en)
+ unregister_bl_notifier(&wmt_bl_notify);
+ //i2c_del_driver(&zet6221_ts_driver);
+}
+//module_exit(zet6221_ts_exit);
+
+void zet6221_set_ts_mode(u8 mode)
+{
+ dbg( "[Touch Screen]ts mode = %d \n", mode);
+}
+//EXPORT_SYMBOL_GPL(zet6221_set_ts_mode);
+
+struct wmtts_device zet6221_tsdev = {
+ .driver_name = WMT_TS_I2C_NAME,
+ .ts_id = "ZET62",
+ .init = zet6221_ts_init,
+ .exit = zet6221_ts_exit,
+ .suspend = zet_ts_suspend,
+ .resume = zet_ts_resume,
+};
+
+
+MODULE_DESCRIPTION("ZET6221 I2C Touch Screen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/zet6221_ts/zet6221_ts.h b/drivers/input/touchscreen/zet6221_ts/zet6221_ts.h
new file mode 100755
index 00000000..671bb29d
--- /dev/null
+++ b/drivers/input/touchscreen/zet6221_ts/zet6221_ts.h
@@ -0,0 +1,6 @@
+#ifndef ZET6221_TSH_201010191758
+#define ZET6221_TSH_201010191758
+
+extern void zet6221_set_tskey(int index,int key);
+
+#endif
diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c
new file mode 100644
index 00000000..bf0869a7
--- /dev/null
+++ b/drivers/input/touchscreen/zylonite-wm97xx.c
@@ -0,0 +1,232 @@
+/*
+ * zylonite-wm97xx.c -- Zylonite Continuous Touch screen driver
+ *
+ * Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ * Andrew Zabolotny <zap@homelink.ru>
+ *
+ * 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.
+ *
+ * Notes:
+ * This is a wm97xx extended touch driver supporting interrupt driven
+ * and continuous operation on Marvell Zylonite development systems
+ * (which have a WM9713 on board).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/wm97xx.h>
+
+#include <mach/hardware.h>
+#include <mach/mfp.h>
+#include <mach/regs-ac97.h>
+
+struct continuous {
+ u16 id; /* codec id */
+ u8 code; /* continuous code */
+ u8 reads; /* number of coord reads per read cycle */
+ u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+ { WM9713_ID2, 0, WM_READS(94), 94 },
+ { WM9713_ID2, 1, WM_READS(120), 120 },
+ { WM9713_ID2, 2, WM_READS(154), 154 },
+ { WM9713_ID2, 3, WM_READS(188), 188 },
+};
+
+/* continuous speed index */
+static int sp_idx;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 200;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+
+/* flush AC97 slot 5 FIFO machines */
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+ int i;
+
+ msleep(1);
+
+ for (i = 0; i < 16; i++)
+ MODR;
+}
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+ int reads = 0;
+ static u16 last, tries;
+
+ /* When the AC97 queue has been drained we need to allow time
+ * to buffer up samples otherwise we end up spinning polling
+ * for samples. The controller can't have a suitably low
+ * threshold set to use the notifications it gives.
+ */
+ msleep(1);
+
+ if (tries > 5) {
+ tries = 0;
+ return RC_PENUP;
+ }
+
+ x = MODR;
+ if (x == last) {
+ tries++;
+ return RC_AGAIN;
+ }
+ last = x;
+ do {
+ if (reads)
+ x = MODR;
+ y = MODR;
+ if (pressure)
+ p = MODR;
+
+ dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n",
+ x, y, p);
+
+ /* are samples valid */
+ if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+ (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+ (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
+ goto up;
+
+ /* coordinate is good */
+ tries = 0;
+ input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
+ input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
+ input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+ input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
+ input_sync(wm->input_dev);
+ reads++;
+ } while (reads < cinfo[sp_idx].reads);
+up:
+ return RC_PENDOWN | RC_AGAIN;
+}
+
+static int wm97xx_acc_startup(struct wm97xx *wm)
+{
+ int idx;
+
+ /* check we have a codec */
+ if (wm->ac97 == NULL)
+ return -ENODEV;
+
+ /* Go you big red fire engine */
+ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+ if (wm->id != cinfo[idx].id)
+ continue;
+ sp_idx = idx;
+ if (cont_rate <= cinfo[idx].speed)
+ break;
+ }
+ wm->acc_rate = cinfo[sp_idx].code;
+ wm->acc_slot = ac97_touch_slot;
+ dev_info(wm->dev,
+ "zylonite accelerated touchscreen driver, %d samples/sec\n",
+ cinfo[sp_idx].speed);
+
+ return 0;
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+ if (enable)
+ enable_irq(wm->pen_irq);
+ else
+ disable_irq_nosync(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops zylonite_mach_ops = {
+ .acc_enabled = 1,
+ .acc_pen_up = wm97xx_acc_pen_up,
+ .acc_pen_down = wm97xx_acc_pen_down,
+ .acc_startup = wm97xx_acc_startup,
+ .irq_enable = wm97xx_irq_enable,
+ .irq_gpio = WM97XX_GPIO_2,
+};
+
+static int zylonite_wm97xx_probe(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+ int gpio_touch_irq;
+
+ if (cpu_is_pxa320())
+ gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15);
+ else
+ gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26);
+
+ wm->pen_irq = gpio_to_irq(gpio_touch_irq);
+ irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);
+
+ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_STICKY,
+ WM97XX_GPIO_WAKE);
+ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
+ WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_NOTSTICKY,
+ WM97XX_GPIO_NOWAKE);
+
+ return wm97xx_register_mach_ops(wm, &zylonite_mach_ops);
+}
+
+static int zylonite_wm97xx_remove(struct platform_device *pdev)
+{
+ struct wm97xx *wm = platform_get_drvdata(pdev);
+
+ wm97xx_unregister_mach_ops(wm);
+
+ return 0;
+}
+
+static struct platform_driver zylonite_wm97xx_driver = {
+ .probe = zylonite_wm97xx_probe,
+ .remove = zylonite_wm97xx_remove,
+ .driver = {
+ .name = "wm97xx-touch",
+ },
+};
+module_platform_driver(zylonite_wm97xx_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite");
+MODULE_LICENSE("GPL");