From patchwork Tue Oct 11 00:33:57 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Icenowy Zheng X-Patchwork-Id: 9370027 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 543A360487 for ; Tue, 11 Oct 2016 00:38:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4308A299AA for ; Tue, 11 Oct 2016 00:38:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3647F299C2; Tue, 11 Oct 2016 00:38:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 16BAC299AB for ; Tue, 11 Oct 2016 00:38:38 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1btl3c-0002je-GB; Tue, 11 Oct 2016 00:36:36 +0000 Received: from forward10m.cmail.yandex.net ([5.255.216.203]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1btl3V-0002dH-3t for linux-arm-kernel@lists.infradead.org; Tue, 11 Oct 2016 00:36:31 +0000 Received: from smtp1o.mail.yandex.net (smtp1o.mail.yandex.net [37.140.190.26]) by forward10m.cmail.yandex.net (Yandex) with ESMTP id 19739213B4; Tue, 11 Oct 2016 03:36:05 +0300 (MSK) Received: from smtp1o.mail.yandex.net (localhost.localdomain [127.0.0.1]) by smtp1o.mail.yandex.net (Yandex) with ESMTP id 0AFC21300D5A; Tue, 11 Oct 2016 03:35:38 +0300 (MSK) Received: by smtp1o.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id SivlLSIFMq-ZMt8rYJC; Tue, 11 Oct 2016 03:35:36 +0300 (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client certificate not present) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aosc.xyz; s=mail; t=1476146138; bh=jUMfwDUaJ/QFEqPOhPtBfe/m1RJp66ULRSIXZZTsJ6c=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; b=lRKAhnvrMt5LN3Z80/vU3KN4lWy4lHTYo9XpsGfSWCuI6GRX+8ua9bRWOEerjDGvX eI6HckfZSlOS9CYXXR8NPpj2KOMIoySOhH6VjJLWw7mC2K9I0YSfKfTReJkks+KrKn wefZMUmra+cUnPJDDx0uCmx0SjRNs8XpoACQXCb4= Authentication-Results: smtp1o.mail.yandex.net; dkim=pass header.i=@aosc.xyz X-Yandex-ForeignMX: US X-Yandex-Suid-Status: 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 1130000036118848 From: Icenowy Zheng To: Dmitry Torokhov , Rob Herring , Maxime Ripard , Chen-Yu Tsai , Hans de Goede , Henrik Rydberg Subject: [PATCH 3/5] Input: add driver for Ilitek ili2139 touch IC Date: Tue, 11 Oct 2016 08:33:57 +0800 Message-Id: <20161011003359.26079-3-icenowy@aosc.xyz> X-Mailer: git-send-email 2.10.1 In-Reply-To: <20161011003359.26079-1-icenowy@aosc.xyz> References: <20161011003359.26079-1-icenowy@aosc.xyz> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161010_173629_591664_B3804710 X-CRM114-Status: GOOD ( 22.78 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , Geert Uytterhoeven , Jarkko Sakkinen , linux-sunxi@googlegroups.com, Benjamin Tissoires , Icenowy Zheng , Marek Vasut , Damien Riegel , Russell King , Javier Martinez Canillas , linux-input@vger.kernel.org, Thierry Reding , Rask Ingemann Lambertsen , devicetree@vger.kernel.org, Arnd Bergmann , Michael Welling , linux-arm-kernel@lists.infradead.org, Jeffrey Lin , Siebren Vroegindeweij , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, Sangwon Jee , Markus Pargmann , Andrew Morton , Shawn Guo MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This driver adds support for Ilitek ili2139 touch IC, which is used in several Colorfly tablets (for example, Colorfly E708 Q1, which is an Allwinner A31s tablet with mainline kernel support). Theortically it may support more Ilitek touch ICs, however, only ili2139 is used in any mainlined device. It supports device tree enumeration, with screen resolution and axis quirks configurable. Signed-off-by: Icenowy Zheng --- drivers/input/touchscreen/Kconfig | 14 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ili2139.c | 320 ++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 drivers/input/touchscreen/ili2139.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 5079813..bb4d9d2 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -348,6 +348,20 @@ config TOUCHSCREEN_ILI210X To compile this driver as a module, choose M here: the module will be called ili210x. +config TOUCHSCREEN_ILI2139 + tristate "Ilitek ILI2139 based touchscreen" + depends on I2C + depends on OF + help + Say Y here if you have a ILI2139 based touchscreen + controller. Such kind of chipsets can be found in several + Colorfly tablets. + + If unsure, say N. + + To compile this driver as a module, choose M here; the + module will be called ili2139. + config TOUCHSCREEN_IPROC tristate "IPROC touch panel driver support" depends on ARCH_BCM_IPROC || COMPILE_TEST diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 81b8645..930b5e2 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o +obj-$(CONFIG_TOUCHSCREEN_ILI2139) += ili2139.o obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o diff --git a/drivers/input/touchscreen/ili2139.c b/drivers/input/touchscreen/ili2139.c new file mode 100644 index 0000000..65c2dea --- /dev/null +++ b/drivers/input/touchscreen/ili2139.c @@ -0,0 +1,320 @@ +/* ------------------------------------------------------------------------- + * Copyright (C) 2016, Icenowy Zheng + * + * Derived from: + * ili210x.c + * Copyright (C) Olivier Sobrie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * ------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_POLL_PERIOD 20 + +#define MAX_TOUCHES 10 +#define COMPATIBLE_TOUCHES 2 + +/* Touchscreen commands */ +#define REG_TOUCHDATA 0x10 +#define REG_TOUCHSUBDATA 0x11 +#define REG_PANEL_INFO 0x20 +#define REG_FIRMWARE_VERSION 0x40 +#define REG_PROTO_VERSION 0x42 + +#define SUBDATA_STATUS_TOUCH_POINT 0x80 +#define SUBDATA_STATUS_RELEASE_POINT 0x00 + +struct finger { + u8 x_low; + u8 x_high; + u8 y_low; + u8 y_high; +} __packed; + +struct touchdata { + u8 length; + struct finger finger[COMPATIBLE_TOUCHES]; +} __packed; + +struct touch_subdata { + u8 status; + struct finger finger; +} __packed; + +struct panel_info { + struct finger finger_max; + u8 xchannel_num; + u8 ychannel_num; +} __packed; + +struct firmware_version { + u8 id; + u8 major; + u8 minor; +} __packed; + +struct ili2139 { + struct i2c_client *client; + struct input_dev *input; + unsigned int poll_period; + struct delayed_work dwork; + struct touchscreen_properties prop; + int slots[MAX_TOUCHES]; + int ids[MAX_TOUCHES]; + struct input_mt_pos pos[MAX_TOUCHES]; +}; + +static int ili2139_read_reg(struct i2c_client *client, u8 reg, void *buf, + size_t len) +{ + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + return 0; +} + +static void ili2139_work(struct work_struct *work) +{ + int id; + struct ili2139 *priv = container_of(work, struct ili2139, + dwork.work); + struct i2c_client *client = priv->client; + struct touchdata touchdata; + struct touch_subdata subdata; + int error; + + error = ili2139_read_reg(client, REG_TOUCHDATA, + &touchdata, sizeof(touchdata)); + if (error) { + dev_err(&client->dev, + "Unable to get touchdata, err = %d\n", error); + return; + } + + for (id = 0; id < touchdata.length; id++) { + error = ili2139_read_reg(client, REG_TOUCHSUBDATA, &subdata, + sizeof(subdata)); + if (error) { + dev_err(&client->dev, + "Unable to get touch subdata, err = %d\n", + error); + return; + } + + priv->ids[id] = subdata.status & 0x3F; + + /* The sequence changed in the v2 subdata protocol. */ + touchscreen_set_mt_pos(&priv->pos[id], &priv->prop, + (subdata.finger.x_high | (subdata.finger.x_low << 8)), + (subdata.finger.y_high | (subdata.finger.y_low << 8))); + } + + input_mt_assign_slots(priv->input, priv->slots, priv->pos, + touchdata.length, 0); + + for (id = 0; id < touchdata.length; id++) { + input_mt_slot(priv->input, priv->slots[id]); + input_mt_report_slot_state(priv->input, MT_TOOL_FINGER, + subdata.status & + SUBDATA_STATUS_TOUCH_POINT); + input_report_abs(priv->input, ABS_MT_POSITION_X, + priv->pos[id].x); + input_report_abs(priv->input, ABS_MT_POSITION_Y, + priv->pos[id].y); + } + + input_mt_sync_frame(priv->input); + input_sync(priv->input); + + schedule_delayed_work(&priv->dwork, + msecs_to_jiffies(priv->poll_period)); +} + +static irqreturn_t ili2139_irq(int irq, void *irq_data) +{ + struct ili2139 *priv = irq_data; + + schedule_delayed_work(&priv->dwork, 0); + + return IRQ_HANDLED; +} + +static int ili2139_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ili2139 *priv; + struct input_dev *input; + struct panel_info panel; + struct firmware_version firmware; + int xmax, ymax; + int error; + + dev_dbg(dev, "Probing for ILI2139 I2C Touschreen driver"); + + if (client->irq <= 0) { + dev_err(dev, "No IRQ!\n"); + return -ENODEV; + } + + /* Get firmware version */ + error = ili2139_read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); + if (error) { + dev_err(dev, "Failed to get firmware version, err: %d\n", + error); + return error; + } + + /* get panel info */ + error = ili2139_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); + if (error) { + dev_err(dev, "Failed to get panel information, err: %d\n", + error); + return error; + } + + xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); + ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + input = devm_input_allocate_device(dev); + if (!priv || !input) + return -ENOMEM; + + priv->client = client; + priv->input = input; + priv->poll_period = DEFAULT_POLL_PERIOD; + INIT_DELAYED_WORK(&priv->dwork, ili2139_work); + + /* Setup input device */ + input->name = "ILI2139 Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + + /* Multi touch */ + input_mt_init_slots(input, MAX_TOUCHES, INPUT_MT_DIRECT | + INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + + touchscreen_parse_properties(input, true, &priv->prop); + + input_set_drvdata(input, priv); + i2c_set_clientdata(client, priv); + + error = devm_request_irq(dev, client->irq, ili2139_irq, + IRQF_TRIGGER_FALLING, client->name, priv); + if (error) { + dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", + error); + return error; + } + + error = input_register_device(priv->input); + if (error) { + dev_err(dev, "Cannot register input device, err: %d\n", error); + return error; + } + + device_init_wakeup(&client->dev, 1); + + dev_dbg(dev, + "ILI2139 initialized (IRQ: %d), firmware version %d.%d.%d", + client->irq, firmware.id, firmware.major, firmware.minor); + + return 0; +} + +static int ili2139_i2c_remove(struct i2c_client *client) +{ + struct ili2139 *priv = i2c_get_clientdata(client); + + cancel_delayed_work_sync(&priv->dwork); + + return 0; +} + +static int __maybe_unused ili2139_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int __maybe_unused ili2139_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ili2139_i2c_pm, + ili2139_i2c_suspend, ili2139_i2c_resume); + +static const struct i2c_device_id ili2139_i2c_id[] = { + { "ili2139", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ili2139_i2c_id); + +static struct i2c_driver ili2139_ts_driver = { + .driver = { + .name = "ili2139_i2c", + .pm = &ili2139_i2c_pm, + }, + .id_table = ili2139_i2c_id, + .probe = ili2139_i2c_probe, + .remove = ili2139_i2c_remove, +}; + +module_i2c_driver(ili2139_ts_driver); + +MODULE_AUTHOR("Olivier Sobrie "); +MODULE_DESCRIPTION("ILI2139 I2C Touchscreen Driver"); +MODULE_LICENSE("GPL");