From patchwork Tue Aug 7 13:10:25 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ferruh Yigit X-Patchwork-Id: 1285721 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 064E13FC23 for ; Tue, 7 Aug 2012 13:16:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754700Ab2HGNPz (ORCPT ); Tue, 7 Aug 2012 09:15:55 -0400 Received: from relay.ihostexchange.net ([66.46.182.57]:12810 "EHLO relay.ihostexchange.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753661Ab2HGNPy convert rfc822-to-8bit (ORCPT ); Tue, 7 Aug 2012 09:15:54 -0400 Received: from ferruhy-laptop.cypress.com (157.95.211.50) by smtp.ihostexchange.net (66.46.182.50) with Microsoft SMTP Server (TLS) id 8.3.213.0; Tue, 7 Aug 2012 09:10:45 -0400 From: Ferruh Yigit To: Dmitry Torokhov CC: Kevin McNeely , Ferruh YIGIT , Javier Martinez Canillas , Henrik Rydberg , Shawn Landden , Ashish Jangam , Olivier Sobrie , linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/4] Input: cyttsp4 - MultiTouch driver for Cypress TMA4XX touchscreen devices Date: Tue, 7 Aug 2012 16:10:25 +0300 Message-ID: <1344345032-30537-1-git-send-email-fery@cypress.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org From: Ferruh YIGIT Cypress TrueTouch(tm) Standard Product controllers, Generetion4 devices, MutliTouch driver. Subscribes to core driver and converts touch information to OS specific touch events. This module is supports multi-touch protocol type B reports. Signed-off-by: Ferruh YIGIT --- drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/Makefile | 8 + drivers/input/touchscreen/cyttsp4_mt_common.c | 612 +++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp4_mt_common.h | 76 +++ drivers/input/touchscreen/cyttsp4_mtb.c | 107 +++++ include/linux/cyttsp4_mt.h | 72 +++ 6 files changed, 884 insertions(+) create mode 100644 drivers/input/touchscreen/cyttsp4_mt_common.c create mode 100644 drivers/input/touchscreen/cyttsp4_mt_common.h create mode 100644 drivers/input/touchscreen/cyttsp4_mtb.c create mode 100644 include/linux/cyttsp4_mt.h -- 1.7.9.5 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. -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 6fa7278..57a38f8 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -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 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d12965b..8feae16 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -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 diff --git a/drivers/input/touchscreen/cyttsp4_mt_common.c b/drivers/input/touchscreen/cyttsp4_mt_common.c new file mode 100644 index 0000000..e512f9c --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_mt_common.c @@ -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 + * + */ + +#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, + }, +}; + diff --git a/drivers/input/touchscreen/cyttsp4_mt_common.h b/drivers/input/touchscreen/cyttsp4_mt_common.h new file mode 100644 index 0000000..d363e9e0 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_mt_common.h @@ -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 + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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; + diff --git a/drivers/input/touchscreen/cyttsp4_mtb.c b/drivers/input/touchscreen/cyttsp4_mtb.c new file mode 100644 index 0000000..7a0b7b1 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_mtb.c @@ -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 + * + */ + +#include +#include + +#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"); diff --git a/include/linux/cyttsp4_mt.h b/include/linux/cyttsp4_mt.h new file mode 100644 index 0000000..4339544 --- /dev/null +++ b/include/linux/cyttsp4_mt.h @@ -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 + * + */ + +#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 */