summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/cyttsp_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/cyttsp_core.c')
-rwxr-xr-xdrivers/input/touchscreen/cyttsp_core.c2257
1 files changed, 2257 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
new file mode 100755
index 00000000000..59d6c091783
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -0,0 +1,2257 @@
+/* Source for:
+ * Cypress TrueTouch(TM) Standard Product touchscreen driver.
+ * drivers/input/touchscreen/cyttsp_core.c
+ *
+ * Copyright (C) 2009-2011 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/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>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/cyttsp.h>
+#include <linux/ctype.h>
+#include <linux/regulator/consumer.h>
+#include "cyttsp_core.h"
+
+#define DBG(x)
+#define DBG2(x)
+#define DBG3(x)
+
+/* rely on kernel input.h to define Multi-Touch capability */
+#ifndef ABS_MT_TRACKING_ID
+/* define only if not defined already by system; */
+/* value based on linux kernel 2.6.30.10 */
+#define ABS_MT_TRACKING_ID (ABS_MT_BLOB_ID + 1)
+#endif /* ABS_MT_TRACKING_ID */
+
+#define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(28))
+/* Bootloader File 0 offset */
+#define CY_BL_FILE0 0x00
+/* Bootloader command directive */
+#define CY_BL_CMD 0xFF
+/* Bootloader Enter Loader mode */
+#define CY_BL_ENTER 0x38
+/* Bootloader Write a Block */
+#define CY_BL_WRITE_BLK 0x39
+/* Bootloader Terminate Loader mode */
+#define CY_BL_TERMINATE 0x3B
+/* Bootloader Exit and Verify Checksum command */
+#define CY_BL_EXIT 0xA5
+/* Bootloader default keys */
+#define CY_BL_KEY0 0
+#define CY_BL_KEY1 1
+#define CY_BL_KEY2 2
+#define CY_BL_KEY3 3
+#define CY_BL_KEY4 4
+#define CY_BL_KEY5 5
+#define CY_BL_KEY6 6
+#define CY_BL_KEY7 7
+
+#define CY_DIFF(m, n) ((m) != (n))
+#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 IS_BAD_PKT(x) ((x) & 0x20)
+#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) {typeof(x) tmp; tmp = (x); (x) = (y); (y) = tmp; }
+#define INVERT_X(x, xmax) ((xmax) - (x))
+#define INVERT_Y(y, ymax) ((ymax) - (y))
+#define SET_HSTMODE(reg, mode) ((reg) & (mode))
+#define GET_HSTMODE(reg) ((reg & 0x70) >> 4)
+#define GET_BOOTLOADERMODE(reg) ((reg & 0x10) >> 4)
+
+/* Watchdog timeout to check if device is reset and no interrupts running */
+#define CY_WDG_TIMEOUT msecs_to_jiffies(3000)
+#define CY_MODE_ERROR(ps, hst_mode, tt_mode) \
+ ((ps == CY_ACTIVE_STATE && GET_HSTMODE(hst_mode) != CY_OPERATE_MODE) ||\
+ GET_BOOTLOADERMODE(tt_mode))
+
+/* 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
+/*
+ * maximum number of concurrent touches
+ * (only CY_NUM_MT_TCH_ID have coord data)
+ */
+#define CY_MAX_TCH 10
+/*
+ * maximum number of touch reports with
+ * current touches=0 before performing Driver reset
+ */
+#define CY_MAX_NTCH 10
+
+#define CY_NTCH 0 /* lift off */
+#define CY_TCH 1 /* touch down */
+#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_SCN_TYP 0x1C
+#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_DELAY_DFLT 10 /* ms */
+#define CY_DELAY_MAX (500/CY_DELAY_DFLT) /* half second */
+#define CY_DELAY_SYSINFO 20 /* ms */
+#define CY_MODE_CHANGE_DELAY 30 /* ms */
+#define CY_DELAY_BL 300
+#define CY_DELAY_DNLOAD 100 /* ms */
+#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
+#define CY_NUM_KEY 8
+
+/* TrueTouch Standard Product Gen3 (Txx3xx) interface definition */
+struct cyttsp_xydata {
+ 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;
+};
+
+struct cyttsp_xydata_gen2 {
+ 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;
+};
+
+/* TrueTouch Standard Product Gen2 (Txx2xx) interface definition */
+enum cyttsp_gen2_std {
+ CY_GEN2_NOTOUCH = 0x03, /* Both touches removed */
+ CY_GEN2_GHOST = 0x02, /* ghost */
+ CY_GEN2_2TOUCH = 0x03, /* 2 touch; no ghost */
+ CY_GEN2_1TOUCH = 0x01, /* 1 touch only */
+ CY_GEN2_TOUCH2 = 0x01, /* 1st touch removed; 2nd touch remains */
+};
+
+/* TTSP System Information interface definition */
+struct cyttsp_sysinfo_data {
+ u8 hst_mode;
+ u8 mfg_stat;
+ u8 mfg_cmd;
+ u8 cid[3];
+ u8 tt_undef1;
+ u8 uid[8];
+ u8 bl_verh;
+ u8 bl_verl;
+ u8 tts_verh;
+ u8 tts_verl;
+ u8 app_idh;
+ u8 app_idl;
+ u8 app_verh;
+ u8 app_verl;
+ u8 tt_undef[5];
+ u8 scn_typ; /* Gen3 only: scan type [0:Mutual, 1:Self] */
+ 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;
+};
+
+#define cyttsp_wake_data cyttsp_xydata
+
+struct cyttsp {
+ struct device *pdev;
+ int irq;
+ struct input_dev *input;
+ struct work_struct work;
+ struct timer_list timer;
+ struct mutex mutex;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ char phys[32];
+ struct cyttsp_platform_data *platform_data;
+ struct cyttsp_bootloader_data bl_data;
+ struct cyttsp_sysinfo_data sysinfo_data;
+ u8 num_prv_st_tch;
+ u16 act_trk[CY_NUM_TRK_ID];
+ u16 prv_mt_tch[CY_NUM_MT_TCH_ID];
+ u16 prv_st_tch[CY_NUM_ST_TCH_ID];
+ u16 prv_mt_pos[CY_NUM_TRK_ID][2];
+ struct cyttsp_bus_ops *bus_ops;
+ struct regulator *regulator;
+ unsigned fw_loader_mode:1;
+ unsigned suspended:1;
+ struct timer_list to_timer;
+ bool to_timeout;
+ struct completion int_running;
+ bool bl_ready;
+ bool device_in_use;
+ u8 reg_id;
+ u8 ntch_count;
+};
+
+struct cyttsp_track_data {
+ u8 prv_tch;
+ u8 cur_tch;
+ u16 tmp_trk[CY_NUM_MT_TCH_ID];
+ u16 snd_trk[CY_NUM_MT_TCH_ID];
+ u16 cur_trk[CY_NUM_TRK_ID];
+ u16 cur_st_tch[CY_NUM_ST_TCH_ID];
+ u16 cur_mt_tch[CY_NUM_MT_TCH_ID];
+ /* if NOT CY_USE_TRACKING_ID then only */
+ /* uses CY_NUM_MT_TCH_ID positions */
+ u16 cur_mt_pos[CY_NUM_TRK_ID][2];
+ /* if NOT CY_USE_TRACKING_ID then only */
+ /* uses CY_NUM_MT_TCH_ID positions */
+ u8 cur_mt_z[CY_NUM_TRK_ID];
+ u8 tool_width;
+ u16 st_x1;
+ u16 st_y1;
+ u8 st_z1;
+ u16 st_x2;
+ u16 st_y2;
+ u8 st_z2;
+};
+
+static const 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
+};
+
+#define LOCK(m) do { \
+ DBG(printk(KERN_INFO "%s: lock\n", __func__);) \
+ mutex_lock(&(m)); \
+} while (0);
+
+#define UNLOCK(m) do { \
+ DBG(printk(KERN_INFO "%s: unlock\n", __func__);) \
+ mutex_unlock(&(m)); \
+} while (0);
+
+DBG(
+static void print_data_block(const char *func, u8 command,
+ u8 length, void *data)
+{
+ char buf[1024];
+ unsigned buf_len = sizeof(buf);
+ char *p = buf;
+ int i;
+ int l;
+
+ l = snprintf(p, buf_len, "cmd 0x%x: ", command);
+ buf_len -= l;
+ p += l;
+ for (i = 0; i < length && buf_len; i++, p += l, buf_len -= l)
+ l = snprintf(p, buf_len, "%02x ", *((char *)data + i));
+ printk(KERN_DEBUG "%s: %s\n", func, buf);
+})
+
+static int cyttsp_soft_reset(struct cyttsp *ts);
+static int cyttsp_set_operational_mode(struct cyttsp *ts);
+static int cyttsp_exit_bl_mode(struct cyttsp *ts);
+static int cyttsp_power_on(struct cyttsp *ts);
+static void cyttsp_init_tch(struct cyttsp *ts)
+{
+ /* init the touch structures */
+ ts->num_prv_st_tch = CY_NTCH;
+ memset(ts->act_trk, CY_NTCH, sizeof(ts->act_trk));
+ memset(ts->prv_mt_pos, CY_NTCH, sizeof(ts->prv_mt_pos));
+ memset(ts->prv_mt_tch, CY_IGNR_TCH, sizeof(ts->prv_mt_tch));
+ memset(ts->prv_st_tch, CY_IGNR_TCH, sizeof(ts->prv_st_tch));
+ ts->ntch_count = 0;
+}
+
+static u8 ttsp_convert_gen2(u8 cur_tch, struct cyttsp_xydata *pxy_data)
+{
+ struct cyttsp_xydata_gen2 *pxy_data_gen2;
+ pxy_data_gen2 = (struct cyttsp_xydata_gen2 *)(pxy_data);
+
+ if (pxy_data_gen2->evnt_idx == CY_GEN2_NOTOUCH) {
+ cur_tch = 0;
+ } else if (cur_tch == CY_GEN2_GHOST) {
+ cur_tch = 0;
+ } else if (cur_tch == CY_GEN2_2TOUCH) {
+ /* stuff artificial track ID1 and ID2 */
+ pxy_data->touch12_id = 0x12;
+ pxy_data->z1 = CY_MAXZ;
+ pxy_data->z2 = CY_MAXZ;
+ cur_tch--; /* 2 touches */
+ } else if (cur_tch == CY_GEN2_1TOUCH) {
+ /* stuff artificial track ID1 and ID2 */
+ pxy_data->touch12_id = 0x12;
+ pxy_data->z1 = CY_MAXZ;
+ pxy_data->z2 = CY_NTCH;
+ if (pxy_data_gen2->evnt_idx == CY_GEN2_TOUCH2) {
+ /* push touch 2 data into touch1
+ * (first finger up; second finger down) */
+ /* stuff artificial track ID1 for touch2 info */
+ pxy_data->touch12_id = 0x20;
+ /* stuff touch 1 with touch 2 coordinate data */
+ pxy_data->x1 = pxy_data->x2;
+ pxy_data->y1 = pxy_data->y2;
+ }
+ } else {
+ cur_tch = 0;
+ }
+ return cur_tch;
+}
+
+static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int rc;
+ int tries;
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ if (!buf || !length) {
+ printk(KERN_ERR "%s: Error, buf:%s len:%u\n",
+ __func__, !buf ? "NULL" : "OK", length);
+ return -EIO;
+ }
+
+ for (tries = 0, rc = -1; tries < CY_NUM_RETRY && (rc < 0); tries++) {
+ rc = ts->bus_ops->read(ts->bus_ops, command, length, buf);
+ if (rc)
+ msleep(CY_DELAY_DFLT);
+ }
+
+ if (rc < 0)
+ printk(KERN_ERR "%s: error %d\n", __func__, rc);
+ DBG(print_data_block(__func__, command, length, buf);)
+ return rc;
+}
+
+static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int rc;
+ int tries;
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ if (!buf || !length) {
+ printk(KERN_ERR "%s: Error, buf:%s len:%u\n",
+ __func__, !buf ? "NULL" : "OK", length);
+ return -EIO;
+ }
+
+ for (tries = 0, rc = -1; tries < CY_NUM_RETRY && (rc < 0); tries++) {
+ rc = ts->bus_ops->write(ts->bus_ops, command, length, buf);
+ if (rc)
+ msleep(CY_DELAY_DFLT);
+ }
+
+ if (rc < 0)
+ printk(KERN_ERR "%s: error %d\n", __func__, rc);
+ DBG(print_data_block(__func__, command, length, buf);)
+ return rc;
+}
+
+static int ttsp_tch_ext(struct cyttsp *ts, void *buf)
+{
+ int rc;
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ if (!buf) {
+ printk(KERN_ERR "%s: Error, buf:%s\n",
+ __func__, !buf ? "NULL" : "OK");
+ return -EIO;
+ }
+ rc = ts->bus_ops->ext(ts->bus_ops, buf);
+ if (rc < 0)
+ printk(KERN_ERR "%s: error %d\n", __func__, rc);
+ return rc;
+}
+
+/* ************************************************************************
+ * The cyttsp_xy_worker function reads the XY coordinates and sends them to
+ * the input layer. It is called from the Touch Interrupt.
+ * *************************************************************************/
+static int cyttsp_inlist(u16 prev_track[], u8 cur_trk_id, u8 *prev_loc,
+ u8 num_touches)
+{
+ u8 id = 0;
+
+ DBG(printk(KERN_INFO"%s: IN p[%d]=%d c=%d n=%d loc=%d\n",
+ __func__, id, prev_track[id], cur_trk_id,
+ num_touches, *prev_loc);)
+
+ for (*prev_loc = CY_IGNR_TCH; id < num_touches; id++) {
+ DBG(printk(KERN_INFO"%s: p[%d]=%d c=%d n=%d loc=%d\n",
+ __func__, id, prev_track[id], cur_trk_id,
+ num_touches, *prev_loc);)
+ if (prev_track[id] == cur_trk_id) {
+ *prev_loc = id;
+ break;
+ }
+ }
+ DBG(printk(KERN_INFO"%s: OUT p[%d]=%d c=%d n=%d loc=%d\n", __func__,
+ id, prev_track[id], cur_trk_id, num_touches, *prev_loc);)
+
+ return *prev_loc < CY_NUM_TRK_ID;
+}
+
+static int cyttsp_next_avail_inlist(u16 cur_trk[], u8 *new_loc,
+ u8 num_touches)
+{
+ u8 id = 0;
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ for (*new_loc = CY_IGNR_TCH; id < num_touches; id++) {
+ if (cur_trk[id] > CY_NUM_TRK_ID) {
+ *new_loc = id;
+ break;
+ }
+ }
+ return *new_loc < CY_NUM_TRK_ID;
+}
+
+static void handle_single_touch(struct cyttsp_xydata *xy,
+ struct cyttsp_track_data *t, struct cyttsp *ts)
+{
+ u8 id;
+ u8 use_trk_id = ts->platform_data->use_trk_id;
+
+ DBG(printk(KERN_INFO"%s: ST STEP 0 - ST1 ID=%d ST2 ID=%d\n",
+ __func__, t->cur_st_tch[CY_ST_FNGR1_IDX],
+ t->cur_st_tch[CY_ST_FNGR2_IDX]);)
+
+ if (t->cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) {
+ /* reassign finger 1 and 2 positions to new tracks */
+ if (t->cur_tch > 0) {
+ /* reassign st finger1 */
+ if (use_trk_id) {
+ id = CY_MT_TCH1_IDX;
+ t->cur_st_tch[CY_ST_FNGR1_IDX] =
+ t->cur_mt_tch[id];
+ } else {
+ id = GET_TOUCH1_ID(xy->touch12_id);
+ t->cur_st_tch[CY_ST_FNGR1_IDX] = id;
+ }
+ t->st_x1 = t->cur_mt_pos[id][CY_XPOS];
+ t->st_y1 = t->cur_mt_pos[id][CY_YPOS];
+ t->st_z1 = t->cur_mt_z[id];
+
+ DBG(printk(KERN_INFO"%s: ST STEP 1 - ST1 ID=%3d\n",
+ __func__, t->cur_st_tch[CY_ST_FNGR1_IDX]);)
+
+ if ((t->cur_tch > 1) &&
+ (t->cur_st_tch[CY_ST_FNGR2_IDX] >
+ CY_NUM_TRK_ID)) {
+ /* reassign st finger2 */
+ if (use_trk_id) {
+ id = CY_MT_TCH2_IDX;
+ t->cur_st_tch[CY_ST_FNGR2_IDX] =
+ t->cur_mt_tch[id];
+ } else {
+ id = GET_TOUCH2_ID(xy->touch12_id);
+ t->cur_st_tch[CY_ST_FNGR2_IDX] = id;
+ }
+ t->st_x2 = t->cur_mt_pos[id][CY_XPOS];
+ t->st_y2 = t->cur_mt_pos[id][CY_YPOS];
+ t->st_z2 = t->cur_mt_z[id];
+
+ DBG(
+ printk(KERN_INFO"%s: ST STEP 2 - ST2 ID=%3d\n",
+ __func__, t->cur_st_tch[CY_ST_FNGR2_IDX]);)
+ }
+ }
+ } else if (t->cur_st_tch[CY_ST_FNGR2_IDX] > CY_NUM_TRK_ID) {
+ if (t->cur_tch > 1) {
+ /* reassign st finger2 */
+ if (use_trk_id) {
+ /* reassign st finger2 */
+ id = CY_MT_TCH2_IDX;
+ t->cur_st_tch[CY_ST_FNGR2_IDX] =
+ t->cur_mt_tch[id];
+ } else {
+ /* reassign st finger2 */
+ id = GET_TOUCH2_ID(xy->touch12_id);
+ t->cur_st_tch[CY_ST_FNGR2_IDX] = id;
+ }
+ t->st_x2 = t->cur_mt_pos[id][CY_XPOS];
+ t->st_y2 = t->cur_mt_pos[id][CY_YPOS];
+ t->st_z2 = t->cur_mt_z[id];
+
+ DBG(printk(KERN_INFO"%s: ST STEP 3 - ST2 ID=%3d\n",
+ __func__, t->cur_st_tch[CY_ST_FNGR2_IDX]);)
+ }
+ }
+ /* if the 1st touch is missing and there is a 2nd touch,
+ * then set the 1st touch to 2nd touch and terminate 2nd touch
+ */
+ if ((t->cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) &&
+ (t->cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID)) {
+ t->st_x1 = t->st_x2;
+ t->st_y1 = t->st_y2;
+ t->st_z1 = t->st_z2;
+ t->cur_st_tch[CY_ST_FNGR1_IDX] = t->cur_st_tch[CY_ST_FNGR2_IDX];
+ t->cur_st_tch[CY_ST_FNGR2_IDX] = CY_IGNR_TCH;
+ }
+ /* if the 2nd touch ends up equal to the 1st touch,
+ * then just report a single touch */
+ if (t->cur_st_tch[CY_ST_FNGR1_IDX] == t->cur_st_tch[CY_ST_FNGR2_IDX])
+ t->cur_st_tch[CY_ST_FNGR2_IDX] = CY_IGNR_TCH;
+
+ /* set Single Touch current event signals */
+ if (t->cur_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) {
+ input_report_abs(ts->input, ABS_X, t->st_x1);
+ input_report_abs(ts->input, ABS_Y, t->st_y1);
+ input_report_abs(ts->input, ABS_PRESSURE, t->st_z1);
+ input_report_key(ts->input, BTN_TOUCH, CY_TCH);
+ input_report_abs(ts->input, ABS_TOOL_WIDTH, t->tool_width);
+
+ DBG2(printk(KERN_INFO"%s:ST->F1:%3d X:%3d Y:%3d Z:%3d\n",
+ __func__, t->cur_st_tch[CY_ST_FNGR1_IDX],
+ t->st_x1, t->st_y1, t->st_z1);)
+
+ if (t->cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID) {
+ input_report_key(ts->input, BTN_2, CY_TCH);
+ input_report_abs(ts->input, ABS_HAT0X, t->st_x2);
+ input_report_abs(ts->input, ABS_HAT0Y, t->st_y2);
+
+ DBG2(printk(KERN_INFO
+ "%s:ST->F2:%3d X:%3d Y:%3d Z:%3d\n",
+ __func__, t->cur_st_tch[CY_ST_FNGR2_IDX],
+ t->st_x2, t->st_y2, t->st_z2);)
+ } else {
+ input_report_key(ts->input, BTN_2, CY_NTCH);
+ }
+ } else {
+ input_report_abs(ts->input, ABS_PRESSURE, CY_NTCH);
+ input_report_key(ts->input, BTN_TOUCH, CY_NTCH);
+ input_report_key(ts->input, BTN_2, CY_NTCH);
+ }
+ /* update platform data for the current single touch info */
+ ts->prv_st_tch[CY_ST_FNGR1_IDX] = t->cur_st_tch[CY_ST_FNGR1_IDX];
+ ts->prv_st_tch[CY_ST_FNGR2_IDX] = t->cur_st_tch[CY_ST_FNGR2_IDX];
+
+}
+
+static void handle_multi_touch(struct cyttsp_track_data *t, struct cyttsp *ts)
+{
+
+ u8 id;
+ u8 i, loc;
+ void (*mt_sync_func)(struct input_dev *) = ts->platform_data->mt_sync;
+
+ if (!ts->platform_data->use_trk_id)
+ goto no_track_id;
+
+ /* terminate any previous touch where the track
+ * is missing from the current event */
+ for (id = 0; id < CY_NUM_TRK_ID; id++) {
+ if ((ts->act_trk[id] == CY_NTCH) || (t->cur_trk[id] != CY_NTCH))
+ continue;
+
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, CY_NTCH);
+ input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, t->tool_width);
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ ts->prv_mt_pos[id][CY_XPOS]);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ ts->prv_mt_pos[id][CY_YPOS]);
+ if (mt_sync_func)
+ mt_sync_func(ts->input);
+ ts->act_trk[id] = CY_NTCH;
+ ts->prv_mt_pos[id][CY_XPOS] = 0;
+ ts->prv_mt_pos[id][CY_YPOS] = 0;
+ }
+ /* set Multi-Touch current event signals */
+ for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+ if (t->cur_mt_tch[id] >= CY_NUM_TRK_ID)
+ continue;
+
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID,
+ t->cur_mt_tch[id]);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+ t->cur_mt_z[id]);
+ input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR,
+ t->tool_width);
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ t->cur_mt_pos[id][CY_XPOS]);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ t->cur_mt_pos[id][CY_YPOS]);
+ if (mt_sync_func)
+ mt_sync_func(ts->input);
+
+ ts->act_trk[id] = CY_TCH;
+ ts->prv_mt_pos[id][CY_XPOS] = t->cur_mt_pos[id][CY_XPOS];
+ ts->prv_mt_pos[id][CY_YPOS] = t->cur_mt_pos[id][CY_YPOS];
+ }
+ return;
+no_track_id:
+
+ /* set temporary track array elements to voids */
+ memset(t->tmp_trk, CY_IGNR_TCH, sizeof(t->tmp_trk));
+ memset(t->snd_trk, CY_IGNR_TCH, sizeof(t->snd_trk));
+
+ /* get what is currently active */
+ for (i = id = 0; id < CY_NUM_TRK_ID && i < CY_NUM_MT_TCH_ID; id++) {
+ if (t->cur_trk[id] == CY_TCH) {
+ /* only incr counter if track found */
+ t->tmp_trk[i] = id;
+ i++;
+ }
+ }
+ DBG(printk(KERN_INFO"%s: T1: t0=%d, t1=%d, t2=%d, t3=%d\n", __func__,
+ t->tmp_trk[0], t->tmp_trk[1],
+ t->tmp_trk[2], t->tmp_trk[3]);)
+ DBG(printk(KERN_INFO"%s: T1: p0=%d, p1=%d, p2=%d, p3=%d\n", __func__,
+ ts->prv_mt_tch[0], ts->prv_mt_tch[1],
+ ts->prv_mt_tch[2], ts->prv_mt_tch[3]);)
+
+ /* pack in still active previous touches */
+ for (id = t->prv_tch = 0; id < CY_NUM_MT_TCH_ID; id++) {
+ if (t->tmp_trk[id] >= CY_NUM_TRK_ID)
+ continue;
+
+ if (cyttsp_inlist(ts->prv_mt_tch, t->tmp_trk[id], &loc,
+ CY_NUM_MT_TCH_ID)) {
+ loc &= CY_NUM_MT_TCH_ID - 1;
+ t->snd_trk[loc] = t->tmp_trk[id];
+ t->prv_tch++;
+ DBG(printk(KERN_INFO"%s: in list s[%d]=%d "
+ "t[%d]=%d, loc=%d p=%d\n", __func__,
+ loc, t->snd_trk[loc],
+ id, t->tmp_trk[id],
+ loc, t->prv_tch);)
+ } else {
+ DBG(printk(KERN_INFO"%s: is not in list s[%d]=%d"
+ " t[%d]=%d loc=%d\n", __func__,
+ id, t->snd_trk[id],
+ id, t->tmp_trk[id],
+ loc);)
+ }
+ }
+ DBG(printk(KERN_INFO"%s: S1: s0=%d, s1=%d, s2=%d, s3=%d p=%d\n",
+ __func__,
+ t->snd_trk[0], t->snd_trk[1], t->snd_trk[2],
+ t->snd_trk[3], t->prv_tch);)
+
+ /* pack in new touches */
+ for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+ if (t->tmp_trk[id] >= CY_NUM_TRK_ID)
+ continue;
+
+ if (!cyttsp_inlist(t->snd_trk, t->tmp_trk[id], &loc,
+ CY_NUM_MT_TCH_ID)) {
+
+ DBG(
+ printk(KERN_INFO"%s: not in list t[%d]=%d, loc=%d\n",
+ __func__,
+ id, t->tmp_trk[id], loc);)
+
+ if (cyttsp_next_avail_inlist(t->snd_trk, &loc,
+ CY_NUM_MT_TCH_ID)) {
+ loc &= CY_NUM_MT_TCH_ID - 1;
+ t->snd_trk[loc] = t->tmp_trk[id];
+ DBG(printk(KERN_INFO "%s: put in list s[%d]=%d"
+ " t[%d]=%d\n", __func__,
+ loc,
+ t->snd_trk[loc], id, t->tmp_trk[id]);
+ )
+ }
+ } else {
+ DBG(printk(KERN_INFO"%s: is in list s[%d]=%d "
+ "t[%d]=%d loc=%d\n", __func__,
+ id, t->snd_trk[id], id, t->tmp_trk[id], loc);)
+ }
+ }
+ DBG(printk(KERN_INFO"%s: S2: s0=%d, s1=%d, s2=%d, s3=%d\n", __func__,
+ t->snd_trk[0], t->snd_trk[1],
+ t->snd_trk[2], t->snd_trk[3]);)
+
+ /* sync motion event signals for each current touch */
+ for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+ /* z will either be 0 (NOTOUCH) or
+ * some pressure (TOUCH)
+ */
+ DBG(printk(KERN_INFO "%s: MT0 prev[%d]=%d "
+ "temp[%d]=%d send[%d]=%d\n",
+ __func__, id, ts->prv_mt_tch[id],
+ id, t->tmp_trk[id], id, t->snd_trk[id]);)
+
+ if (ts->platform_data->invert) {
+ t->cur_mt_pos[t->snd_trk[id]][CY_XPOS] =
+ INVERT_X(t->cur_mt_pos[t->snd_trk[id]]
+ [CY_XPOS], ts->platform_data->maxx);
+ t->cur_mt_pos[t->snd_trk[id]][CY_YPOS] =
+ INVERT_X(t->cur_mt_pos[t->snd_trk[id]]
+ [CY_YPOS], ts->platform_data->maxy);
+ }
+ if (t->snd_trk[id] < CY_NUM_TRK_ID) {
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+ t->cur_mt_z[t->snd_trk[id]]);
+ input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR,
+ t->tool_width);
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ t->cur_mt_pos[t->snd_trk[id]][CY_XPOS]);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ t->cur_mt_pos[t->snd_trk[id]][CY_YPOS]);
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ if (mt_sync_func)
+ mt_sync_func(ts->input);
+
+ DBG2(printk(KERN_INFO"%s: MT1 -> TID:"
+ "%3d X:%3d Y:%3d Z:%3d\n", __func__,
+ t->snd_trk[id],
+ t->cur_mt_pos[t->snd_trk[id]][CY_XPOS],
+ t->cur_mt_pos[t->snd_trk[id]][CY_YPOS],
+ t->cur_mt_z[t->snd_trk[id]]);)
+
+ } else if (ts->prv_mt_tch[id] < CY_NUM_TRK_ID) {
+ /* void out this touch */
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+ CY_NTCH);
+ input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR,
+ t->tool_width);
+ input_report_abs(ts->input, ABS_MT_POSITION_X,
+ ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS]);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y,
+ ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS]);
+ input_report_key(ts->input, BTN_TOUCH, 0);
+
+ if (mt_sync_func)
+ mt_sync_func(ts->input);
+
+ DBG2(printk(KERN_INFO"%s: "
+ "MT2->TID:%2d X:%3d Y:%3d Z:%3d liftoff-sent\n",
+ __func__, ts->prv_mt_tch[id],
+ ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS],
+ ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS],
+ CY_NTCH);)
+ } else {
+ /* do not stuff any signals for this
+ * previously and currently void touches
+ */
+ DBG(printk(KERN_INFO"%s: "
+ "MT3->send[%d]=%d - No touch - NOT sent\n",
+ __func__, id, t->snd_trk[id]);)
+ }
+ }
+
+ /* save current posted tracks to
+ * previous track memory */
+ for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+ ts->prv_mt_tch[id] = t->snd_trk[id];
+ if (t->snd_trk[id] < CY_NUM_TRK_ID) {
+ ts->prv_mt_pos[t->snd_trk[id]][CY_XPOS] =
+ t->cur_mt_pos[t->snd_trk[id]][CY_XPOS];
+ ts->prv_mt_pos[t->snd_trk[id]][CY_YPOS] =
+ t->cur_mt_pos[t->snd_trk[id]][CY_YPOS];
+ DBG(printk(KERN_INFO"%s: "
+ "MT4->TID:%2d X:%3d Y:%3d Z:%3d save for prv\n",
+ __func__, t->snd_trk[id],
+ ts->prv_mt_pos[t->snd_trk[id]][CY_XPOS],
+ ts->prv_mt_pos[t->snd_trk[id]][CY_YPOS],
+ CY_NTCH);)
+ }
+ }
+ memset(ts->act_trk, CY_NTCH, sizeof(ts->act_trk));
+ for (id = 0; id < CY_NUM_MT_TCH_ID; id++) {
+ if (t->snd_trk[id] < CY_NUM_TRK_ID)
+ ts->act_trk[t->snd_trk[id]] = CY_TCH;
+ }
+}
+
+static int cyttsp_reset_controller(struct cyttsp *ts)
+{
+ int ret;
+
+ ret = gpio_request(ts->platform_data->rst_gpio, "reset_pin");
+ if (ret) {
+ printk(KERN_ERR "cyttsp_reset_controller: touch gpio fail\n");
+ return ret;
+ }
+ ret = gpio_direction_output(ts->platform_data->rst_gpio, 1);
+ if (ret < 0) {
+ printk(KERN_ERR "cyttsp_reset_controller: reset gpio direction fail\n");
+ goto out;
+ }
+ /*
+ * The start up procedure
+ * Set the RESET pin to low
+ * Wait for a period of 1 milisecond
+ * Set the RESET pin to high
+ * Wait for a period of 5 milisecond
+ * Start the initial Sequence
+ */
+ gpio_set_value(ts->platform_data->rst_gpio, 0);
+ usleep_range(10000, 20000);
+ gpio_set_value(ts->platform_data->rst_gpio, 1);
+ usleep_range(50000, 60000);
+out:
+ gpio_free(ts->platform_data->rst_gpio);
+ return ret;
+}
+
+static void cyttsp_xy_worker(struct cyttsp *ts)
+{
+ struct cyttsp_xydata xy_data;
+ u8 id, tilt, rev_x, rev_y;
+ struct cyttsp_track_data trc;
+ s32 retval;
+
+ DBG(printk(KERN_INFO "%s: Enter\n", __func__);)
+ /* get event data from CYTTSP device */
+ retval = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(xy_data), &xy_data);
+
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error, fail to read device on host interface bus\n",
+ __func__);
+ goto exit_xy_worker;
+ }
+
+ if (CY_MODE_ERROR(ts->platform_data->power_state,
+ xy_data.hst_mode, xy_data.tt_mode)) {
+ /* TTSP device has switched to non-operational mode */
+ printk(KERN_ERR "%s: Error, bad mode ps=%d hm=%02X tm=%02X\n",
+ __func__, ts->platform_data->power_state,
+ xy_data.hst_mode, xy_data.tt_mode);
+ retval = cyttsp_reset_controller(ts);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error, conroller reset fail\n",
+ __func__);
+ goto exit_xy_worker;
+ }
+ retval = cyttsp_power_on(ts);
+ if (retval < 0)
+ printk(KERN_ERR "%s: Error, power on fail\n", __func__);
+ goto exit_xy_worker;
+ }
+
+ /* touch extension handling */
+ retval = ttsp_tch_ext(ts, &xy_data);
+
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error, touch extension handling\n",
+ __func__);
+ goto exit_xy_worker;
+ } else if (retval > 0) {
+ DBG(printk(KERN_INFO "%s: Touch extension handled\n",
+ __func__);)
+ goto exit_xy_worker;
+ }
+
+ /* provide flow control handshake */
+ if (ts->irq) {
+ if (ts->platform_data->use_hndshk) {
+ u8 cmd;
+ cmd = xy_data.hst_mode & CY_HNDSHK_BIT ?
+ xy_data.hst_mode & ~CY_HNDSHK_BIT :
+ xy_data.hst_mode | CY_HNDSHK_BIT;
+ retval = ttsp_write_block_data(ts, CY_REG_BASE,
+ sizeof(cmd), (u8 *)&cmd);
+ }
+ }
+ trc.cur_tch = GET_NUM_TOUCHES(xy_data.tt_stat);
+
+ if (IS_LARGE_AREA(xy_data.tt_stat) == 1) {
+ /* terminate all active tracks */
+ trc.cur_tch = CY_NTCH;
+ DBG(printk(KERN_INFO "%s: Large area detected\n",
+ __func__);)
+ } else if (trc.cur_tch > CY_MAX_TCH) {
+ /* terminate all active tracks */
+ trc.cur_tch = CY_NTCH;
+ DBG(printk(KERN_INFO "%s: Num touch error detected\n",
+ __func__);)
+ } else if (IS_BAD_PKT(xy_data.tt_mode)) {
+ /* terminate all active tracks */
+ trc.cur_tch = CY_NTCH;
+ DBG(printk(KERN_INFO "%s: Invalid buffer detected\n",
+ __func__);)
+ }
+
+ /* set tool size */
+ trc.tool_width = CY_SMALL_TOOL_WIDTH;
+
+ if (ts->platform_data->gen == CY_GEN2) {
+ /* translate Gen2 interface data into comparable Gen3 data */
+ trc.cur_tch = ttsp_convert_gen2(trc.cur_tch, &xy_data);
+ }
+
+ /* clear current active track ID array and count previous touches */
+ for (id = 0, trc.prv_tch = CY_NTCH; id < CY_NUM_TRK_ID; id++) {
+ trc.cur_trk[id] = CY_NTCH;
+ trc.prv_tch += ts->act_trk[id];
+ }
+
+ /*
+ * send no events if there were no
+ * previous touches and no new touches
+ */
+ if ((trc.prv_tch == CY_NTCH) && ((trc.cur_tch == CY_NTCH))) {
+ if (++ts->ntch_count > CY_MAX_NTCH) {
+ trc.cur_tch = CY_NTCH;
+ }
+ } else
+ ts->ntch_count = 0;
+
+ DBG(printk(KERN_INFO "%s: prev=%d curr=%d\n", __func__,
+ trc.prv_tch, trc.cur_tch);)
+
+ /* clear current single-touch array */
+ memset(trc.cur_st_tch, CY_IGNR_TCH, sizeof(trc.cur_st_tch));
+
+ /* clear single touch positions */
+ trc.st_x1 = trc.st_y1 = trc.st_z1 =
+ trc.st_x2 = trc.st_y2 = trc.st_z2 = CY_NTCH;
+
+ /* clear current multi-touch arrays */
+ memset(trc.cur_mt_tch, CY_IGNR_TCH, sizeof(trc.cur_mt_tch));
+ memset(trc.cur_mt_pos, CY_NTCH, sizeof(trc.cur_mt_pos));
+ memset(trc.cur_mt_z, CY_NTCH, sizeof(trc.cur_mt_z));
+
+ DBG(
+ if (trc.cur_tch) {
+ unsigned i;
+ u8 *pdata = (u8 *)&xy_data;
+
+ printk(KERN_INFO "%s: TTSP data_pack: ", __func__);
+ for (i = 0; i < sizeof(struct cyttsp_xydata); i++)
+ printk(KERN_INFO "[%d]=0x%x ", i, pdata[i]);
+ printk(KERN_INFO "\n");
+ })
+
+ /* Determine if display is tilted */
+ tilt = !!FLIP_DATA(ts->platform_data->flags);
+ /* Check for switch in origin */
+ rev_x = !!REVERSE_X(ts->platform_data->flags);
+ rev_y = !!REVERSE_Y(ts->platform_data->flags);
+
+ /* process the touches */
+ switch (trc.cur_tch) {
+ case 4:
+ xy_data.x4 = be16_to_cpu(xy_data.x4);
+ xy_data.y4 = be16_to_cpu(xy_data.y4);
+ if (tilt)
+ FLIP_XY(xy_data.x4, xy_data.y4);
+
+ if (rev_x)
+ xy_data.x4 = INVERT_X(xy_data.x4,
+ ts->platform_data->maxx);
+ if (rev_y)
+ xy_data.y4 = INVERT_X(xy_data.y4,
+ ts->platform_data->maxy);
+
+ id = GET_TOUCH4_ID(xy_data.touch34_id);
+ if (ts->platform_data->use_trk_id) {
+ trc.cur_mt_pos[CY_MT_TCH4_IDX][CY_XPOS] = xy_data.x4;
+ trc.cur_mt_pos[CY_MT_TCH4_IDX][CY_YPOS] = xy_data.y4;
+ trc.cur_mt_z[CY_MT_TCH4_IDX] = xy_data.z4;
+ } else {
+ trc.cur_mt_pos[id][CY_XPOS] = xy_data.x4;
+ trc.cur_mt_pos[id][CY_YPOS] = xy_data.y4;
+ trc.cur_mt_z[id] = xy_data.z4;
+ }
+ trc.cur_mt_tch[CY_MT_TCH4_IDX] = id;
+ trc.cur_trk[id] = CY_TCH;
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) {
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+ trc.st_x1 = xy_data.x4;
+ trc.st_y1 = xy_data.y4;
+ trc.st_z1 = xy_data.z4;
+ trc.cur_st_tch[CY_ST_FNGR1_IDX] = id;
+ } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+ trc.st_x2 = xy_data.x4;
+ trc.st_y2 = xy_data.y4;
+ trc.st_z2 = xy_data.z4;
+ trc.cur_st_tch[CY_ST_FNGR2_IDX] = id;
+ }
+ }
+ DBG(printk(KERN_INFO"%s: 4th XYZ:% 3d,% 3d,% 3d ID:% 2d\n\n",
+ __func__, xy_data.x4, xy_data.y4, xy_data.z4,
+ (xy_data.touch34_id & 0x0F));)
+
+ /* do not break */
+ case 3:
+ xy_data.x3 = be16_to_cpu(xy_data.x3);
+ xy_data.y3 = be16_to_cpu(xy_data.y3);
+ if (tilt)
+ FLIP_XY(xy_data.x3, xy_data.y3);
+
+ if (rev_x)
+ xy_data.x3 = INVERT_X(xy_data.x3,
+ ts->platform_data->maxx);
+ if (rev_y)
+ xy_data.y3 = INVERT_X(xy_data.y3,
+ ts->platform_data->maxy);
+
+ id = GET_TOUCH3_ID(xy_data.touch34_id);
+ if (ts->platform_data->use_trk_id) {
+ trc.cur_mt_pos[CY_MT_TCH3_IDX][CY_XPOS] = xy_data.x3;
+ trc.cur_mt_pos[CY_MT_TCH3_IDX][CY_YPOS] = xy_data.y3;
+ trc.cur_mt_z[CY_MT_TCH3_IDX] = xy_data.z3;
+ } else {
+ trc.cur_mt_pos[id][CY_XPOS] = xy_data.x3;
+ trc.cur_mt_pos[id][CY_YPOS] = xy_data.y3;
+ trc.cur_mt_z[id] = xy_data.z3;
+ }
+ trc.cur_mt_tch[CY_MT_TCH3_IDX] = id;
+ trc.cur_trk[id] = CY_TCH;
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) {
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+ trc.st_x1 = xy_data.x3;
+ trc.st_y1 = xy_data.y3;
+ trc.st_z1 = xy_data.z3;
+ trc.cur_st_tch[CY_ST_FNGR1_IDX] = id;
+ } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+ trc.st_x2 = xy_data.x3;
+ trc.st_y2 = xy_data.y3;
+ trc.st_z2 = xy_data.z3;
+ trc.cur_st_tch[CY_ST_FNGR2_IDX] = id;
+ }
+ }
+ DBG(printk(KERN_INFO"%s: 3rd XYZ:% 3d,% 3d,% 3d ID:% 2d\n",
+ __func__, xy_data.x3, xy_data.y3, xy_data.z3,
+ ((xy_data.touch34_id >> 4) & 0x0F));)
+
+ /* do not break */
+ case 2:
+ xy_data.x2 = be16_to_cpu(xy_data.x2);
+ xy_data.y2 = be16_to_cpu(xy_data.y2);
+ if (tilt)
+ FLIP_XY(xy_data.x2, xy_data.y2);
+
+ if (rev_x)
+ xy_data.x2 = INVERT_X(xy_data.x2,
+ ts->platform_data->maxx);
+ if (rev_y)
+ xy_data.y2 = INVERT_X(xy_data.y2,
+ ts->platform_data->maxy);
+ id = GET_TOUCH2_ID(xy_data.touch12_id);
+ if (ts->platform_data->use_trk_id) {
+ trc.cur_mt_pos[CY_MT_TCH2_IDX][CY_XPOS] = xy_data.x2;
+ trc.cur_mt_pos[CY_MT_TCH2_IDX][CY_YPOS] = xy_data.y2;
+ trc.cur_mt_z[CY_MT_TCH2_IDX] = xy_data.z2;
+ } else {
+ trc.cur_mt_pos[id][CY_XPOS] = xy_data.x2;
+ trc.cur_mt_pos[id][CY_YPOS] = xy_data.y2;
+ trc.cur_mt_z[id] = xy_data.z2;
+ }
+ trc.cur_mt_tch[CY_MT_TCH2_IDX] = id;
+ trc.cur_trk[id] = CY_TCH;
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) {
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+ trc.st_x1 = xy_data.x2;
+ trc.st_y1 = xy_data.y2;
+ trc.st_z1 = xy_data.z2;
+ trc.cur_st_tch[CY_ST_FNGR1_IDX] = id;
+ } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+ trc.st_x2 = xy_data.x2;
+ trc.st_y2 = xy_data.y2;
+ trc.st_z2 = xy_data.z2;
+ trc.cur_st_tch[CY_ST_FNGR2_IDX] = id;
+ }
+ }
+ DBG(printk(KERN_INFO"%s: 2nd XYZ:% 3d,% 3d,% 3d ID:% 2d\n",
+ __func__, xy_data.x2, xy_data.y2, xy_data.z2,
+ (xy_data.touch12_id & 0x0F));)
+
+ /* do not break */
+ case 1:
+ xy_data.x1 = be16_to_cpu(xy_data.x1);
+ xy_data.y1 = be16_to_cpu(xy_data.y1);
+ if (tilt)
+ FLIP_XY(xy_data.x1, xy_data.y1);
+
+ if (rev_x)
+ xy_data.x1 = INVERT_X(xy_data.x1,
+ ts->platform_data->maxx);
+ if (rev_y)
+ xy_data.y1 = INVERT_X(xy_data.y1,
+ ts->platform_data->maxy);
+
+ id = GET_TOUCH1_ID(xy_data.touch12_id);
+ if (ts->platform_data->use_trk_id) {
+ trc.cur_mt_pos[CY_MT_TCH1_IDX][CY_XPOS] = xy_data.x1;
+ trc.cur_mt_pos[CY_MT_TCH1_IDX][CY_YPOS] = xy_data.y1;
+ trc.cur_mt_z[CY_MT_TCH1_IDX] = xy_data.z1;
+ } else {
+ trc.cur_mt_pos[id][CY_XPOS] = xy_data.x1;
+ trc.cur_mt_pos[id][CY_YPOS] = xy_data.y1;
+ trc.cur_mt_z[id] = xy_data.z1;
+ }
+ trc.cur_mt_tch[CY_MT_TCH1_IDX] = id;
+ trc.cur_trk[id] = CY_TCH;
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) {
+ if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) {
+ trc.st_x1 = xy_data.x1;
+ trc.st_y1 = xy_data.y1;
+ trc.st_z1 = xy_data.z1;
+ trc.cur_st_tch[CY_ST_FNGR1_IDX] = id;
+ } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) {
+ trc.st_x2 = xy_data.x1;
+ trc.st_y2 = xy_data.y1;
+ trc.st_z2 = xy_data.z1;
+ trc.cur_st_tch[CY_ST_FNGR2_IDX] = id;
+ }
+ }
+ DBG(printk(KERN_INFO"%s: S1st XYZ:% 3d,% 3d,% 3d ID:% 2d\n",
+ __func__, xy_data.x1, xy_data.y1, xy_data.z1,
+ ((xy_data.touch12_id >> 4) & 0x0F));)
+
+ break;
+ case 0:
+ default:
+ break;
+ }
+
+ if (ts->platform_data->use_st)
+ handle_single_touch(&xy_data, &trc, ts);
+
+ if (ts->platform_data->use_mt)
+ handle_multi_touch(&trc, ts);
+
+ /* handle gestures */
+ if (ts->platform_data->use_gestures && xy_data.gest_id) {
+ input_report_key(ts->input, BTN_3, CY_TCH);
+ input_report_abs(ts->input, ABS_HAT1X, xy_data.gest_id);
+ input_report_abs(ts->input, ABS_HAT1Y, xy_data.gest_cnt);
+ }
+
+ /* signal the view motion event */
+ input_sync(ts->input);
+
+ /* update platform data for the current multi-touch information */
+ memcpy(ts->act_trk, trc.cur_trk, CY_NUM_TRK_ID);
+
+exit_xy_worker:
+ /* reset the watchdog */
+ mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT);
+ DBG(printk(KERN_INFO"%s: finished.\n", __func__);)
+ return;
+}
+
+static irqreturn_t cyttsp_irq(int irq, void *handle)
+{
+ struct cyttsp *ts = (struct cyttsp *)handle;
+
+ DBG(printk(KERN_INFO"%s: Got IRQ!\n", __func__);)
+ switch (ts->platform_data->power_state) {
+ case CY_BL_STATE:
+ case CY_SYSINFO_STATE:
+ complete(&ts->int_running);
+ break;
+ case CY_LDR_STATE:
+ ts->bl_ready = true;
+ break;
+ case CY_ACTIVE_STATE:
+ cyttsp_xy_worker(ts);
+ break;
+ case CY_READY_STATE:
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* Timer function used as watchdog */
+static void cyttsp_timer(unsigned long handle)
+{
+ struct cyttsp *ts = (struct cyttsp *)handle;
+
+ DBG(printk(KERN_INFO"%s: Watchdog timer event\n", __func__);)
+ if (!work_pending(&ts->work))
+ schedule_work(&ts->work);
+ return;
+}
+/*
+ ************************************************************************
+ * Probe initialization functions
+ ************************************************************************
+ */
+static int cyttsp_load_bl_regs(struct cyttsp *ts)
+{
+ int retval;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ memset(&ts->bl_data, 0, sizeof(ts->bl_data));
+
+ retval = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->bl_data), &(ts->bl_data));
+
+ if (retval < 0) {
+ DBG2(printk(KERN_INFO "%s: Failed reading block data, err:%d\n",
+ __func__, retval);)
+ goto fail;
+ }
+
+ DBG({
+ int i;
+ u8 *bl_data = (u8 *)&(ts->bl_data);
+ for (i = 0; i < sizeof(struct cyttsp_bootloader_data); i++)
+ printk(KERN_INFO "%s bl_data[%d]=0x%x\n",
+ __func__, i, bl_data[i]);
+ })
+ DBG2(printk(KERN_INFO "%s: bl f=%02X s=%02X e=%02X\n",
+ __func__,
+ ts->bl_data.bl_file,
+ ts->bl_data.bl_status,
+ ts->bl_data.bl_error);)
+
+ return 0;
+fail:
+ return retval;
+}
+
+static bool cyttsp_bl_app_valid(struct cyttsp *ts)
+{
+ int retval;
+
+ ts->bl_data.bl_status = 0x00;
+
+ retval = cyttsp_load_bl_regs(ts);
+
+ if (retval < 0)
+ return false;
+
+ if (ts->bl_data.bl_status == 0x11) {
+ printk(KERN_INFO "%s: App found; normal boot\n", __func__);
+ return true;
+ } else if (ts->bl_data.bl_status == 0x10) {
+ printk(KERN_INFO "%s: NO APP; load firmware!!\n", __func__);
+ return false;
+ } else {
+ if (ts->bl_data.bl_file == CY_OPERATE_MODE) {
+ int invalid_op_mode_status;
+ invalid_op_mode_status = ts->bl_data.bl_status & 0x3f;
+ if (invalid_op_mode_status)
+ return false;
+ else {
+ if (ts->platform_data->power_state ==
+ CY_ACTIVE_STATE)
+ printk(KERN_INFO "%s: Operational\n",
+ __func__);
+ else
+ printk(KERN_INFO "%s: No bootloader\n",
+ __func__);
+ }
+ }
+ return true;
+ }
+}
+
+static int cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+ int retval;
+ int tries = 0;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ /* check if in bootloader mode;
+ * if in operational mode then just return without fail
+ */
+ cyttsp_load_bl_regs(ts);
+ if (!GET_BOOTLOADERMODE(ts->bl_data.bl_status))
+ return 0;
+
+ retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(bl_cmd),
+ (void *)bl_cmd);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Failed writing block data, err:%d\n",
+ __func__, retval);
+ goto fail;
+ }
+ do {
+ msleep(10);
+ cyttsp_load_bl_regs(ts);
+ } while (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && tries++ < 1);
+
+ DBG2(printk(KERN_INFO "%s: read tries=%d\n", __func__, tries);)
+
+ DBG(printk(KERN_INFO"%s: Exit "
+ "f=%02X s=%02X e=%02X\n",
+ __func__,
+ ts->bl_data.bl_file, ts->bl_data.bl_status,
+ ts->bl_data.bl_error);)
+
+ return 0;
+fail:
+ return retval;
+}
+
+static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+ int retval;
+ u8 cmd = CY_SYSINFO_MODE;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ memset(&(ts->sysinfo_data), 0, sizeof(struct cyttsp_sysinfo_data));
+
+ /* switch to sysinfo mode */
+ retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Failed writing block data, err:%d\n",
+ __func__, retval);
+ goto exit_sysinfo_mode;
+ }
+
+ if (!(retval < 0)) {
+ /* wait for interrupt to set ready completion */
+ ts->platform_data->power_state = CY_SYSINFO_STATE;
+ INIT_COMPLETION(ts->int_running);
+ retval = wait_for_completion_interruptible_timeout(
+ &ts->int_running,
+ msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
+ ts->platform_data->power_state = CY_READY_STATE;
+ }
+
+ if (retval < 0) {
+ ts->platform_data->power_state = CY_IDLE_STATE;
+ printk(KERN_ERR "%s: reset timeout fail (ret=%d)\n",
+ __func__, retval);
+ }
+
+exit_sysinfo_mode:
+ /* read sysinfo registers */
+ retval = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->sysinfo_data), &(ts->sysinfo_data));
+
+ DBG(printk(KERN_INFO"%s:SI2: hst_mode=0x%02X mfg_cmd=0x%02X "
+ "mfg_stat=0x%02X\n", __func__, ts->sysinfo_data.hst_mode,
+ ts->sysinfo_data.mfg_cmd,
+ ts->sysinfo_data.mfg_stat);)
+
+ DBG(printk(KERN_INFO"%s:SI2: bl_ver=0x%02X%02X\n",
+ __func__, ts->sysinfo_data.bl_verh, ts->sysinfo_data.bl_verl);)
+
+ DBG(printk(KERN_INFO"%s:SI2: sysinfo act_intrvl=0x%02X "
+ "tch_tmout=0x%02X lp_intrvl=0x%02X\n",
+ __func__, ts->sysinfo_data.act_intrvl,
+ ts->sysinfo_data.tch_tmout,
+ ts->sysinfo_data.lp_intrvl);)
+
+ printk(KERN_INFO"%s:SI2:tts_ver=%02X%02X app_id=%02X%02X "
+ "app_ver=%02X%02X c_id=%02X%02X%02X "
+ "u_id=%02X%02X%02X%02X%02X%02X%02X%02X\n",
+ __func__,
+ ts->sysinfo_data.tts_verh, ts->sysinfo_data.tts_verl,
+ ts->sysinfo_data.app_idh, ts->sysinfo_data.app_idl,
+ ts->sysinfo_data.app_verh, ts->sysinfo_data.app_verl,
+ ts->sysinfo_data.cid[0], ts->sysinfo_data.cid[1],
+ ts->sysinfo_data.cid[2],
+ ts->sysinfo_data.uid[0], ts->sysinfo_data.uid[1],
+ ts->sysinfo_data.uid[2], ts->sysinfo_data.uid[3],
+ ts->sysinfo_data.uid[4], ts->sysinfo_data.uid[5],
+ ts->sysinfo_data.uid[6], ts->sysinfo_data.uid[7]);
+
+ return retval;
+}
+
+static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
+{
+ int retval = 0;
+
+ if (ts->platform_data->scn_typ != CY_SCN_TYP_DFLT) {
+ u8 scn_typ = ts->platform_data->scn_typ;
+
+ retval = ttsp_write_block_data(ts,
+ CY_REG_SCN_TYP,
+ sizeof(scn_typ), &scn_typ);
+ }
+
+ if (retval < 0)
+ return retval;
+
+ if ((ts->platform_data->act_intrvl != CY_ACT_INTRVL_DFLT) ||
+ (ts->platform_data->tch_tmout != CY_TCH_TMOUT_DFLT) ||
+ (ts->platform_data->lp_intrvl != CY_LP_INTRVL_DFLT)) {
+
+ u8 intrvl_ray[3];
+
+ intrvl_ray[0] = ts->platform_data->act_intrvl;
+ intrvl_ray[1] = ts->platform_data->tch_tmout;
+ intrvl_ray[2] = ts->platform_data->lp_intrvl;
+
+ /* set intrvl registers */
+ retval = ttsp_write_block_data(ts,
+ CY_REG_ACT_INTRVL,
+ sizeof(intrvl_ray), intrvl_ray);
+
+ msleep(CY_DELAY_SYSINFO);
+ }
+
+ return retval;
+}
+
+static int cyttsp_set_operational_mode(struct cyttsp *ts)
+{
+ int retval;
+ int tries;
+ u8 cmd = CY_OPERATE_MODE;
+ u8 gest_default;
+ struct cyttsp_xydata xy_data;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Failed writing block data, err:%d\n",
+ __func__, retval);
+ goto write_block_data_fail;
+ }
+
+ /* wait for TTSP Device to complete switch to Operational mode */
+ msleep(10);
+
+ tries = 0;
+ gest_default =
+ CY_GEST_GRP1 + CY_GEST_GRP2 +
+ CY_GEST_GRP3 + CY_GEST_GRP4 +
+ CY_ACT_DIST_DFLT;
+ tries = 0;
+ do {
+ msleep(CY_DELAY_DFLT);
+ memset(&xy_data, 0, sizeof(xy_data));
+ retval = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(struct cyttsp_xydata), &xy_data);
+ } while (!((retval == 0) &&
+ ((xy_data.gest_set & CY_ACT_DIST_BITS) ==
+ (CY_ACT_DIST_DFLT & CY_ACT_DIST_BITS))) &&
+ (tries++ < CY_DELAY_MAX));
+
+ DBG2(printk(KERN_INFO "%s: read tries=%d\n", __func__, tries);)
+
+ return 0;
+write_block_data_fail:
+ return retval;
+}
+
+static int cyttsp_soft_reset(struct cyttsp *ts)
+{
+ int retval;
+ u8 cmd = CY_SOFT_RESET_MODE;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ /* reset TTSP Device back to bootloader mode */
+ ts->platform_data->power_state = CY_BL_STATE;
+ retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+
+ if (!(retval < 0)) {
+ /* wait for interrupt to set ready completion */
+ INIT_COMPLETION(ts->int_running);
+ retval = wait_for_completion_interruptible_timeout(
+ &ts->int_running,
+ msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
+ }
+
+ if (retval < 0) {
+ ts->platform_data->power_state = CY_IDLE_STATE;
+ printk(KERN_ERR "%s: reset timeout fail (ret=%d)\n",
+ __func__, retval);
+ }
+
+ if (retval > 0)
+ retval = 0;
+
+ return retval;
+}
+
+static int cyttsp_gesture_setup(struct cyttsp *ts)
+{
+ int retval;
+ u8 gesture_setup;
+
+ DBG(printk(KERN_INFO"%s: Init gesture; active distance setup\n",
+ __func__);)
+
+ gesture_setup = ts->platform_data->gest_set;
+ retval = ttsp_write_block_data(ts, CY_REG_GEST_SET,
+ sizeof(gesture_setup), &gesture_setup);
+
+ return retval;
+}
+
+#ifdef CY_INCLUDE_LOAD_RECS
+#include "cyttsp_ldr.h"
+#else
+static bool cyttsp_bl_status(struct cyttsp *ts)
+{
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ return ((ts->bl_data.bl_status == 0x10) ||
+ (ts->bl_data.bl_status == 0x11));
+}
+
+static int cyttsp_loader(struct cyttsp *ts)
+{
+ void *fptr = cyttsp_bl_status; /* kill warning */
+
+ if (ts) {
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ DBG(printk(KERN_INFO"%s: Exit\n", __func__);)
+ }
+
+ if (!fptr)
+ return -EIO;
+ else
+ return 0;
+}
+#endif
+
+static int cyttsp_power_on(struct cyttsp *ts)
+{
+ int retval = 0;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ /* (re-)start watchdog */
+ mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT);
+
+ cyttsp_init_tch(ts);
+ retval = cyttsp_soft_reset(ts);
+ DBG2(printk(KERN_INFO"%s: after softreset r=%d\n", __func__, retval);)
+ if (retval < 0)
+ goto bypass;
+
+ if (ts->platform_data->use_load_file)
+ retval = cyttsp_loader(ts);
+
+ if (!cyttsp_bl_app_valid(ts)) {
+ retval = 1;
+ goto bypass;
+ }
+
+ retval = cyttsp_exit_bl_mode(ts);
+ DBG2(printk(KERN_INFO"%s: after exit bl r=%d\n", __func__, retval);)
+
+ if (retval < 0)
+ goto bypass;
+
+ /* switch to System Information mode to read */
+ /* versions and set interval registers */
+ ts->platform_data->power_state = CY_READY_STATE;
+ retval = cyttsp_set_sysinfo_mode(ts);
+ if (retval < 0)
+ goto bypass;
+
+ retval = cyttsp_set_sysinfo_regs(ts);
+ if (retval < 0)
+ goto bypass;
+
+ /* init gesture setup; required for active distance */
+ cyttsp_gesture_setup(ts);
+
+ /* switch back to Operational mode */
+ DBG2(printk(KERN_INFO"%s: switch back to operational mode\n",
+ __func__);)
+ retval = cyttsp_set_operational_mode(ts);
+ if (retval < 0)
+ goto bypass;
+
+bypass:
+ if (retval)
+ ts->platform_data->power_state = CY_IDLE_STATE;
+ else
+ ts->platform_data->power_state = CY_ACTIVE_STATE;
+
+ printk(KERN_INFO"%s: Power state is %s\n",
+ __func__, (ts->platform_data->power_state == CY_ACTIVE_STATE) ?
+ "ACTIVE" : "IDLE");
+ return retval;
+}
+
+static void cyttsp_check_bl(struct work_struct *work)
+{
+ struct cyttsp *ts = container_of(work, struct cyttsp, work);
+ s32 retval;
+ struct cyttsp_xydata xy_data;
+
+ retval = ttsp_read_block_data(ts, CY_REG_BASE, 2, &xy_data);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error, fail to read device\n", __func__);
+ goto reserve_next;
+ }
+
+ if (CY_MODE_ERROR(ts->platform_data->power_state,
+ xy_data.hst_mode, xy_data.tt_mode)) {
+ printk(KERN_ERR "%s: Error, mode error detected "
+ "on watchdog timeout ps=%d mode=%02X bl_mode=%02X\n",
+ __func__, ts->platform_data->power_state,
+ xy_data.hst_mode, xy_data.tt_mode);
+ retval = cyttsp_power_on(ts);
+ if (retval < 0)
+ printk(KERN_ERR "%s: Error, power on fail\n", __func__);
+ }
+
+reserve_next:
+ mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT);
+}
+
+static int cyttsp_resume(struct cyttsp *ts)
+{
+ int retval = 0;
+ struct cyttsp_xydata xydata;
+
+ if (!ts->device_in_use)
+ return 0;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ if (ts->platform_data->use_sleep && (ts->platform_data->power_state !=
+ CY_ACTIVE_STATE)) {
+ if (ts->platform_data->wakeup) {
+ retval = ts->platform_data->wakeup();
+ if (retval < 0)
+ printk(KERN_ERR "%s: Error, wakeup failed!\n",
+ __func__);
+ } else {
+ printk(KERN_ERR "%s: Error, wakeup not implemented "
+ "(check board file).\n", __func__);
+ retval = -ENOSYS;
+ }
+ if (!(retval < 0)) {
+ retval = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(xydata), &xydata);
+ if (!(retval < 0) && !GET_HSTMODE(xydata.hst_mode))
+ ts->platform_data->power_state =
+ CY_ACTIVE_STATE;
+ }
+ }
+ DBG(printk(KERN_INFO"%s: Wake Up %s\n", __func__,
+ (retval < 0) ? "FAIL" : "PASS");)
+ return retval;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static int cyttsp_suspend(struct cyttsp *ts)
+{
+ u8 sleep_mode = 0;
+ int retval = 0;
+
+ if (!ts->device_in_use)
+ return 0;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ if (ts->platform_data->use_sleep &&
+ (ts->platform_data->power_state == CY_ACTIVE_STATE)) {
+ sleep_mode = CY_DEEP_SLEEP_MODE;
+ retval = ttsp_write_block_data(ts,
+ CY_REG_BASE, sizeof(sleep_mode), &sleep_mode);
+ if (!(retval < 0))
+ ts->platform_data->power_state = CY_SLEEP_STATE;
+ msleep(CY_MODE_CHANGE_DELAY);
+ }
+ DBG(printk(KERN_INFO"%s: Sleep Power state is %s\n", __func__,
+ (ts->platform_data->power_state == CY_ACTIVE_STATE) ?
+ "ACTIVE" :
+ ((ts->platform_data->power_state == CY_SLEEP_STATE) ?
+ "SLEEP" : "LOW POWER"));)
+ return retval;
+}
+
+static void cyttsp_ts_early_suspend(struct early_suspend *h)
+{
+ struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend);
+
+ if (!ts->device_in_use)
+ return;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ LOCK(ts->mutex);
+ if (!ts->fw_loader_mode) {
+ disable_irq_nosync(ts->irq);
+ ts->suspended = 1;
+ /* kill watchdog */
+ del_timer(&ts->timer);
+ cancel_work_sync(&ts->work);
+ cyttsp_suspend(ts);
+ }
+ regulator_disable(ts->regulator);
+ UNLOCK(ts->mutex);
+}
+
+static void cyttsp_ts_late_resume(struct early_suspend *h)
+{
+ struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend);
+
+ if (!ts->device_in_use)
+ return;
+
+ regulator_enable(ts->regulator);
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ LOCK(ts->mutex);
+ if (!ts->fw_loader_mode && ts->suspended) {
+ ts->suspended = 0;
+ if (cyttsp_resume(ts) < 0)
+ printk(KERN_ERR "%s: Error, cyttsp_resume.\n",
+ __func__);
+ enable_irq(ts->irq);
+ /* resume watchdog */
+ mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT);
+ }
+ UNLOCK(ts->mutex);
+}
+#endif
+
+static int cyttsp_wr_reg(struct cyttsp *ts, u8 reg_id, u8 reg_data)
+{
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ return ttsp_write_block_data(ts,
+ CY_REG_BASE + reg_id, sizeof(u8), &reg_data);
+}
+
+static int cyttsp_rd_reg(struct cyttsp *ts, u8 reg_id, u8 *reg_data)
+{
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+ return ttsp_read_block_data(ts,
+ CY_REG_BASE + reg_id, sizeof(u8), reg_data);
+}
+
+static ssize_t firmware_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cyttsp *ts = dev_get_drvdata(dev);
+ LOCK(ts->mutex);
+ if (!ts->fw_loader_mode) {
+ unsigned short val = *(unsigned short *)buf;
+ u8 reg_data = val & 0xff;
+ ts->reg_id = (val & 0xff00) >> 8;
+ if (!(ts->reg_id & 0x80)) {
+ /* write user specified operational register */
+ if (ts->reg_id == 0x00) {
+ switch (GET_HSTMODE(reg_data)) {
+ case GET_HSTMODE(CY_OPERATE_MODE):
+ cyttsp_wr_reg(ts, ts->reg_id, reg_data);
+ ts->platform_data->power_state =
+ CY_ACTIVE_STATE;
+ enable_irq(ts->irq);
+ mod_timer(&ts->timer, CY_WDG_TIMEOUT);
+ printk(KERN_INFO "%s: "
+ "Switch to Operational Mode "
+ "ps=%d\n", __func__,
+ ts->platform_data->power_state);
+ break;
+ case GET_HSTMODE(CY_SYSINFO_MODE):
+ ts->platform_data->power_state =
+ CY_READY_STATE;
+ disable_irq_nosync(ts->irq);
+ /* kill watchdog */
+ del_timer(&ts->timer);
+ cancel_work_sync(&ts->work);
+ cyttsp_wr_reg(ts, ts->reg_id, reg_data);
+ printk(KERN_INFO "%s: "
+ "Switch to SysInfo Mode "
+ "ps=%d\n", __func__,
+ ts->platform_data->power_state);
+ break;
+ default:
+ cyttsp_wr_reg(ts, ts->reg_id, reg_data);
+ break;
+ }
+ } else
+ cyttsp_wr_reg(ts, ts->reg_id, reg_data);
+ printk(KERN_INFO "%s: "
+ "write(reg=%02X(%d) dat=0x%02X(%d))\n",
+ __func__,
+ ts->reg_id & ~0x80, ts->reg_id & ~0x80,
+ reg_data, reg_data);
+ } else {
+ /* save user specified operational read register */
+ DBG2(printk(KERN_INFO "%s: read(r=0x%02X)\n",
+ __func__, ts->reg_id);)
+ }
+ } else {
+ int retval = 0;
+ int tries = 0;
+ DBG({
+ char str[128];
+ char *p = str;
+ int i;
+ for (i = 0; i < size; i++, p += 2)
+ sprintf(p, "%02x", (unsigned char)buf[i]);
+ printk(KERN_DEBUG "%s: size %d, pos %ld payload %s\n",
+ __func__, size, (long)pos, str);
+ })
+ do {
+ retval = ttsp_write_block_data(ts,
+ CY_REG_BASE, size, buf);
+ if (retval < 0)
+ msleep(500);
+ } while ((retval < 0) && (tries++ < 10));
+ }
+ UNLOCK(ts->mutex);
+ return size;
+}
+
+static ssize_t firmware_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *ba,
+ char *buf, loff_t pos, size_t size)
+{
+ int count = 0;
+ u8 reg_data;
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cyttsp *ts = dev_get_drvdata(dev);
+
+ DBG2(printk(KERN_INFO"%s: Enter (mode=%d)\n",
+ __func__, ts->fw_loader_mode);)
+
+ LOCK(ts->mutex);
+ if (!ts->fw_loader_mode) {
+ /* read user specified operational register */
+ cyttsp_rd_reg(ts, ts->reg_id & ~0x80, &reg_data);
+ *(unsigned short *)buf = reg_data << 8;
+ count = sizeof(unsigned short);
+ printk(KERN_INFO "%s: read(reg=%02X(%d) dat=0x%02X(%d))\n",
+ __func__, ts->reg_id & ~0x80, ts->reg_id & ~0x80,
+ reg_data, reg_data);
+ } else {
+ int retval = 0;
+ int tries = 0;
+
+ do {
+ retval = cyttsp_load_bl_regs(ts);
+ if (retval < 0)
+ msleep(500);
+ } while ((retval < 0) && (tries++ < 10));
+
+ if (retval < 0) {
+ printk(KERN_ERR "%s: error reading status\n", __func__);
+ count = 0;
+ } else {
+ *(unsigned short *)buf = ts->bl_data.bl_status << 8 |
+ ts->bl_data.bl_error;
+ count = sizeof(unsigned short);
+ }
+
+ DBG2(printk(KERN_INFO
+ "%s:bl_f=0x%02X bl_s=0x%02X bl_e=0x%02X\n",
+ __func__,
+ ts->bl_data.bl_file,
+ ts->bl_data.bl_status,
+ ts->bl_data.bl_error);)
+ }
+ UNLOCK(ts->mutex);
+ return count;
+}
+
+static struct bin_attribute cyttsp_firmware = {
+ .attr = {
+ .name = "firmware",
+ .mode = 0644,
+ },
+ .size = 128,
+ .read = firmware_read,
+ .write = firmware_write,
+};
+
+static ssize_t attr_fwloader_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp *ts = dev_get_drvdata(dev);
+ return sprintf(buf, "0x%02X%02X 0x%02X%02X 0x%02X%02X 0x%02X%02X%02X\n",
+ ts->sysinfo_data.tts_verh, ts->sysinfo_data.tts_verl,
+ ts->sysinfo_data.app_idh, ts->sysinfo_data.app_idl,
+ ts->sysinfo_data.app_verh, ts->sysinfo_data.app_verl,
+ ts->sysinfo_data.cid[0], ts->sysinfo_data.cid[1],
+ ts->sysinfo_data.cid[2]);
+}
+
+static ssize_t attr_fwloader_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ char *p;
+ int ret;
+ struct cyttsp *ts = dev_get_drvdata(dev);
+ unsigned val = simple_strtoul(buf, &p, 10);
+
+ ret = p - buf;
+ if (*p && isspace(*p))
+ ret++;
+ DBG2(printk(KERN_DEBUG "%s: %u\n", __func__, val);)
+
+ LOCK(ts->mutex)
+ if (val == 3) {
+ sysfs_remove_bin_file(&dev->kobj, &cyttsp_firmware);
+ DBG2(printk(KERN_INFO "%s: FW loader closed for reg r/w\n",
+ __func__);)
+ } else if (val == 2) {
+ if (sysfs_create_bin_file(&dev->kobj, &cyttsp_firmware))
+ printk(KERN_ERR "%s: unable to create file\n",
+ __func__);
+ DBG2(printk(KERN_INFO "%s: FW loader opened for reg r/w\n",
+ __func__);)
+ if (ts->suspended) {
+ cyttsp_resume(ts);
+ ts->suspended = 0;
+ enable_irq(ts->irq);
+ /* resume watchdog */
+ mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT);
+ }
+ } else if ((val == 1) && !ts->fw_loader_mode) {
+ ts->fw_loader_mode = 1;
+ if (ts->suspended) {
+ cyttsp_resume(ts);
+ ts->suspended = 0;
+ enable_irq(ts->irq);
+ /* resume watchdog */
+ mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT);
+ }
+ disable_irq_nosync(ts->irq);
+ /* kill watchdog */
+ del_timer(&ts->timer);
+ cancel_work_sync(&ts->work);
+ if (sysfs_create_bin_file(&dev->kobj, &cyttsp_firmware))
+ printk(KERN_ERR "%s: unable to create file\n",
+ __func__);
+ DBG2(printk(KERN_INFO
+ "%s: FW loader opened for start load: ps=%d mode=%d\n",
+ __func__,
+ ts->platform_data->power_state, ts->fw_loader_mode);)
+ cyttsp_soft_reset(ts);
+ printk(KERN_INFO "%s: FW loader started.\n", __func__);
+ ts->platform_data->power_state = CY_LDR_STATE;
+ } else if (!val && ts->fw_loader_mode) {
+ sysfs_remove_bin_file(&dev->kobj, &cyttsp_firmware);
+ ts->fw_loader_mode = 0;
+ printk(KERN_INFO "%s: FW loader finished.\n", __func__);
+ enable_irq(ts->irq);
+ ret = cyttsp_power_on(ts);
+ if (ret < 0)
+ printk(KERN_ERR "%s: Error, power on fail\n", __func__);
+ /* resume watchdog */
+ mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT);
+ }
+ UNLOCK(ts->mutex);
+ return ret == size ? ret : -EINVAL;
+}
+
+static void cyttsp_close(struct input_dev *dev)
+{
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ struct cyttsp *ts = dev_get_drvdata(&dev->dev);
+ cancel_work_sync(&ts->work);
+ free_irq(ts->irq, ts);
+ regulator_disable(ts->regulator);
+ ts->device_in_use = false;
+}
+
+static int cyttsp_open(struct input_dev *dev)
+{
+ struct cyttsp *ts = dev_get_drvdata(&dev->dev);
+ int ret = 0;
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ ret = regulator_enable(ts->regulator);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: regulator enable failed\n", __func__);
+ goto error_regulator;
+ }
+ /* enable interrupts */
+ ts->irq = gpio_to_irq(ts->platform_data->irq_gpio);
+ ret = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ ts->input->name, ts);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: IRQ request failed r=%d\n",
+ __func__, ret);
+ ts->platform_data->power_state = CY_INVALID_STATE;
+ goto error_gpio_irq;
+ }
+ ret = cyttsp_reset_controller(ts);
+ if (ret < 0) {
+ printk(KERN_ERR "controller reset failed\n");
+ goto error_reset;
+ }
+ DBG(printk(KERN_INFO "%s: call power_on\n", __func__);)
+
+ ret = cyttsp_power_on(ts);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: Error, power on failed!\n", __func__);
+ goto error_power_on;
+ }
+ ts->device_in_use = true;
+
+ return ret;
+
+error_power_on:
+error_reset:
+ if (ts->irq >= 0)
+ free_irq(ts->irq, ts);
+error_gpio_irq:
+ regulator_disable(ts->regulator);
+error_regulator:
+ return ret;
+}
+
+static struct device_attribute fwloader =
+ __ATTR(fwloader, 0644, attr_fwloader_show, attr_fwloader_store);
+
+void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev)
+{
+ struct input_dev *input_device;
+ struct cyttsp *ts;
+ int retval = 0;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL) {
+ printk(KERN_ERR "%s: Error, kzalloc\n", __func__);
+ goto error_alloc_data_failed;
+ }
+ mutex_init(&ts->mutex);
+ ts->pdev = pdev;
+ ts->platform_data = pdev->platform_data;
+ ts->bus_ops = bus_ops;
+ init_completion(&ts->int_running);
+
+ ts->regulator = regulator_get(ts->pdev, "vcpin");
+ if (IS_ERR(ts->regulator)) {
+ printk(KERN_ERR "%s: Error, regulator_get failed\n", __func__);
+ ts->regulator = NULL;
+ goto error_regulator_get;
+ }
+ if (ts->platform_data->init)
+ retval = ts->platform_data->init(1);
+ if (retval) {
+ printk(KERN_ERR "%s: platform init failed!\n", __func__);
+ goto error_init;
+ }
+
+ /* Create the input device and register it. */
+ input_device = input_allocate_device();
+ if (!input_device) {
+ retval = -ENOMEM;
+ printk(KERN_ERR "%s: Error, failed to allocate input device\n",
+ __func__);
+ goto error_input_allocate_device;
+ }
+ ts->input = input_device;
+ input_device->name = ts->platform_data->name;
+ input_device->phys = ts->phys;
+ input_device->dev.parent = ts->pdev;
+ input_set_drvdata(ts->input, ts);
+ input_device->open = cyttsp_open;
+ input_device->close = cyttsp_close;
+
+ /* setup watchdog */
+ INIT_WORK(&ts->work, cyttsp_check_bl);
+ setup_timer(&ts->timer, cyttsp_timer, (unsigned long) ts);
+
+ set_bit(EV_SYN, input_device->evbit);
+ set_bit(EV_KEY, input_device->evbit);
+ set_bit(EV_ABS, input_device->evbit);
+ set_bit(BTN_TOUCH, input_device->keybit);
+ set_bit(BTN_2, input_device->keybit);
+ if (ts->platform_data->use_gestures)
+ set_bit(BTN_3, input_device->keybit);
+
+ input_set_abs_params(input_device, ABS_X, 0, ts->platform_data->maxx,
+ 0, 0);
+ input_set_abs_params(input_device, ABS_Y, 0, ts->platform_data->maxy,
+ 0, 0);
+ input_set_abs_params(input_device, ABS_TOOL_WIDTH, 0,
+ CY_LARGE_TOOL_WIDTH, 0, 0);
+ input_set_abs_params(input_device, ABS_PRESSURE, 0, CY_MAXZ, 0, 0);
+ input_set_abs_params(input_device, ABS_HAT0X, 0,
+ ts->platform_data->maxx, 0, 0);
+ input_set_abs_params(input_device, ABS_HAT0Y, 0,
+ ts->platform_data->maxy, 0, 0);
+ if (ts->platform_data->use_gestures) {
+ input_set_abs_params(input_device, ABS_HAT1X, 0, CY_MAXZ,
+ 0, 0);
+ input_set_abs_params(input_device, ABS_HAT1Y, 0, CY_MAXZ,
+ 0, 0);
+ }
+ if (ts->platform_data->use_mt) {
+ input_set_abs_params(input_device, ABS_MT_POSITION_X, 0,
+ ts->platform_data->maxx, 0, 0);
+ input_set_abs_params(input_device, ABS_MT_POSITION_Y, 0,
+ ts->platform_data->maxy, 0, 0);
+ input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR, 0,
+ CY_MAXZ, 0, 0);
+ input_set_abs_params(input_device, ABS_MT_WIDTH_MAJOR, 0,
+ CY_LARGE_TOOL_WIDTH, 0, 0);
+ if (ts->platform_data->use_trk_id)
+ input_set_abs_params(input_device, ABS_MT_TRACKING_ID,
+ 0, CY_NUM_TRK_ID, 0, 0);
+ }
+
+ if (ts->platform_data->use_virtual_keys)
+ input_set_capability(input_device, EV_KEY, KEY_PROG1);
+
+ retval = input_register_device(input_device);
+ if (retval) {
+ printk(KERN_ERR "%s: Error, failed to register input device\n",
+ __func__);
+ goto error_input_register_device;
+ }
+ DBG(printk(KERN_INFO "%s: Registered input device %s\n",
+ __func__, input_device->name);)
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = cyttsp_ts_early_suspend;
+ ts->early_suspend.resume = cyttsp_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+ retval = device_create_file(pdev, &fwloader);
+ if (retval) {
+ printk(KERN_ERR "%s: Error, could not create attribute\n",
+ __func__);
+ goto device_create_error;
+ }
+
+ return ts;
+
+device_create_error:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+error_input_register_device:
+ input_unregister_device(input_device);
+ cancel_work_sync(&ts->work);
+ del_timer_sync(&ts->timer);
+error_input_allocate_device:
+ if (ts->platform_data->init)
+ ts->platform_data->init(0);
+error_init:
+ regulator_put(ts->regulator);
+error_regulator_get:
+ kfree(ts);
+error_alloc_data_failed:
+ return NULL;
+}
+
+/* registered in driver struct */
+void cyttsp_core_release(void *handle)
+{
+ struct cyttsp *ts = handle;
+
+ DBG(printk(KERN_INFO"%s: Enter\n", __func__);)
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+ del_timer_sync(&ts->timer);
+ cancel_work_sync(&ts->work);
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ input_free_device(ts->input);
+ if (ts->platform_data->init)
+ ts->platform_data->init(0);
+ kfree(ts);
+}
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
+MODULE_AUTHOR("Cypress");
+