[PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

Kevin McNeely
2010-11-09T18:26:14Z

Initial release of Cypress TTSP Gen3 Core Driver.
Core Driver includes platform data definition file,
core driver definition file, and core touchscreen
touch handling of device data. Generates
multi-touch input events.

Signed-off-by: Kevin McNeely <kev@cypress.com>
---
drivers/input/touchscreen/Kconfig | 8 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/cyttsp_core.c | 885 +++++++++++++++++++++++++++++++
drivers/input/touchscreen/cyttsp_core.h | 55 ++
include/linux/input/cyttsp.h | 78 +++
5 files changed, 1027 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/touchscreen/cyttsp_core.c
create mode 100644 drivers/input/touchscreen/cyttsp_core.h
create mode 100644 include/linux/input/cyttsp.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 06ea8da..ee5a31b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -124,6 +124,14 @@ config TOUCHSCREEN_CY8CTMG110
To compile this driver as a module, choose M here: the
module will be called cy8ctmg110_ts.

+config TOUCHSCREEN_CYTTSP_CORE
+ bool "Cypress TTSP touchscreen core"
+ help
+ Say Y here if you have a Cypress TTSP touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
config TOUCHSCREEN_DA9034
tristate "Touchscreen support for Dialog Semiconductor DA9034"
depends on PMIC_DA903X
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 7cc1b4f..f629af9 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
new file mode 100644
index 0000000..99fd316
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -0,0 +1,885 @@
+/* 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 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.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+/* Bootloader File 0 offset */
+#define CY_BL_FILE0 0x00
+/* Bootloader command directive */
+#define CY_BL_CMD 0xFF
+/* Bootloader Exit and Verify Checksum command */
+#define CY_BL_EXIT 0xA5
+/* Bootloader number of command keys */
+#define CY_NUM_BL_KEYS 8
+/* Bootloader default command 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
+
+/* 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)
+
+/* maximum number of concurrent tracks */
+#define CY_NUM_TCH_ID 4
+/* maximum number of track IDs */
+#define CY_NUM_TRK_ID 16
+
+#define CY_NTCH 0 /* lift off */
+#define CY_TCH 1 /* touch down */
+#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_MAXZ 255
+#define CY_DELAY_SYSINFO 20 /* 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
+
+/* Touch structure */
+struct cyttsp_touch {
+ u16 x __attribute__ ((packed));
+ u16 y __attribute__ ((packed));
+ u8 z;
+};
+
+/* TrueTouch Standard Product Gen3 interface definition */
+struct cyttsp_xydata {
+ u8 hst_mode;
+ u8 tt_mode;
+ u8 tt_stat;
+ struct cyttsp_touch tch1;
+ u8 touch12_id;
+ struct cyttsp_touch tch2;
+ u8 gest_cnt;
+ u8 gest_id;
+ struct cyttsp_touch tch3;
+ u8 touch34_id;
+ struct cyttsp_touch tch4;
+ u8 tt_undef[3];
+ u8 gest_set;
+ u8 tt_reserved;
+};
+
+/* 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_tch {
+ struct cyttsp_touch *tch;
+ u8 *id;
+};
+
+struct cyttsp_trk {
+ u8 tch;
+ u8 w;
+ u16 x;
+ u16 y;
+ u8 z;
+};
+
+struct cyttsp {
+ struct device *dev;
+ int irq;
+ struct input_dev *input;
+ struct mutex mutex;
+ char phys[32];
+ struct bus_type *bus_type;
+ struct cyttsp_platform_data *platform_data;
+ struct cyttsp_xydata xy_data;
+ struct cyttsp_bootloader_data bl_data;
+ struct cyttsp_sysinfo_data sysinfo_data;
+ struct cyttsp_trk prv_trk[CY_NUM_TRK_ID];
+ struct cyttsp_tch tch_map[CY_NUM_TCH_ID];
+ struct cyttsp_bus_ops *bus_ops;
+ struct timer_list to_timer;
+ bool bl_ready;
+};
+
+struct cyttsp_track_data {
+ struct cyttsp_trk cur_trk[CY_NUM_TRK_ID];
+};
+
+static const u8 bl_command[] = {
+ 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
+};
+
+static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int retval;
+ int tries;
+
+ if (!buf || !length)
+ return -EIO;
+
+ for (tries = 0, retval = -1;
+ tries < CY_NUM_RETRY && (retval < 0);
+ tries++)
+ retval = ts->bus_ops->read(ts->bus_ops, command, length, buf);
+
+ return retval;
+}
+
+static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int retval;
+ if (!buf || !length)
+ return -EIO;
+
+ retval = ts->bus_ops->write(ts->bus_ops, command, length, buf);
+
+ return retval;
+}
+
+static int ttsp_tch_ext(struct cyttsp *ts, void *buf)
+{
+ int retval;
+
+ if (!buf)
+ return -EIO;
+
+ retval = ts->bus_ops->ext(ts->bus_ops, buf);
+
+ return retval;
+}
+
+static irqreturn_t cyttsp_bl_ready_irq(int irq, void *handle)
+{
+ struct cyttsp *ts = handle;
+
+ ts->bl_ready = true;
+ return IRQ_HANDLED;
+}
+
+static int cyttsp_load_bl_regs(struct cyttsp *ts)
+{
+ int retval;
+
+ memset(&(ts->bl_data), 0, sizeof(struct cyttsp_bootloader_data));
+
+ retval = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->bl_data), &(ts->bl_data));
+
+ return retval;
+}
+
+static int cyttsp_bl_app_valid(struct cyttsp *ts)
+{
+ int retval;
+
+ retval = cyttsp_load_bl_regs(ts);
+
+ if (retval < 0)
+ return -ENODEV;
+
+ if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) {
+ if (IS_VALID_APP(ts->bl_data.bl_status)) {
+ dev_dbg(ts->dev, "%s: App found; normal boot\n",
+ __func__);
+ return 0;
+ } else {
+ dev_dbg(ts->dev, "%s: NO APP; load firmware!!\n",
+ __func__);
+ return -ENODEV;
+ }
+ } else if (GET_HSTMODE(ts->bl_data.bl_file) == CY_OPERATE_MODE) {
+ if (!(IS_OPERATIONAL_ERR(ts->bl_data.bl_status))) {
+ dev_dbg(ts->dev, "%s: Operational\n",
+ __func__);
+ return 1;
+ } else {
+ dev_dbg(ts->dev, "%s: Operational failure\n",
+ __func__);
+ return -ENODEV;
+ }
+ } else {
+ dev_dbg(ts->dev, "%s: Non-Operational failure\n",
+ __func__);
+ return -ENODEV;
+ }
+
+}
+
+static bool cyttsp_wait_bl_ready(struct cyttsp *ts, int loop_delay, int max_try)
+{
+ int tries = 0;
+
+ ts->bl_ready = false; /* wait for interrupt to set ready flag */
+
+ while (!ts->bl_ready && (tries++ < max_try))
+ msleep(loop_delay);
+
+ if (tries < max_try)
+ return true;
+ else
+ return false;
+}
+
+static int cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+ int retval;
+ int tries = 0;
+ u8 bl_cmd[sizeof(bl_command)];
+
+ memcpy(bl_cmd, bl_command, sizeof(bl_command));
+ if (ts->platform_data->bl_keys)
+ memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
+ ts->platform_data->bl_keys, sizeof(bl_command));
+
+ dev_dbg(ts->dev,
+ "%s: bl_cmd= "
+ "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, bl_cmd[0], bl_cmd[1], bl_cmd[2],
+ bl_cmd[3], bl_cmd[4], bl_cmd[5], bl_cmd[6],
+ bl_cmd[7], bl_cmd[8], bl_cmd[9], bl_cmd[10]);
+
+ retval = ttsp_write_block_data(ts, CY_REG_BASE,
+ sizeof(bl_cmd), (void *)bl_cmd);
+ if (retval < 0)
+ return retval;
+
+ do {
+ msleep(500);
+ retval = cyttsp_load_bl_regs(ts);
+ } while (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
+ tries++ < 10 &&
+ !(retval < 0));
+
+ if (retval < 0)
+ return retval;
+ else if (GET_BOOTLOADERMODE(ts->bl_data.bl_status))
+ return -ENODEV;
+ else
+ return 0;
+}
+
+static int cyttsp_set_operational_mode(struct cyttsp *ts)
+{
+ int retval;
+ u8 cmd = CY_OPERATE_MODE;
+
+ retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+
+ if (retval < 0)
+ return retval;
+
+ /* wait for TTSP Device to complete switch to Operational mode */
+ msleep(500);
+
+ return retval;
+}
+
+static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+ int retval;
+ u8 cmd = CY_SYSINFO_MODE;
+
+ 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)
+ return retval;
+
+ msleep(500);
+
+ /* read sysinfo registers */
+ retval = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->sysinfo_data), &(ts->sysinfo_data));
+
+ dev_info(ts->dev, "%s: tv=%02X%02X ai=0x%02X%02X "
+ "av=0x%02X%02X ci=0x%02X%02X%02X\n", "cyttsp",
+ 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]);
+
+ return retval;
+}
+
+static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
+{
+ int retval = 0;
+
+ 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_soft_reset(struct cyttsp *ts)
+{
+ int retval;
+ u8 cmd = CY_SOFT_RESET_MODE;
+
+ /* enable bootloader interrupts */
+ retval = request_irq(ts->irq, cyttsp_bl_ready_irq,
+ IRQF_TRIGGER_FALLING, ts->platform_data->name, ts);
+ if (retval)
+ return retval;
+
+ retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+ if (!retval) {
+ if (!cyttsp_wait_bl_ready(ts, 20, 100))
+ retval = -ENODEV;
+ else
+ retval = 0;
+ }
+
+ free_irq(ts->irq, ts);
+ return retval;
+}
+
+static int cyttsp_gesture_setup(struct cyttsp *ts)
+{
+ int retval;
+ u8 gesture_setup;
+
+ /* Init gesture; active distance setup */
+ gesture_setup = ts->platform_data->gest_set;
+ retval = ttsp_write_block_data(ts, CY_REG_GEST_SET,
+ sizeof(gesture_setup), &gesture_setup);
+
+ return retval;
+}
+
+static void cyttsp_init_tch_map(struct cyttsp *ts)
+{
+ ts->tch_map[0].tch = &ts->xy_data.tch1;
+ ts->tch_map[0].id = &ts->xy_data.touch12_id;
+ ts->tch_map[1].tch = &ts->xy_data.tch2;
+ ts->tch_map[1].id = &ts->xy_data.touch12_id;
+ ts->tch_map[2].tch = &ts->xy_data.tch3;
+ ts->tch_map[2].id = &ts->xy_data.touch34_id;
+ ts->tch_map[3].tch = &ts->xy_data.tch4;
+ ts->tch_map[3].id = &ts->xy_data.touch34_id;
+}
+
+static void cyttsp_init_prv_trks(struct cyttsp *ts)
+{
+ /* init the touch structures */
+ memset(ts->prv_trk, CY_NTCH, sizeof(ts->prv_trk));
+}
+
+static void cyttsp_init_cur_trks(struct cyttsp_track_data *t)
+{
+ memset(t->cur_trk, CY_NTCH, sizeof(t->cur_trk));
+}
+
+static int cyttsp_hndshk(struct cyttsp *ts, u8 hst_mode)
+{
+ int retval;
+ u8 cmd;
+
+ cmd = hst_mode & CY_HNDSHK_BIT ?
+ hst_mode & ~CY_HNDSHK_BIT :
+ hst_mode | CY_HNDSHK_BIT;
+
+ retval = ttsp_write_block_data(ts, CY_REG_BASE,
+ sizeof(cmd), (u8 *)&cmd);
+
+ return retval;
+}
+
+static void handle_multi_touch(struct cyttsp_track_data *t, struct cyttsp *ts)
+{
+ u8 id;
+ u8 cnt = 0;
+
+ /* terminate any previous touch where the track
+ * is missing from the current event
+ */
+ for (id = 0; id < CY_NUM_TRK_ID; id++) {
+ if (t->cur_trk[id].tch) {
+ /* put active current track data */
+ input_report_abs(ts->input,
+ ABS_MT_TRACKING_ID, id);
+ input_report_abs(ts->input,
+ ABS_MT_WIDTH_MAJOR, t->cur_trk[id].w);
+ input_report_abs(ts->input,
+ ABS_MT_POSITION_X, t->cur_trk[id].x);
+ input_report_abs(ts->input,
+ ABS_MT_POSITION_Y, t->cur_trk[id].y);
+ input_report_abs(ts->input,
+ ABS_MT_TOUCH_MAJOR, t->cur_trk[id].z);
+ input_mt_sync(ts->input);
+
+ dev_dbg(ts->dev, "%s: MT1: X=%d Y=%d Z=%d\n",
+ __func__,
+ t->cur_trk[id].x,
+ t->cur_trk[id].y,
+ t->cur_trk[id].z);
+ /* save current track data into previous track data */
+ ts->prv_trk[id] = t->cur_trk[id];
+ cnt++;
+ } else if (ts->prv_trk[id].tch) {
+ /* put lift-off previous track data */
+ input_report_abs(ts->input,
+ ABS_MT_TRACKING_ID, id);
+ input_report_abs(ts->input,
+ ABS_MT_WIDTH_MAJOR, ts->prv_trk[id].w);
+ input_report_abs(ts->input,
+ ABS_MT_POSITION_X, ts->prv_trk[id].x);
+ input_report_abs(ts->input,
+ ABS_MT_POSITION_Y, ts->prv_trk[id].y);
+ input_report_abs(ts->input,
+ ABS_MT_TOUCH_MAJOR, CY_NTCH);
+ input_mt_sync(ts->input);
+
+ dev_dbg(ts->dev, "%s: MT1: X=%d Y=%d Z=%d lift-off\n",
+ __func__,
+ ts->prv_trk[id].x,
+ ts->prv_trk[id].y,
+ CY_NTCH);
+ ts->prv_trk[id].tch = CY_NTCH;
+ cnt++;
+ }
+ }
+
+ /* signal the view motion event */
+ if (cnt)
+ input_sync(ts->input);
+}
+
+static void cyttsp_get_xydata(struct cyttsp *ts,
+ struct cyttsp_track_data *t,
+ u8 id, u8 w, u16 x, u16 y, u8 z)
+{
+ struct cyttsp_trk *trk;
+
+ trk = &(t->cur_trk[id]);
+ trk->tch = CY_TCH;
+ trk->w = w;
+ trk->x = x;
+ trk->y = y;
+ trk->z = z;
+}
+
+static int cyttsp_xy_worker(struct cyttsp *ts)
+{
+ u8 cur_tch = 0;
+ u8 tch;
+ struct cyttsp_track_data trk;
+
+ /* get event data from CYTTSP device */
+ if (ttsp_read_block_data(ts,
+ CY_REG_BASE, sizeof(struct cyttsp_xydata), &ts->xy_data))
+ return 0;
+
+ /* touch extension handling */
+ if (ttsp_tch_ext(ts, &ts->xy_data))
+ return 0;
+
+ /* provide flow control handshake */
+ if (ts->platform_data->use_hndshk)
+ if (cyttsp_hndshk(ts, ts->xy_data.hst_mode))
+ return 0;
+
+ cur_tch = GET_NUM_TOUCHES(ts->xy_data.tt_stat);
+
+ if (ts->bus_ops->power_state == CY_IDLE_STATE)
+ return 0;
+ else if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) {
+ return -1;
+ } else if (IS_LARGE_AREA(ts->xy_data.tt_stat) == 1) {
+ /* terminate all active tracks */
+ cur_tch = CY_NTCH;
+ dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
+ } else if (cur_tch > CY_NUM_TCH_ID) {
+ /* terminate all active tracks */
+ cur_tch = CY_NTCH;
+ dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__);
+ } else if (IS_BAD_PKT(ts->xy_data.tt_mode)) {
+ /* terminate all active tracks */
+ cur_tch = CY_NTCH;
+ dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__);
+ }
+
+ /* process the touches */
+ cyttsp_init_cur_trks(&trk);
+
+ for (tch = 0; tch < cur_tch; tch++) {
+ cyttsp_get_xydata(ts, &trk,
+ tch & 0x01 ?
+ (*(ts->tch_map[tch].id) & 0x0F) :
+ (*(ts->tch_map[tch].id) & 0xF0) >> 4,
+ CY_SMALL_TOOL_WIDTH,
+ be16_to_cpu((ts->tch_map[tch].tch)->x),
+ be16_to_cpu((ts->tch_map[tch].tch)->y),
+ (ts->tch_map[tch].tch)->z);
+ }
+
+ handle_multi_touch(&trk, ts);
+
+ return 0;
+}
+
+static irqreturn_t cyttsp_irq(int irq, void *handle)
+{
+ struct cyttsp *ts = handle;
+ int retval;
+
+ retval = cyttsp_xy_worker(ts);
+
+ if (retval < 0) {
+ /* TTSP device has reset back to bootloader mode
+ * Reset driver touch history and restore operational mode
+ */
+ cyttsp_init_prv_trks(ts);
+ retval = cyttsp_exit_bl_mode(ts);
+ if (retval)
+ ts->bus_ops->power_state = CY_IDLE_STATE;
+ dev_info(ts->dev, "%s: %s\n",
+ __func__,
+ (ts->bus_ops->power_state == CY_ACTIVE_STATE) ?
+ "ACTIVE" : "IDLE");
+ }
+ return IRQ_HANDLED;
+}
+
+static int cyttsp_power_on(struct cyttsp *ts)
+{
+ int retval = 0;
+
+ ts->bus_ops->power_state = CY_IDLE_STATE;
+
+ retval = cyttsp_bl_app_valid(ts);
+ if (retval < 0)
+ goto bypass;
+ else if (retval > 0) {
+ retval = cyttsp_soft_reset(ts);
+ if (retval < 0)
+ goto bypass;
+ }
+
+ retval = cyttsp_exit_bl_mode(ts);
+ if (retval < 0)
+ goto bypass;
+
+ retval = cyttsp_set_sysinfo_mode(ts);
+ if (retval < 0)
+ goto bypass;
+
+ retval = cyttsp_set_sysinfo_regs(ts);
+ if (retval < 0)
+ goto bypass;
+
+ retval = cyttsp_set_operational_mode(ts);
+ if (retval < 0)
+ goto bypass;
+
+ /* enable touch interrupts */
+ retval = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ ts->platform_data->name, ts);
+ if (retval < 0)
+ goto bypass;
+
+ /* init gesture setup; required for active distance */
+ retval = cyttsp_gesture_setup(ts);
+
+bypass:
+ if (!retval)
+ ts->bus_ops->power_state = CY_ACTIVE_STATE;
+
+ dev_info(ts->dev, "%s: %s\n",
+ __func__,
+ (ts->bus_ops->power_state == CY_ACTIVE_STATE) ?
+ "ACTIVE" : "IDLE");
+ return retval;
+}
+
+#ifdef CONFIG_PM
+int cyttsp_resume(void *handle)
+{
+ struct cyttsp *ts = handle;
+ int retval = 0;
+ struct cyttsp_xydata xydata;
+
+ if (ts->platform_data->use_sleep && (ts->bus_ops->power_state !=
+ CY_ACTIVE_STATE)) {
+ if (ts->platform_data->wakeup) {
+ retval = ts->platform_data->wakeup();
+ if (retval < 0)
+ dev_dbg(ts->dev, "%s: Error, wakeup failed!\n",
+ __func__);
+ } else {
+ dev_dbg(ts->dev, "%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->bus_ops->power_state = CY_ACTIVE_STATE;
+ }
+ }
+ dev_dbg(ts->dev, "%s: Wake Up %s\n", __func__,
+ (retval < 0) ? "FAIL" : "PASS");
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cyttsp_resume);
+
+int cyttsp_suspend(void *handle)
+{
+ struct cyttsp *ts = handle;
+ u8 sleep_mode = 0;
+ int retval = 0;
+
+ if (ts->platform_data->use_sleep &&
+ (ts->bus_ops->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->bus_ops->power_state = CY_SLEEP_STATE;
+ }
+ dev_dbg(ts->dev, "%s: Sleep Power state is %s\n", __func__,
+ (ts->bus_ops->power_state == CY_ACTIVE_STATE) ?
+ "ACTIVE" :
+ ((ts->bus_ops->power_state == CY_SLEEP_STATE) ?
+ "SLEEP" : "LOW POWER"));
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cyttsp_suspend);
+#endif
+
+int cyttsp_core_init(struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, void *handle)
+{
+ struct input_dev *input_device;
+ struct cyttsp *ts;
+ int retval = 0;
+
+ if ((dev == NULL) || (bus_ops == NULL))
+ goto error_alloc_data;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL) {
+ dev_dbg(ts->dev, "%s: Error, kzalloc\n", __func__);
+ retval = -ENOMEM;
+ goto error_alloc_data;
+ }
+ mutex_init(&ts->mutex);
+ ts->dev = dev;
+ ts->platform_data = dev->platform_data;
+ ts->bus_ops = bus_ops;
+ ts->platform_data->dev = ts->dev;
+
+ if (ts->platform_data->init) {
+ retval = ts->platform_data->init(ts->platform_data, 1);
+ if (retval) {
+ dev_dbg(ts->dev, "%s: Error, platform init failed!\n",
+ __func__);
+ retval = -EIO;
+ goto error_init;
+ }
+ }
+
+ ts->irq = gpio_to_irq(ts->platform_data->irq_gpio);
+ if (ts->irq <= 0) {
+ dev_dbg(ts->dev, "%s: Error, failed to allocate irq\n",
+ __func__);
+ retval = -EIO;
+ goto error_init;
+ }
+
+ /* Create the input device and register it. */
+ input_device = input_allocate_device();
+ if (!input_device) {
+ retval = -ENOMEM;
+ dev_dbg(ts->dev, "%s: Error, failed to allocate input device\n",
+ __func__);
+ retval = -ENODEV;
+ 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->dev;
+ ts->bus_type = bus_ops->dev->bus;
+
+ cyttsp_init_tch_map(ts);
+ cyttsp_init_prv_trks(ts);
+
+ __set_bit(EV_SYN, input_device->evbit);
+ __set_bit(EV_KEY, input_device->evbit);
+ __set_bit(EV_ABS, input_device->evbit);
+
+ 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);
+ input_set_abs_params(input_device, ABS_MT_TRACKING_ID,
+ 0, CY_NUM_TRK_ID, 0, 0);
+
+ retval = input_register_device(input_device);
+ if (retval) {
+ dev_dbg(ts->dev, "%s: Error, failed to register input device\n",
+ __func__);
+ retval = -ENODEV;
+ goto error_input_register_device;
+ }
+
+ retval = cyttsp_power_on(ts);
+
+ if (retval < 0) {
+ dev_dbg(ts->dev, "%s: Error, power on failed!\n", __func__);
+ retval = -ENODEV;
+ goto error_power_on;
+ }
+
+ handle = ts;
+ goto no_error;
+
+error_power_on:
+ free_irq(ts->irq, ts);
+error_input_register_device:
+ input_unregister_device(input_device);
+error_input_allocate_device:
+ if (ts->platform_data->init)
+ ts->platform_data->init(ts->platform_data, 0);
+error_init:
+ mutex_destroy(&ts->mutex);
+ kfree(ts);
+error_alloc_data:
+ handle = NULL;
+no_error:
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cyttsp_core_init);
+
+/* registered in driver struct */
+void cyttsp_core_release(void *handle)
+{
+ struct cyttsp *ts = handle;
+
+ mutex_destroy(&ts->mutex);
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ if (ts->platform_data->init)
+ ts->platform_data->init(ts->platform_data, 0);
+ kfree(ts);
+}
+EXPORT_SYMBOL_GPL(cyttsp_core_release);
+
+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 0000000..43fe438
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.h
@@ -0,0 +1,55 @@
+/* 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 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.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+
+#ifndef __CYTTSP_CORE_H__
+#define __CYTTSP_CORE_H__
+
+#include <linux/kernel.h>
+#include <linux/input/cyttsp.h>
+
+#define CY_NUM_RETRY 4 /* max number of retries for read ops */
+
+
+struct cyttsp_bus_ops {
+ s32 (*write)(void *handle, u8 addr, u8 length, const void *values);
+ s32 (*read)(void *handle, u8 addr, u8 length, void *values);
+ s32 (*ext)(void *handle, void *values);
+ struct device *dev;
+ enum cyttsp_powerstate power_state;
+};
+
+int cyttsp_core_init(struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, void *handle);
+
+void cyttsp_core_release(void *handle);
+#ifdef CONFIG_PM
+int cyttsp_resume(void *handle);
+int cyttsp_suspend(void *handle);
+#endif
+
+#endif /* __CYTTSP_CORE_H__ */
diff --git a/include/linux/input/cyttsp.h b/include/linux/input/cyttsp.h
new file mode 100644
index 0000000..5280252
--- /dev/null
+++ b/include/linux/input/cyttsp.h
@@ -0,0 +1,78 @@
+/* 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 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.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com)
+ *
+ */
+#ifndef _CYTTSP_H_
+#define _CYTTSP_H_
+
+#define CY_SPI_NAME "cyttsp-spi"
+#define CY_I2C_NAME "cyttsp-i2c"
+/* 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
+/*
+ * Active distance in pixels for a gesture to be reported
+ * if set to 0, then all gesture movements are reported
+ * Valid range is 0 - 15
+ */
+#define CY_ACT_DIST_DFLT 8
+#define CY_ACT_DIST CY_ACT_DIST_DFLT
+
+enum cyttsp_gest {
+ CY_GEST_GRP_NONE = 0,
+ CY_GEST_GRP1 = 0x10,
+ CY_GEST_GRP2 = 0x20,
+ CY_GEST_GRP3 = 0x40,
+ CY_GEST_GRP4 = 0x80,
+};
+
+enum cyttsp_powerstate {
+ CY_IDLE_STATE,
+ CY_ACTIVE_STATE,
+ CY_LOW_PWR_STATE,
+ CY_SLEEP_STATE,
+};
+
+struct cyttsp_platform_data {
+ struct device *dev;
+ u32 maxx;
+ u32 maxy;
+ unsigned use_hndshk:1;
+ unsigned use_sleep:1;
+ u8 gest_set; /* Active distance */
+ u8 act_intrvl; /* Active refresh interval; ms */
+ u8 tch_tmout; /* Active touch timeout; ms */
+ u8 lp_intrvl; /* Low power refresh interval; ms */
+ int (*wakeup)(void);
+ int (*init)(struct cyttsp_platform_data *, int on_off);
+ char *name;
+ s16 irq_gpio;
+ u8 *bl_keys;
+};
+
+#endif /* _CYTTSP_H_ */
--
1.7.2.1