2010-11-09 18:26:14

by Kevin McNeely

[permalink] [raw]
Subject: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

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 <[email protected]>
---
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 http://www.cypress.com <[email protected]>
+ *
+ */
+
+#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 http://www.cypress.com <[email protected]>
+ *
+ */
+
+
+#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 http://www.cypress.com ([email protected])
+ *
+ */
+#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


2010-11-15 16:47:31

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

On 11/09/2010 07:25 PM, Kevin McNeely wrote:

> 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 <[email protected]>

> ---


Thanks for your submission. Please find comments on MT inline. On a general
note, it is strongly recommended that this driver be converted to the MT slots
(type B) protocol.

> +
> +/* 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;


I take it the gesture functionality is not in use in this driver?

> + u8 tt_reserved;
> +};
> +
> +
> +struct cyttsp_tch {
> + struct cyttsp_touch *tch;
> + u8 *id;
> +};


Given how this mapping is used, it could possibly be dropped altogether. See
further comment on cyttsp_init_tch_map().

> +

> +struct cyttsp_trk {
> + u8 tch;
> + u8 w;


It seems the device does not report contact width, so it is better not reported
at all.

> + 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];


Apart from the ids, what information is used from the previous frame?

> +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;
> +}


What does this initialization actually do?

> +
> +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;
> +}


Calling cyttsp_get_xydata() four times with special arguments would make this
function unnecessary.

> +
> +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);


This value does not seem to be reported by the device and should be dropped.

> + 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);


Reporting tracking id here unfortunately does not work very well. With the type
A protocol, ids not reported will automatically be removed, and

> + 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);


checking for zero touch like this only applies for drivers not reporting
tracking id.

> + 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);
> +}


Since the device does its own tracking, the driver would benefit greatly from
using the type B protocol.

> +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);
> + }


How about expanding this loop with special arguments instead?

> +/*
> + * 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


These do not seem to be used anywhere.

> +
> +enum cyttsp_gest {
> + CY_GEST_GRP_NONE = 0,
> + CY_GEST_GRP1 = 0x10,
> + CY_GEST_GRP2 = 0x20,
> + CY_GEST_GRP3 = 0x40,
> + CY_GEST_GRP4 = 0x80,
> +};


Neither do these.

Thanks,
Henrik

2010-11-19 18:02:41

by Kevin McNeely

[permalink] [raw]
Subject: RE: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

Hello Henrik,

Thank you for reviewing this code submission.

I have replied to each of your comments below.

I would like to resubmit to include the changes.

Thanks and best regards,
Kevin


> -----Original Message-----
> From: Henrik Rydberg [mailto:[email protected]]
> Sent: Monday, November 15, 2010 8:46 AM
> To: Kevin McNeely
> Cc: Dmitry Torokhov; David Brown; Trilok Soni; Samuel Ortiz; Eric
Miao;
> Simtec Linux Team; Luotao Fu; [email protected]; linux-
> [email protected]
> Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver
>
> On 11/09/2010 07:25 PM, Kevin McNeely wrote:
>
> > 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 <[email protected]>
>
> > ---
>
>
> Thanks for your submission. Please find comments on MT inline. On a
> general
> note, it is strongly recommended that this driver be converted to the
> MT slots
> (type B) protocol.

I will resubmit with Protocol A only at this time. I will remove the
Tracking ID's.

>
> > +
> > +/* 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;
>
>
> I take it the gesture functionality is not in use in this driver?

Correct, I will remove all instances and mentions of the gesture. The
field of interest is the active distance for touches.

>
> > + u8 tt_reserved;
> > +};
> > +
> > +
> > +struct cyttsp_tch {
> > + struct cyttsp_touch *tch;
> > + u8 *id;
> > +};
>
>
> Given how this mapping is used, it could possibly be dropped
> altogether. See
> further comment on cyttsp_init_tch_map().

The mapping is to allow number of touch growth with extra register
information. The mapping is to allow looping on the touch processing.

>
> > +
>
> > +struct cyttsp_trk {
> > + u8 tch;
> > + u8 w;
>
>
> It seems the device does not report contact width, so it is better not
> reported
> at all.

The width will be removed.

>
> > + 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];
>
>
> Apart from the ids, what information is used from the previous frame?

The previous X, Y.

>
> > +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;
> > +}
>
>
> What does this initialization actually do?

This will be replaced with an active distance setup function, which is
the sole intent of the current gesture setup function. We are really
just initializing the active distance field.

>
> > +
> > +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;
> > +}
>
>
> Calling cyttsp_get_xydata() four times with special arguments would
> make this
> function unnecessary.

The cyttsp_get_xydata() makes a single I2C read transaction which
contains touch information for all touches currently tracked by the
device.

>
> > +
> > +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);
>
>
> This value does not seem to be reported by the device and should be
> dropped.

This will be dropped.

>
> > + 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);
>
>
> Reporting tracking id here unfortunately does not work very well. With
> the type
> A protocol, ids not reported will automatically be removed, and

This will be Protocol A only. The Track Id reporting will be removed.

>
> > + 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);
>
>
> checking for zero touch like this only applies for drivers not
> reporting
> tracking id.

This will now be necessary for Protocol A only.

>
> > + 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);
> > +}
>
>
> Since the device does its own tracking, the driver would benefit
> greatly from
> using the type B protocol.

Current requests are for Protocol A only. Cypress will followup with a
Protocol B driver submission.

>
> > +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);
> > + }
>
>
> How about expanding this loop with special arguments instead?

The mapping method is intended to hide the offset information in the
current registry and be extensible for future updates for more touches.

>
> > +/*
> > + * 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
>
>
> These do not seem to be used anywhere.

This will be refined and added to the code which checks for valid
operational mode.

>
> > +
> > +enum cyttsp_gest {
> > + CY_GEST_GRP_NONE = 0,
> > + CY_GEST_GRP1 = 0x10,
> > + CY_GEST_GRP2 = 0x20,
> > + CY_GEST_GRP3 = 0x40,
> > + CY_GEST_GRP4 = 0x80,
> > +};
>
>
> Neither do these.

These will be removed.

>
> Thanks,
> Henrik

Thanks again for your review.
Best regards,
Kevin


---------------------------------------------------------------
This message and any attachments may contain Cypress (or its
subsidiaries) confidential information. If it has been received
in error, please advise the sender and immediately delete this
message.
---------------------------------------------------------------

2010-12-01 07:22:40

by Trilok Soni

[permalink] [raw]
Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

Hi Henrik,

On 11/19/2010 11:09 PM, Kevin McNeely wrote:
> Hello Henrik,
>
> Thank you for reviewing this code submission.
>
> I have replied to each of your comments below.
>
> I would like to resubmit to include the changes.
>
> Thanks and best regards,
> Kevin
>
>
>> -----Original Message-----
>> From: Henrik Rydberg [mailto:[email protected]]
>> Sent: Monday, November 15, 2010 8:46 AM
>> To: Kevin McNeely
>> Cc: Dmitry Torokhov; David Brown; Trilok Soni; Samuel Ortiz; Eric
> Miao;
>> Simtec Linux Team; Luotao Fu; [email protected]; linux-
>> [email protected]
>> Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver
>>
>> On 11/09/2010 07:25 PM, Kevin McNeely wrote:
>>
>>> 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 <[email protected]>
>>
>>> ---
>>
>>
>> Thanks for your submission. Please find comments on MT inline. On a
>> general
>> note, it is strongly recommended that this driver be converted to the
>> MT slots
>> (type B) protocol.
>
> I will resubmit with Protocol A only at this time. I will remove the
> Tracking ID's.
>

Any comments on this?

---Trilok Soni

--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-01 14:39:42

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

>>>

>>> Thanks for your submission. Please find comments on MT inline. On a
>>> general
>>> note, it is strongly recommended that this driver be converted to the
>>> MT slots
>>> (type B) protocol.
>>
>> I will resubmit with Protocol A only at this time. I will remove the
>> Tracking ID's.
>>
>
> Any comments on this?


It is not optimal, but it is not wrong. If the plan is to support kernels 2.6.38
and later, I strongly recommend using the type B protocol, as already stated.

Henrik

2010-12-02 00:00:24

by Kevin McNeely

[permalink] [raw]
Subject: RE: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

Hello Henrik,

Cypress would like to submit as Protocol A now and provide a Protocol B
update later.

May I submit the updated Core Driver with the Protocol A only (tracking
ID's removed)? The updated Core Driver also includes other changes as
you recommended.

Thank you,
Kevin

> -----Original Message-----
> From: Henrik Rydberg [mailto:[email protected]]
> Sent: Wednesday, December 01, 2010 6:39 AM
> To: Trilok Soni
> Cc: Kevin McNeely; Dmitry Torokhov; David Brown; Samuel Ortiz; Eric
> Miao; Simtec Linux Team; Luotao Fu; [email protected];
linux-
> [email protected]
> Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver
>
> >>>
>
> >>> Thanks for your submission. Please find comments on MT inline. On
a
> >>> general
> >>> note, it is strongly recommended that this driver be converted to
> the
> >>> MT slots
> >>> (type B) protocol.
> >>
> >> I will resubmit with Protocol A only at this time. I will remove
> the
> >> Tracking ID's.
> >>
> >
> > Any comments on this?
>
>
> It is not optimal, but it is not wrong. If the plan is to support
> kernels 2.6.38
> and later, I strongly recommend using the type B protocol, as already
> stated.
>
> Henrik

---------------------------------------------------------------
This message and any attachments may contain Cypress (or its
subsidiaries) confidential information. If it has been received
in error, please advise the sender and immediately delete this
message.
---------------------------------------------------------------

2010-12-02 00:02:04

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

On 12/02/2010 12:59 AM, Kevin McNeely wrote:

> Hello Henrik,
>
> Cypress would like to submit as Protocol A now and provide a Protocol B
> update later.
>
> May I submit the updated Core Driver with the Protocol A only (tracking
> ID's removed)? The updated Core Driver also includes other changes as
> you recommended.


Absolutely, you are welcome.

Henrik

2010-12-02 00:34:25

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH] touchscreen: Cypress TTSP G3 MTDEV Core Driver

Hi Kevin,

On Tue, Nov 09, 2010 at 10:25:17AM -0800, Kevin McNeely wrote:
>
> +config TOUCHSCREEN_CYTTSP_CORE
> + bool "Cypress TTSP touchscreen core"

Tristate please - i see no reason why it can't be compiled as a module.
Same goes for I2C and SPI parts.

> + help
> + Say Y here if you have a Cypress TTSP touchscreen
> + connected to your system.
> +
> + If unsure, say N.
> +

To compile this driver as a module...

> +
> +/* Touch structure */
> +struct cyttsp_touch {
> + u16 x __attribute__ ((packed));
> + u16 y __attribute__ ((packed));

Why packed? Natural alignment will give exactly same layout.

> +
> +struct cyttsp {
> + struct device *dev;
> + int irq;
> + struct input_dev *input;
> + struct mutex mutex;
> + char phys[32];
> + struct bus_type *bus_type;

const.

> + struct cyttsp_platform_data *platform_data;

const.

> + 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;

const.

> + struct timer_list to_timer;
> + bool bl_ready;
> +};
> +
> +struct cyttsp_track_data {
> + struct cyttsp_trk cur_trk[CY_NUM_TRK_ID];

Do you expect to extend this? Otherwise just pass around struct
cyttsp_trk *.

> +
> +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);

Ewwww! wait_event_timeout() or wait_for_completion_timeout().

> +
> +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)) {

Too many parens.

> +
> +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;
> + }

Oh, yes, this should be a completion.

> +
> + free_irq(ts->irq, ts);
> + return retval;
> +}
> +
> +

...

> +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));
> +}

Do we really need these helpers?

> +
> +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);


You know, open-coding instead of passing so many complex arguments would
be cleaner.

> + }
> +
> + 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);

This should probably go into input->open() method. No need to power up
until there are users.

> +
> + 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;

Why?

Btw, have it return struct cyttsp * and check for errors with IS_ERR().

> +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);

Maybe call it platform_data->exit()?

> + 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 http://www.cypress.com <[email protected]>
> + *
> + */
> +
> +
> +#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 http://www.cypress.com ([email protected])
> + *
> + */
> +#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;

Hm, platform data might be shared, not a good idea to have device
pointer here.

> + u32 maxx;
> + u32 maxy;
> + unsigned use_hndshk:1;

bool.

> + unsigned use_sleep:1;

bool.

> + 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;
> +};
> +

Thanks.

--
Dmitry