From patchwork Thu Sep 8 11:40:29 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhang Jiejing X-Patchwork-Id: 1129412 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.4) with ESMTP id p88BdRJf001651 for ; Thu, 8 Sep 2011 11:42:53 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758322Ab1IHLmx (ORCPT ); Thu, 8 Sep 2011 07:42:53 -0400 Received: from am1ehsobe004.messaging.microsoft.com ([213.199.154.207]:2882 "EHLO AM1EHSOBE004.bigfish.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758318Ab1IHLmw (ORCPT ); Thu, 8 Sep 2011 07:42:52 -0400 Received: from mail1-am1-R.bigfish.com (10.3.201.248) by AM1EHSOBE004.bigfish.com (10.3.204.24) with Microsoft SMTP Server id 14.1.225.22; Thu, 8 Sep 2011 11:42:47 +0000 Received: from mail1-am1 (localhost.localdomain [127.0.0.1]) by mail1-am1-R.bigfish.com (Postfix) with ESMTP id 9B6D1398182; Thu, 8 Sep 2011 11:42:47 +0000 (UTC) X-SpamScore: 8 X-BigFish: VS8(zz78fbmc8kzz1202hzz8275bhz2dh2a8h668h839h61h) X-Spam-TCS-SCL: 0:0 X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPVD:NLI; H:mail.freescale.net; RD:none; EFVD:NLI Received: from mail1-am1 (localhost.localdomain [127.0.0.1]) by mail1-am1 (MessageSwitch) id 1315482167218368_1524; Thu, 8 Sep 2011 11:42:47 +0000 (UTC) Received: from AM1EHSMHS006.bigfish.com (unknown [10.3.201.245]) by mail1-am1.bigfish.com (Postfix) with ESMTP id 2F6224F0051; Thu, 8 Sep 2011 11:42:47 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by AM1EHSMHS006.bigfish.com (10.3.207.106) with Microsoft SMTP Server (TLS) id 14.1.225.22; Thu, 8 Sep 2011 11:42:44 +0000 Received: from az33smr02.freescale.net (10.64.34.200) by 039-SN1MMR1-003.039d.mgd.msft.net (10.84.1.16) with Microsoft SMTP Server id 14.1.323.7; Thu, 8 Sep 2011 06:42:43 -0500 Received: from shdroid1.ap.freescale.net (udp160409uds.ap.freescale.net [10.192.224.88]) by az33smr02.freescale.net (8.13.1/8.13.0) with ESMTP id p88BgegK015232; Thu, 8 Sep 2011 06:42:41 -0500 (CDT) From: Zhang Jiejing To: , , , Subject: [PATCH] input: add EETI eGalax I2C capacitive multi touch driver. Date: Thu, 8 Sep 2011 19:40:29 +0800 Message-ID: <1315482029-13761-1-git-send-email-jiejing.zhang@freescale.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <20110812203003.GA8995@polaris.bitmath.org> References: <20110812203003.GA8995@polaris.bitmath.org> MIME-Version: 1.0 X-OriginatorOrg: freescale.com Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Thu, 08 Sep 2011 11:42:53 +0000 (UTC) this patch adds EETI eGalax serial multi touch controller driver. EETI eGalax serial touch screen controller is a I2C based multiple capacitive touch screen controller, it can supports 5 touch events maximum. Signed-off-by: Zhang Jiejing --- V1 changes: * use mt protocol B for report pointer. * get the pressure value from pointer. * use macro to define X,Y max value. V2 changes: * change MODE_SINGLE to a meanful name MODE_MOUSE * add some comment on each mode to avoid misunderstand --- drivers/input/touchscreen/Kconfig | 10 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/egalax_ts.c | 299 +++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+), 0 deletions(-) create mode 100644 drivers/input/touchscreen/egalax_ts.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index cabd9e5..92f02b0 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -177,6 +177,16 @@ config TOUCHSCREEN_EETI To compile this driver as a module, choose M here: the module will be called eeti_ts. +config TOUCHSCREEN_EGALAX + tristate "EETI eGalax multi-touch panel support" + depends on I2C + help + Say Y here to enable support for I2C connected EETI + eGalax multi-touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts. + config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 282d6f7..a787ebc 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c new file mode 100644 index 0000000..9f0a527 --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts.c @@ -0,0 +1,299 @@ +/* + * Driver for EETI eGalax Multiple Touch Controller + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * based on max11801_ts.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* EETI eGalax serial touch screen controller is a I2C based multiple + * touch screen controller, it can supports 5 pointer multiple touch. */ + +/* TODO: + - auto idle mode support +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Mouse Mode: some panel may configure the controller to mouse mode, + * which can only one point at a given time. */ +#define REPORT_MODE_MOUSE 0x1 +/* Vendor Mode: this mode is used to transfer some vendor specify + * messages, it was ignore in this driver. */ +#define REPORT_MODE_VENDOR 0x3 +/* Multiple Touch Mode */ +#define REPORT_MODE_MTTOUCH 0x4 + +#define MAX_SUPPORT_POINTS 5 + +#define EVENT_VALID_OFFSET 7 +#define EVENT_VAILD_MASK (0x1 << EVENT_VALID_OFFSET) +#define EVENT_ID_OFFSET 2 +#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET) +#define EVENT_IN_RANGE (0x1 << 1) +#define EVENT_DOWN_UP (0X1 << 0) + +#define MAX_I2C_DATA_LEN 10 + +#define EGALAX_MAX_X 32760 +#define EGALAX_MAX_Y 32760 + +struct egalax_ts { + struct i2c_client *client; + struct input_dev *input_dev; +}; + +static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) +{ + struct egalax_ts *data = dev_id; + struct input_dev *input_dev = data->input_dev; + struct i2c_client *client = data->client; + u8 buf[MAX_I2C_DATA_LEN]; + int id, ret, x, y, z; + bool down, valid; + u8 state; + +retry: + ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN); + if (ret == -EAGAIN) + goto retry; + + if (ret < 0) + return IRQ_HANDLED; + + if (buf[0] != REPORT_MODE_VENDOR + && buf[0] != REPORT_MODE_MOUSE + && buf[0] != REPORT_MODE_MTTOUCH) { + /* invalid point */ + return IRQ_HANDLED; + } + + if (buf[0] == REPORT_MODE_VENDOR) { + dev_dbg(&client->dev, "vendor message, ignore...\n"); + return IRQ_HANDLED; + } + + state = buf[1]; + x = (buf[3] << 8) | buf[2]; + y = (buf[5] << 8) | buf[4]; + z = (buf[7] << 8) | buf[6]; /* only valid in multitouch mode. */ + + if (buf[0] == REPORT_MODE_MOUSE) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_key(input_dev, BTN_TOUCH, !!state); + input_sync(input_dev); + return IRQ_HANDLED; + } + + /* deal with multiple touch */ + valid = state & EVENT_VAILD_MASK; + id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; + down = state & EVENT_DOWN_UP; + + if (!valid || id > MAX_SUPPORT_POINTS) { + dev_dbg(&client->dev, "point invalid\n"); + return IRQ_HANDLED; + } + + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down); + + dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d", + (down ? "down" : "up"), id, x, y, z); + + if (down) { + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, z); + } + + input_mt_report_pointer_emulation(input_dev, true); + input_sync(input_dev); + return IRQ_HANDLED; +} + +/* wake up controller by an falling edge of interrupt gpio. */ +static int egalax_wake_up_device(struct i2c_client *client) +{ + int gpio = irq_to_gpio(client->irq); + int ret; + + ret = gpio_request(gpio, "egalax_irq"); + if (ret < 0) { + dev_err(&client->dev, "request gpio failed," + "cannot wake up controller:%d\n", ret); + return ret; + } + /* wake up controller via an falling edge on IRQ gpio. */ + gpio_direction_output(gpio, 0); + gpio_set_value(gpio, 1); + /* controller should be waken up, return irq. */ + gpio_direction_input(gpio); + gpio_free(gpio); + return 0; +} + +static int egalax_firmware_version(struct i2c_client *client) +{ + static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; + int ret; + ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN); + if (ret < 0) + return ret; + return 0; +} + +static int __devinit egalax_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct egalax_ts *data; + struct input_dev *input_dev; + int ret; + + data = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free_data; + } + + data->client = client; + data->input_dev = input_dev; + /* controller may be in sleep, wake it up. */ + egalax_wake_up_device(client); + ret = egalax_firmware_version(client); + if (ret < 0) { + dev_err(&client->dev, + "egalax_ts: failed to read firmware version\n"); + ret = -EIO; + goto err_free_dev; + } + + input_dev->name = "EETI eGalax Touch Screen"; + input_dev->phys = "I2C", + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0); + input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS); + + input_set_drvdata(input_dev, data); + + ret = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "egalax_ts", data); + if (ret < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_dev; + } + + ret = input_register_device(data->input_dev); + if (ret < 0) + goto err_free_irq; + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_dev: + input_free_device(input_dev); +err_free_data: + kfree(data); + + return ret; +} + +static __devexit int egalax_ts_remove(struct i2c_client *client) +{ + struct egalax_ts *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + input_free_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id egalax_ts_id[] = { + {"egalax_ts", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, egalax_ts_id); + +#ifdef CONFIG_PM_SLEEP +static int egalax_ts_suspend(struct device *dev) +{ + int ret; + u8 suspend_cmd[MAX_I2C_DATA_LEN] = {0x3, 0x6, 0xa, 0x3, 0x36, + 0x3f, 0x2, 0, 0, 0}; + struct i2c_client *client = to_i2c_client(dev); + ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN); + return ret > 0 ? 0 : ret; +} + +static int egalax_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + return egalax_wake_up_device(client); +} +#endif + +static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); +static struct i2c_driver egalax_ts_driver = { + .driver = { + .name = "egalax_ts", + .pm = &egalax_ts_pm_ops, + }, + .id_table = egalax_ts_id, + .probe = egalax_ts_probe, + .remove = __devexit_p(egalax_ts_remove), +}; + +static int __init egalax_ts_init(void) +{ + return i2c_add_driver(&egalax_ts_driver); +} + +static void __exit egalax_ts_exit(void) +{ + i2c_del_driver(&egalax_ts_driver); +} + +module_init(egalax_ts_init); +module_exit(egalax_ts_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); +MODULE_LICENSE("GPL");