@@ -192,6 +192,7 @@ config TOUCHSCREEN_CYPRESS_CYTTSP4
tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
default m
select CYPRESS_CYTTSP4_BUS
+ select TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B
help
Core driver for Cypress TrueTouch(tm) Standard Product
Geneartion4 touchscreen controllers.
@@ -222,6 +223,14 @@ config TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG
Say Y here to enable verbose debug output.
+config TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B
+ tristate "Cypress TrueTouch Gen4 MultiTouch Protocol B"
+ depends on TOUCHSCREEN_CYPRESS_CYTTSP4
+ default m
+ help
+ Cypress TrueTouch(tm) Standard Product Generation4
+ MutliTouch Protocol B support.
+
config TOUCHSCREEN_DA9034
tristate "Touchscreen support for Dialog Semiconductor DA9034"
depends on PMIC_DA903X
@@ -75,12 +75,20 @@ obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_CYPRESS_CYTTSP4_BUS) += cyttsp4_bus.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4) += cyttsp4_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B) += cyttsp4_mt_b.o
+cyttsp4_mt_b-y := cyttsp4_mtb.o cyttsp4_mt_common.o
ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG),y)
CFLAGS_cyttsp4_core.o += -DDEBUG
CFLAGS_cyttsp4_bus.o += -DDEBUG
+CFLAGS_cyttsp4_mtb.o += -DDEBUG
+CFLAGS_cyttsp4_mt_b.o += -DDEBUG
+CFLAGS_cyttsp4_mt_common.o += -DDEBUG
endif
ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG),y)
CFLAGS_cyttsp4_core.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp4_bus.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_mtb.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_mt_b.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_mt_common.o += -DVERBOSE_DEBUG
endif
new file mode 100644
@@ -0,0 +1,612 @@
+/*
+ * cyttsp4_mt_common.c
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * 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 <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_mt_common.h"
+
+static void cyttsp4_lift_all(struct cyttsp4_mt_data *md)
+{
+ if (md->num_prv_tch != 0) {
+ if (md->mt_function.report_slot_liftoff)
+ md->mt_function.report_slot_liftoff(md);
+ /* ICS Lift off button release signal and empty mt */
+ if (md->prv_tch_type != CY_OBJ_HOVER)
+ input_report_key(md->input, BTN_TOUCH, CY_BTN_RELEASED);
+ input_sync(md->input);
+ md->num_prv_tch = 0;
+ }
+}
+
+static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md,
+ int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+ int nbyte;
+ int next;
+
+ for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+ dev_vdbg(&md->ttsp->dev,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+ " xy_data[%d]=%02X(%d) bofs=%d\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next], bofs);
+ *axis = (*axis * 256) + (xy_data[next] >> bofs);
+ next++;
+ }
+
+ *axis &= max - 1;
+
+ dev_vdbg(&md->ttsp->dev,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+ " xy_data[%d]=%02X(%d)\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next]);
+}
+
+static void cyttsp4_get_touch(struct cyttsp4_mt_data *md,
+ struct cyttsp4_touch *touch, u8 *xy_data)
+{
+ struct device *dev = &md->ttsp->dev;
+ struct cyttsp4_sysinfo *si = md->si;
+ enum cyttsp4_tch_abs abs;
+ int tmp;
+ bool flipped;
+
+ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
+ cyttsp4_get_touch_axis(md, &touch->abs[abs],
+ si->si_ofs.tch_abs[abs].size,
+ si->si_ofs.tch_abs[abs].max,
+ xy_data + si->si_ofs.tch_abs[abs].ofs,
+ si->si_ofs.tch_abs[abs].bofs);
+ dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
+ cyttsp4_tch_abs_string[abs],
+ touch->abs[abs], touch->abs[abs]);
+ }
+
+ if (md->pdata->flags & CY_FLAG_FLIP) {
+ tmp = touch->abs[CY_TCH_X];
+ touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y];
+ touch->abs[CY_TCH_Y] = tmp;
+ flipped = true;
+ } else
+ flipped = false;
+
+ if (md->pdata->flags & CY_FLAG_INV_X) {
+ if (flipped)
+ touch->abs[CY_TCH_X] = md->si->si_ofs.max_y -
+ touch->abs[CY_TCH_X];
+ else
+ touch->abs[CY_TCH_X] = md->si->si_ofs.max_x -
+ touch->abs[CY_TCH_X];
+ }
+ if (md->pdata->flags & CY_FLAG_INV_Y) {
+ if (flipped)
+ touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x -
+ touch->abs[CY_TCH_Y];
+ else
+ touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y -
+ touch->abs[CY_TCH_Y];
+ }
+
+ dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
+ __func__, flipped ? "true" : "false",
+ md->pdata->flags & CY_FLAG_INV_X ? "true" : "false",
+ md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false",
+ touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
+ touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
+}
+
+static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch)
+{
+ struct device *dev = &md->ttsp->dev;
+ struct cyttsp4_sysinfo *si = md->si;
+ struct cyttsp4_touch tch;
+ int sig;
+ int i, j, t = 0;
+ int ids[max(CY_TMA1036_MAX_TCH + 1,
+ CY_TMA4XX_MAX_TCH + 1)]; /* add one for hover */
+ int mt_sync_count = 0;
+
+ memset(ids, 0, (si->si_ofs.max_tchs + 1) * sizeof(int));
+ memset(&tch, 0, sizeof(struct cyttsp4_touch));
+ for (i = 0; i < num_cur_tch; i++) {
+ cyttsp4_get_touch(md, &tch, si->xy_data +
+ (i * si->si_ofs.tch_rec_size));
+ if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) ||
+ (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) {
+ dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n",
+ __func__, i, tch.abs[CY_TCH_T],
+ md->pdata->frmwrk->abs[(CY_ABS_ID_OST *
+ CY_NUM_ABS_SET) + CY_MAX_OST]);
+ if (md->mt_function.input_sync)
+ md->mt_function.input_sync(md->input);
+ mt_sync_count++;
+ continue;
+ }
+
+ /*
+ * if any touch is hover, then there is only one touch
+ * so it is OK to check the first touch for hover condition
+ */
+ if ((md->num_prv_tch == 0 && tch.abs[CY_TCH_O] != CY_OBJ_HOVER)
+ || (md->prv_tch_type == CY_OBJ_HOVER
+ && tch.abs[CY_TCH_O] != CY_OBJ_HOVER))
+ input_report_key(md->input, BTN_TOUCH, CY_BTN_PRESSED);
+
+ /* use 0 based track id's */
+ sig = md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0];
+ if (sig != CY_IGNORE_VALUE) {
+ t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST];
+ if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) {
+ dev_dbg(dev, "%s: t=%d e=%d lift-off\n",
+ __func__, t, tch.abs[CY_TCH_E]);
+ goto cyttsp4_get_mt_touches_pr_tch;
+ }
+ if (md->mt_function.input_report)
+ md->mt_function.input_report(md->input, sig, t);
+ ids[t] = true;
+ }
+
+ /* Check if hover on this touch */
+ dev_vdbg(dev, "%s: t=%d z=%d\n", __func__, t,
+ tch.abs[CY_TCH_P]);
+ if (t == CY_ACTIVE_STYLUS_ID) {
+ tch.abs[CY_TCH_P] = 0;
+ dev_dbg(dev, "%s: t=%d z=%d force zero\n", __func__, t,
+ tch.abs[CY_TCH_P]);
+ }
+
+ /* all devices: position and pressure fields */
+ for (j = 0; j <= CY_ABS_W_OST ; j++) {
+ sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) *
+ CY_NUM_ABS_SET) + 0];
+ if (sig != CY_IGNORE_VALUE)
+ input_report_abs(md->input, sig,
+ tch.abs[CY_TCH_X + j]);
+ }
+ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+ /*
+ * TMA400 size and orientation fields:
+ * if pressure is non-zero and major touch
+ * signal is zero, then set major and minor touch
+ * signals to minimum non-zero value
+ */
+ if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0)
+ tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1;
+
+ /* Get the extended touch fields */
+ for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) {
+ sig = md->pdata->frmwrk->abs
+ [((CY_ABS_MAJ_OST + j) *
+ CY_NUM_ABS_SET) + 0];
+ if (sig != CY_IGNORE_VALUE)
+ input_report_abs(md->input, sig,
+ tch.abs[CY_TCH_MAJ + j]);
+ }
+ }
+ if (md->mt_function.input_sync)
+ md->mt_function.input_sync(md->input);
+ mt_sync_count++;
+
+cyttsp4_get_mt_touches_pr_tch:
+ if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE)
+ dev_dbg(dev,
+ "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n",
+ __func__, t,
+ tch.abs[CY_TCH_X],
+ tch.abs[CY_TCH_Y],
+ tch.abs[CY_TCH_P],
+ tch.abs[CY_TCH_MAJ],
+ tch.abs[CY_TCH_MIN],
+ tch.abs[CY_TCH_OR],
+ tch.abs[CY_TCH_E]);
+ else
+ dev_dbg(dev,
+ "%s: t=%d x=%d y=%d z=%d e=%d\n", __func__,
+ t,
+ tch.abs[CY_TCH_X],
+ tch.abs[CY_TCH_Y],
+ tch.abs[CY_TCH_P],
+ tch.abs[CY_TCH_E]);
+ }
+
+ if (md->mt_function.final_sync)
+ md->mt_function.final_sync(md->input, si->si_ofs.max_tchs,
+ mt_sync_count, ids);
+
+ md->num_prv_tch = num_cur_tch;
+ md->prv_tch_type = tch.abs[CY_TCH_O];
+
+ return;
+}
+
+/* read xy_data for all current touches */
+static int cyttsp4_xy_worker(struct cyttsp4_mt_data *md)
+{
+ struct device *dev = &md->ttsp->dev;
+ struct cyttsp4_sysinfo *si = md->si;
+ u8 num_cur_tch;
+ u8 hst_mode;
+ u8 rep_len;
+ u8 rep_stat;
+ u8 tt_stat;
+ int rc = 0;
+
+ /*
+ * Get event data from cyttsp4 device.
+ * The event data includes all data
+ * for all active touches.
+ * Event data also includes button data
+ */
+ /*
+ * Use 2 reads:
+ * 1st read to get mode + button bytes + touch count (core)
+ * 2nd read (optional) to get touch 1 - touch n data
+ */
+ hst_mode = si->xy_mode[CY_REG_BASE];
+ rep_len = si->xy_mode[si->si_ofs.rep_ofs];
+ rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1];
+ tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs];
+ dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__,
+ "hst_mode=", hst_mode, "rep_len=", rep_len,
+ "rep_stat=", rep_stat, "tt_stat=", tt_stat);
+
+ num_cur_tch = GET_NUM_TOUCHES(tt_stat);
+ dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch);
+
+ if (rep_len == 0 && num_cur_tch > 0) {
+ dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n",
+ __func__, rep_len, num_cur_tch);
+ goto cyttsp4_xy_worker_exit;
+ }
+
+ /* read touches */
+ if (num_cur_tch > 0) {
+ rc = cyttsp4_read(md->ttsp, CY_MODE_OPERATIONAL,
+ si->si_ofs.tt_stat_ofs + 1, si->xy_data,
+ num_cur_tch * si->si_ofs.tch_rec_size);
+ if (rc < 0) {
+ dev_err(dev, "%s: read fail on touch regs r=%d\n",
+ __func__, rc);
+ goto cyttsp4_xy_worker_exit;
+ }
+ }
+
+ /* print xy data */
+ cyttsp4_pr_buf(dev, md->pr_buf, si->xy_data, num_cur_tch *
+ si->si_ofs.tch_rec_size, "xy_data");
+
+ /* check any error conditions */
+ if (IS_BAD_PKT(rep_stat)) {
+ dev_dbg(dev, "%s: Invalid buffer detected\n", __func__);
+ rc = 0;
+ goto cyttsp4_xy_worker_exit;
+ } else if (IS_LARGE_AREA(tt_stat)) {
+ /* terminate all active tracks */
+ num_cur_tch = 0;
+ dev_dbg(dev, "%s: Large area detected\n", __func__);
+ } else if (num_cur_tch > si->si_ofs.max_tchs) {
+ if (num_cur_tch > max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)) {
+ /* terminate all active tracks */
+ dev_err(dev, "%s: Num touch err detected (n=%d)\n",
+ __func__, num_cur_tch);
+ num_cur_tch = 0;
+ } else {
+ dev_err(dev, "%s: %s (n=%d c=%d)\n", __func__,
+ "too many tch; set to max tch",
+ num_cur_tch, si->si_ofs.max_tchs);
+ num_cur_tch = si->si_ofs.max_tchs;
+ }
+ }
+
+ /* extract xy_data for all currently reported touches */
+ dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__,
+ num_cur_tch);
+ if (num_cur_tch)
+ cyttsp4_get_mt_touches(md, num_cur_tch);
+ else
+ cyttsp4_lift_all(md);
+
+ dev_vdbg(dev, "%s: done\n", __func__);
+ rc = 0;
+
+cyttsp4_xy_worker_exit:
+ return rc;
+}
+
+static int cyttsp4_mt_attention(struct cyttsp4_device *ttsp)
+{
+ struct device *dev = &ttsp->dev;
+ struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+ int rc = 0;
+
+ dev_vdbg(dev, "%s\n", __func__);
+
+ /* core handles handshake */
+ rc = cyttsp4_xy_worker(md);
+ if (rc < 0)
+ dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static int cyttsp4_startup_attention(struct cyttsp4_device *ttsp)
+{
+ struct device *dev = &ttsp->dev;
+ struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+ int rc = 0;
+
+ dev_vdbg(dev, "%s\n", __func__);
+
+ cyttsp4_lift_all(md);
+ return rc;
+}
+
+static int cyttsp4_mt_open(struct input_dev *input)
+{
+ struct device *dev = input->dev.parent;
+ struct cyttsp4_device *ttsp =
+ container_of(dev, struct cyttsp4_device, dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ pm_runtime_get_sync(dev);
+
+ dev_vdbg(dev, "%s: setup subscriptions\n", __func__);
+
+ /* set up touch call back */
+ cyttsp4_subscribe_attention(ttsp, CY_ATTEN_IRQ,
+ cyttsp4_mt_attention, CY_MODE_OPERATIONAL);
+
+ /* set up startup call back */
+ cyttsp4_subscribe_attention(ttsp, CY_ATTEN_STARTUP,
+ cyttsp4_startup_attention, 0);
+
+ return 0;
+}
+
+static void cyttsp4_mt_close(struct input_dev *input)
+{
+ struct device *dev = input->dev.parent;
+ struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+ struct cyttsp4_device *ttsp =
+ container_of(dev, struct cyttsp4_device, dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ cyttsp4_lift_all(md);
+
+ cyttsp4_unsubscribe_attention(ttsp, CY_ATTEN_IRQ,
+ cyttsp4_mt_attention, CY_MODE_OPERATIONAL);
+
+ cyttsp4_unsubscribe_attention(ttsp, CY_ATTEN_STARTUP,
+ cyttsp4_startup_attention, 0);
+
+ pm_runtime_put(dev);
+}
+
+
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
+static int cyttsp4_mt_suspend(struct device *dev)
+{
+ struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ cyttsp4_lift_all(md);
+ return 0;
+}
+
+static int cyttsp4_mt_resume(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops cyttsp4_mt_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_mt_suspend, cyttsp4_mt_resume)
+ SET_RUNTIME_PM_OPS(cyttsp4_mt_suspend, cyttsp4_mt_resume, NULL)
+};
+
+int cyttsp4_mt_release(struct cyttsp4_device *ttsp)
+{
+ struct device *dev = &ttsp->dev;
+ struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+
+ input_unregister_device(md->input);
+
+ pm_runtime_suspend(dev);
+ pm_runtime_disable(dev);
+
+ dev_set_drvdata(dev, NULL);
+ kfree(md);
+ return 0;
+}
+
+static int cyttsp4_mt_probe(struct cyttsp4_device *ttsp)
+{
+ struct device *dev = &ttsp->dev;
+ struct cyttsp4_mt_data *md;
+ struct cyttsp4_mt_platform_data *pdata = dev_get_platdata(dev);
+ int signal = CY_IGNORE_VALUE;
+ int max_x, max_y, max_p, min, max;
+ int max_x_tmp, max_y_tmp;
+ int i;
+ int rc;
+
+ dev_info(dev, "%s\n", __func__);
+ dev_dbg(dev, "%s: debug on\n", __func__);
+ dev_vdbg(dev, "%s: verbose debug on\n", __func__);
+
+ md = kzalloc(sizeof(*md), GFP_KERNEL);
+ if (md == NULL) {
+ dev_err(dev, "%s: Error, kzalloc\n", __func__);
+ rc = -ENOMEM;
+ goto error_alloc_data_failed;
+ }
+
+ cyttsp4_init_function_ptrs(md);
+
+ md->prv_tch_type = CY_OBJ_STANDARD_FINGER;
+ md->ttsp = ttsp;
+ md->pdata = pdata;
+ dev_set_drvdata(dev, md);
+ /* Create the input device and register it. */
+ dev_vdbg(dev, "%s: Create the input device and register it\n",
+ __func__);
+ md->input = input_allocate_device();
+ if (md->input == NULL) {
+ dev_err(dev, "%s: Error, failed to allocate input device\n",
+ __func__);
+ rc = -ENOSYS;
+ goto error_alloc_failed;
+ }
+
+ md->input->name = ttsp->name;
+ scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev));
+ md->input->phys = md->phys;
+ md->input->dev.parent = &md->ttsp->dev;
+ md->input->open = cyttsp4_mt_open;
+ md->input->close = cyttsp4_mt_close;
+ input_set_drvdata(md->input, md);
+
+ pm_runtime_enable(dev);
+
+ pm_runtime_get_sync(dev);
+ /* get sysinfo */
+ md->si = cyttsp4_request_sysinfo(ttsp);
+ pm_runtime_put(dev);
+
+ if (md->si == NULL) {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+ __func__, md->si);
+ rc = -ENODEV;
+ goto error_get_sysinfo;
+ }
+
+ dev_vdbg(dev, "%s: Initialize event signals\n", __func__);
+ __set_bit(EV_ABS, md->input->evbit);
+ __set_bit(EV_REL, md->input->evbit);
+ __set_bit(EV_KEY, md->input->evbit);
+ bitmap_fill(md->input->absbit, ABS_MAX);
+ __set_bit(BTN_TOUCH, md->input->keybit);
+
+ /* If virtualkeys enabled, don't use all screen */
+ if (md->pdata->flags & CY_FLAG_VKEYS) {
+ max_x_tmp = CY_VKEYS_X;
+ max_y_tmp = CY_VKEYS_Y;
+ } else {
+ max_x_tmp = md->si->si_ofs.max_x;
+ max_y_tmp = md->si->si_ofs.max_y;
+ }
+
+ /* get maximum values from the sysinfo data */
+ if (md->pdata->flags & CY_FLAG_FLIP) {
+ max_x = max_y_tmp - 1;
+ max_y = max_x_tmp - 1;
+ } else {
+ max_x = max_x_tmp - 1;
+ max_y = max_y_tmp - 1;
+ }
+ max_p = md->si->si_ofs.max_p;
+
+ /* set event signal capabilities */
+ for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) {
+ signal = md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST];
+ if (signal != CY_IGNORE_VALUE) {
+ min = md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_MIN_OST];
+ max = md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_MAX_OST];
+ if (i == CY_ABS_ID_OST) {
+ /* shift track ids down to start at 0 */
+ max = max - min;
+ min = min - min;
+ } else if (i == CY_ABS_X_OST)
+ max = max_x;
+ else if (i == CY_ABS_Y_OST)
+ max = max_y;
+ else if (i == CY_ABS_P_OST)
+ max = max_p;
+ input_set_abs_params(md->input, signal, min, max,
+ md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+ md->pdata->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+ dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n",
+ __func__, signal, min, max);
+ if ((i == CY_ABS_ID_OST) &&
+ (md->si->si_ofs.tch_rec_size <
+ CY_TMA4XX_TCH_REC_SIZE))
+ break;
+ }
+ }
+
+ rc = md->mt_function.input_register_device(md->input,
+ md->si->si_ofs.max_tchs);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, failed register input device r=%d\n",
+ __func__, rc);
+ goto error_init_input;
+ }
+
+ dev_dbg(dev, "%s: OK\n", __func__);
+ return 0;
+
+error_init_input:
+ input_free_device(md->input);
+error_get_sysinfo:
+ pm_runtime_suspend(dev);
+ pm_runtime_disable(dev);
+ input_set_drvdata(md->input, NULL);
+error_alloc_failed:
+ kfree(md);
+error_alloc_data_failed:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+struct cyttsp4_driver cyttsp4_mt_driver = {
+ .probe = cyttsp4_mt_probe,
+ .remove = cyttsp4_mt_release,
+ .driver = {
+ .name = CYTTSP4_MT_NAME,
+ .bus = &cyttsp4_bus_type,
+ .pm = &cyttsp4_mt_pm_ops,
+ },
+};
+
new file mode 100644
@@ -0,0 +1,76 @@
+/*
+ * cyttsp4_mt_common.h
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * 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 <ttdrivers@cypress.com>
+ *
+ */
+
+#include <linux/cyttsp4_bus.h>
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/cyttsp4_core.h>
+#include <linux/cyttsp4_mt.h>
+#include "cyttsp4_regs.h"
+
+struct cyttsp4_mt_data;
+struct cyttsp4_mt_function {
+ int (*mt_release)(struct cyttsp4_device *ttsp);
+ int (*mt_probe)(struct cyttsp4_device *ttsp,
+ struct cyttsp4_mt_data *md);
+ void (*report_slot_liftoff)(struct cyttsp4_mt_data *md);
+ void (*input_sync)(struct input_dev *input);
+ void (*input_report)(struct input_dev *input, int sig, int t);
+ void (*final_sync)(struct input_dev *input, int max_tchs,
+ int mt_sync_count, int *ids);
+ int (*input_register_device)(struct input_dev *input, int max_tchs);
+};
+
+struct cyttsp4_mt_data {
+ struct cyttsp4_device *ttsp;
+ struct cyttsp4_mt_platform_data *pdata;
+ struct cyttsp4_sysinfo *si;
+ struct input_dev *input;
+ struct cyttsp4_mt_function mt_function;
+ char phys[NAME_MAX];
+ int num_prv_tch;
+ int prv_tch_type;
+#ifdef VERBOSE_DEBUG
+ u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+};
+
+extern void cyttsp4_init_function_ptrs(struct cyttsp4_mt_data *md);
+extern struct cyttsp4_driver cyttsp4_mt_driver;
+
new file mode 100644
@@ -0,0 +1,107 @@
+/*
+ * cyttsp4_mtb.c
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * 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 <ttdrivers@cypress.com>
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/input/mt.h>
+
+#include "cyttsp4_mt_common.h"
+
+static void cyttsp4_final_sync(struct input_dev *input, int max_tchs,
+ int mt_sync_count, int *ids)
+{
+ int t;
+
+ for (t = 0; t < max_tchs + 1; t++) {
+ if (ids[t])
+ continue;
+ input_mt_slot(input, t);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ }
+
+ input_sync(input);
+}
+
+static void cyttsp4_input_report(struct input_dev *input, int sig, int t)
+{
+ input_mt_slot(input, t);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+}
+
+static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md)
+{
+ struct cyttsp4_sysinfo *si = md->si;
+ int t;
+
+ if (md->num_prv_tch == 0)
+ return;
+
+ for (t = 0; t < si->si_ofs.max_tchs + 1; t++) {
+ input_mt_slot(md->input, t);
+ input_mt_report_slot_state(md->input,
+ MT_TOOL_FINGER, false);
+ }
+}
+
+static int cyttsp4_input_register_device(struct input_dev *input, int max_tchs)
+{
+ /* max num slots equals max touches + 1 for hover */
+ input_mt_init_slots(input, max_tchs + 1);
+ return input_register_device(input);
+}
+
+void cyttsp4_init_function_ptrs(struct cyttsp4_mt_data *md)
+{
+ md->mt_function.report_slot_liftoff = cyttsp4_report_slot_liftoff;
+ md->mt_function.final_sync = cyttsp4_final_sync;
+ md->mt_function.input_sync = NULL;
+ md->mt_function.input_report = cyttsp4_input_report;
+ md->mt_function.input_register_device = cyttsp4_input_register_device;
+}
+
+static int __init cyttsp4_mt_init(void)
+{
+ int rc;
+ cyttsp4_mt_driver.driver.owner = THIS_MODULE;
+ rc = cyttsp4_register_driver(&cyttsp4_mt_driver);
+ pr_info("%s: Cypress TTSP MT v4 multi-touch (Built %s @ %s), rc=%d\n",
+ __func__, __DATE__, __TIME__, rc);
+ return rc;
+}
+module_init(cyttsp4_mt_init);
+
+static void __exit cyttsp4_mt_exit(void)
+{
+ cyttsp4_unregister_driver(&cyttsp4_mt_driver);
+ pr_info("%s: module exit\n", __func__);
+}
+module_exit(cyttsp4_mt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard 2D multi-touch driver");
+MODULE_AUTHOR("Cypress Semiconductor");
new file mode 100644
@@ -0,0 +1,72 @@
+/*
+ * cyttsp4_mt.h
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * 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 <ttdrivers@cypress.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP4_MT_H
+#define _LINUX_CYTTSP4_MT_H
+
+#define CYTTSP4_MT_NAME "cyttsp4_mt"
+
+/* abs settings */
+#define CY_IGNORE_VALUE 0xFFFF
+/* abs signal capabilities offsets in the frameworks array */
+enum cyttsp4_sig_caps {
+ CY_SIGNAL_OST,
+ CY_MIN_OST,
+ CY_MAX_OST,
+ CY_FUZZ_OST,
+ CY_FLAT_OST,
+ CY_NUM_ABS_SET /* number of signal capability fields */
+};
+
+/* abs axis signal offsets in the framworks array */
+enum cyttsp4_sig_ost {
+ CY_ABS_X_OST,
+ CY_ABS_Y_OST,
+ CY_ABS_P_OST,
+ CY_ABS_W_OST,
+ CY_ABS_ID_OST,
+ CY_ABS_MAJ_OST,
+ CY_ABS_MIN_OST,
+ CY_ABS_OR_OST,
+ CY_NUM_ABS_OST /* number of abs signals */
+};
+
+struct touch_framework {
+ const uint16_t *abs;
+ uint8_t size;
+ uint8_t enable_vkeys;
+} __packed;
+
+struct cyttsp4_mt_platform_data {
+ struct touch_framework *frmwrk;
+ unsigned short flags;
+ char const *inp_dev_name;
+};
+
+#endif /* _LINUX_CYTTSP4_MT_H */