From patchwork Fri Nov 30 14:18:54 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Li Wu X-Patchwork-Id: 1825411 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 1C3A73FC23 for ; Fri, 30 Nov 2012 14:19:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758363Ab2K3OTP (ORCPT ); Fri, 30 Nov 2012 09:19:15 -0500 Received: from mail-pa0-f46.google.com ([209.85.220.46]:52655 "EHLO mail-pa0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758360Ab2K3OTN (ORCPT ); Fri, 30 Nov 2012 09:19:13 -0500 Received: by mail-pa0-f46.google.com with SMTP id bh2so375023pad.19 for ; Fri, 30 Nov 2012 06:19:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer; bh=O4EPdnrTqvUftkw6z4kCqcLHEJZgW8xAICCSDVhVf3E=; b=JsSxE4VMsSZK6wYW8tY/CINRzHt4uGCBCcoplWY+jeZz7vjioQgpTXwe2dfVlncQ5+ 2ewI0W9h8OSHC/SKg1UQ1toM40DyyMf6wBObEc4Efh3HKcGW61zmMKDmbgn7zPA0zVYd M+4rtg2oh555wXSWzGWw/kOt1hlwcz5MRQHVih/U2yHiA0qbyAc3dVJ5lu/Y0XN0379y ZWPpHtJsFFtDEr+btcoMQDIUi5y1S7Jppqr4h3SCDtPhx+vhbGk1HQDAx0z1X21AgZb/ xeG30C4f7xwm8UTO6lJWvOZ09c+CJ35PFta0lFWKo6vDceZEk+g/0IwR3o90WyqLBqua vz7w== Received: by 10.68.129.233 with SMTP id nz9mr5705631pbb.139.1354285153143; Fri, 30 Nov 2012 06:19:13 -0800 (PST) Received: from localhost.localdomain (cm123.gamma122.maxonline.com.sg. [202.156.122.123]) by mx.google.com with ESMTPS id ju7sm3105494pbb.60.2012.11.30.06.19.05 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 30 Nov 2012 06:19:10 -0800 (PST) From: Li Wu To: willy.woo@gmail.com, Dmitry Torokhov , Li Wu , Henrik Rydberg , Andrew Morton , Joe Perches , Greg Kroah-Hartman , "David S. Miller" , Mauro Carvalho Chehab , Shawn Landden , Felipe Balbi , Viresh Kumar , Jianchun , David Dajun Chen , Ashish Jangam , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, device-drivers-devel@blackfin.uclinux.org Cc: victor.phay@st.com Subject: [PATCH 1/1] Input: STMicroelectronics multi touch capacitive touchscreen ftk Date: Fri, 30 Nov 2012 09:18:54 -0500 Message-Id: <1354285134-8188-1-git-send-email-willy.woo@gmail.com> X-Mailer: git-send-email 1.7.1 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This is a initial driver for STMicroelectronics multi touch capacitive touchscren FingertipK. It supports maximum 10 fingers, based on I2C interface. Tested on Beagleboard, Android ICS. Signed-off-by: Li Wu --- MAINTAINERS | 8 + drivers/input/touchscreen/Kconfig | 11 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ftk.c | 792 ++++++++++++++++++++++++++++++++++++ 4 files changed, 812 insertions(+), 0 deletions(-) create mode 100644 drivers/input/touchscreen/ftk.c diff --git a/MAINTAINERS b/MAINTAINERS index 9386a63..3f5398f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -314,6 +314,14 @@ W: http://wiki.analog.com/AD7879 S: Supported F: drivers/input/touchscreen/ad7879.c +STM FTK TOUCHSCREEN DRIVER +M: Li Wu +L: device-drivers-devel@blackfin.uclinux.org +W: http://www.st.com +S: Supported +F: drivers/input/touchscreen/ftk.c + + ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR) M: Jiri Kosina S: Maintained diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f7668b2..c81e2e7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -900,4 +900,15 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_FTK + tristate "STMicroelectronics i2c multitouch touchscreen with FingerTipK" + depends on I2C + help + Say Y here to enable STMicroelectronics touchscreen support. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called STM_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 178eb12..6feba9b 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_FTK) += ftk.o diff --git a/drivers/input/touchscreen/ftk.c b/drivers/input/touchscreen/ftk.c new file mode 100644 index 0000000..7c995f6 --- /dev/null +++ b/drivers/input/touchscreen/ftk.c @@ -0,0 +1,792 @@ +/* + * drivers/input/touchscreen/ftk.c + * + * Driver for STMicroelectronics FTK capacity touchscreen + * + * Author: JH Jang + * Victor Phay + * Li Wu , + * + * Copyright (c) 2012 STMicroelectronics Limited + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Definitions & global arrays. + */ +#define DRIVER_DESC "ftk i2c touchscreen driver" +#define ftk_TS_DRV_NAME "ftk" +#define X_AXIS_MAX 800 +#define X_AXIS_MIN 0 +#define Y_AXIS_MAX 480 +#define Y_AXIS_MIN 0 +#define PRESSURE_MIN 0 +#define PRESSURE_MAX 256 +#define P70_PATCH_ADDR_START 0x00420000 +#define LEAVE_EVENT 0x04 +#define ENTER_EVENT 0x03 +#define MOTION_EVENT 0x05 +#define RESET_EVENT 0x10 +#define MAX_SUPPORTED_FINGERS 10 +#define MAX_TRANSACTION_LENGTH 8 + +static struct i2c_driver stm_ts_driver; +static struct workqueue_struct *stmtouch_wq; +static int cor_xyz[10][3]; +static unsigned char ID_Indx[10] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +struct B0_write { + u8 addr; + u8 val; +}; + +struct ftk_i2c_platform_data { + int (*power)(int on); +}; + +struct ftk_ts { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct hrtimer timer; + struct work_struct work; + spinlock_t lock; + int x; + int y; + int z; + int irq; + int (*power)(int on); +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void stm_ts_early_suspend(struct early_suspend *h); +static void stm_ts_late_resume(struct early_suspend *h); +#endif + +static int ftk_write_reg(struct ftk_ts *ftk_ts, u8 *reg, u16 num_com); +static int ftk_read_reg(struct ftk_ts *ftk_ts, u8 *reg, int cnum, u8 *buf, + int num); +static void touch_ON(struct ftk_ts *ftkts, int x, int y, int z, int id); +static void touch_OFF(struct ftk_ts *ftkts); +static u8 load_config(struct ftk_ts *ftk_ts, const struct firmware *firmware); +static u8 load_patch(struct ftk_ts *ftk_ts, const struct firmware *firmware); +static int verify_firmware(const struct firmware *firmware); +static int init_ftk(struct ftk_ts *ftk_ts); +static enum hrtimer_restart st_ts_timer_func(struct hrtimer *timer); +static irqreturn_t ts_interrupt(int irq, void *handle); +static u8 decode_data_packet(struct ftk_ts *ftkts, unsigned char data[], + unsigned char LeftEvent); +static void ts_tasklet_proc(struct work_struct *work); +static int stm_ts_probe(struct i2c_client *client, + const struct i2c_device_id *idp); +static int stm_ts_remove(struct i2c_client *client); +static int stm_ts_suspend(struct i2c_client *client, pm_message_t mesg); +static int stm_ts_resume(struct i2c_client *client); +static int __init stm_ts_init(void); +static void __exit stm_ts_exit(void); + +static int ftk_write_reg(struct ftk_ts *ftk_ts, u8 *reg, u16 num_com) +{ + struct i2c_msg xfer_msg[2]; + + xfer_msg[0].addr = ftk_ts->client->addr; + xfer_msg[0].len = num_com; + xfer_msg[0].flags = 0; + xfer_msg[0].buf = reg; + + return i2c_transfer(ftk_ts->client->adapter, xfer_msg, 1); +} + + +static int ftk_read_reg(struct ftk_ts *ftk_ts, u8 *reg, int cnum, u8 *buf, + int num) +{ + u16 left = num; + u16 offset = 0; + + struct i2c_msg xfer_msg[2]; + + xfer_msg[0].addr = ftk_ts->client->addr; + xfer_msg[0].len = cnum; + xfer_msg[0].flags = 0; + xfer_msg[0].buf = reg; + + xfer_msg[1].addr = ftk_ts->client->addr; + xfer_msg[1].flags = I2C_M_RD; + + /* MTK platform */ + /* Can only transfer 8 bytes per transaction */ + while (left > 0) { + xfer_msg[1].buf = &buf[offset]; + + if (left > MAX_TRANSACTION_LENGTH) { + xfer_msg[1].len = MAX_TRANSACTION_LENGTH; + left -= MAX_TRANSACTION_LENGTH; + offset += MAX_TRANSACTION_LENGTH; + } else { + xfer_msg[1].len = left; + left = 0; + } + + if (i2c_transfer(ftk_ts->client->adapter, xfer_msg, 2) != 2) { + dev_err(ftk_ts->dev, "FTK - i2c Transfer error!\n"); + return 1; + } + } + + return 0; +} + + +static void touch_ON(struct ftk_ts *ftkts, int x, int y, int z, int id) +{ + input_report_key(ftkts->input_dev, BTN_TOUCH, 1); + input_report_abs(ftkts->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(ftkts->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(ftkts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ftkts->input_dev, ABS_MT_TRACKING_ID, id); + input_mt_sync(ftkts->input_dev); +} + + +static void touch_OFF(struct ftk_ts *ftkts) +{ + input_report_key(ftkts->input_dev, BTN_TOUCH, 0); + input_report_abs(ftkts->input_dev, ABS_MT_TRACKING_ID, 0); + input_mt_sync(ftkts->input_dev); +} + + +static u8 load_config(struct ftk_ts *ftk_ts, const struct firmware *firmware) +{ + u16 one_group_length = 0; + u16 patch_length = 0; + u16 config_length = 0; + u16 i = 0; + u8 *pdata; + u8 regAdd[8]; + u8 val[8]; + int ret; + + patch_length = firmware->data[0] * 256 + firmware->data[1]; + config_length = + firmware->data[patch_length + 2] * 256 + + firmware->data[patch_length + 2 + 1]; + pdata = (u8 *)&firmware->data[patch_length + 4]; + + while (i < config_length) { + one_group_length = pdata[i] * 256 + pdata[i + 1]; + + if ((pdata[i + 2] == 0xFF) && (pdata[i + 3] == 0xFF)) + mdelay(pdata[i + 4]); + else{ + ftk_write_reg(ftk_ts, &(pdata[i + 2]), + one_group_length); + mdelay(100); + } + + i = i + 2; + i = i + one_group_length; + } + + regAdd[0] = 0xB0; + regAdd[1] = 0x05; /* Set Interrupt Polarity */ + regAdd[2] = 0x00; /* '00' - level interrupt */ + /* '02' - edge interrupt */ + ftk_write_reg(ftk_ts, ®Add[0], 3); + mdelay(5); + + regAdd[0] = 0xB0; + regAdd[1] = 0x06; /* Enable Touch Detect Interrupt */ + regAdd[2] = 0x40; /* 0xC0 */ + ftk_write_reg(ftk_ts, ®Add[0], 3); + mdelay(5); + + regAdd[0] = 0xB0; + regAdd[1] = 0x07; /* Read 0x07 to clear ISR */ + ret = ftk_read_reg(ftk_ts, ®Add[0], 2, &val[0], 1); + mdelay(5); + + regAdd[0] = 0x85; + ret = ftk_read_reg(ftk_ts, ®Add[0], 1, &val[0], 8); + mdelay(20); + + regAdd[0] = 0x85; + ret = ftk_read_reg(ftk_ts, ®Add[0], 1, &val[0], 8); + mdelay(20); + + regAdd[0] = 0xB0; + regAdd[1] = 0x03; + ret = ftk_read_reg(ftk_ts, regAdd, 2, val, 1); + mdelay(5); + dev_info(ftk_ts->dev, "Patch loaded, Version =%X\n", val[0]); + + regAdd[0] = 0x83; /* TS Sense on */ + regAdd[1] = 0x00; + ret = ftk_write_reg(ftk_ts, ®Add[0], 1); + mdelay(5); + return ret; +} + + +static u8 load_patch(struct ftk_ts *ftk_ts, const struct firmware *firmware) +{ + u32 writeAddr, j = 0, i = 0; + u16 patch_length = 0; + u8 byteWork1[256 + 3] = { 0 }; + u8 regAdd[3] = { 0 }; + + patch_length = firmware->data[0] * 256 + firmware->data[1]; + + while (j < patch_length) { + writeAddr = P70_PATCH_ADDR_START + j; + + regAdd[0] = 0xB3; + regAdd[1] = (writeAddr >> 24) & 0xFF; + regAdd[2] = (writeAddr >> 16) & 0xFF; + ftk_write_reg(ftk_ts, ®Add[0], 3); + + byteWork1[0] = 0xB1; + byteWork1[1] = (writeAddr >> 8) & 0xFF; + byteWork1[2] = writeAddr & 0xFF; + + i = 0; + while ((j < firmware->size) && (i < 256)) { + byteWork1[i + 3] = firmware->data[j + 2]; + i++; + j++; + } + ftk_write_reg(ftk_ts, &byteWork1[0], 256 + 3); + } + + return 0; +} + + +static int verify_firmware(const struct firmware *firmware) +{ + u16 firmware_length; + u16 patch_length; + u16 config_length; + + firmware_length = firmware->size; + patch_length = firmware->data[0] * 256 + firmware->data[1]; + config_length = + firmware->data[patch_length + 2] * 256 + + firmware->data[patch_length + 2 + 1]; + + if (firmware_length == patch_length + config_length + 4) + return 0; + else + return -1; +} + + +static int init_ftk(struct ftk_ts *ftk_ts) +{ + const struct firmware *firmware; + u8 regAdd[7]; + u8 val[8]; + int ret; + + regAdd[0] = 0xB0; + regAdd[1] = 0x00; + ret = ftk_read_reg(ftk_ts, regAdd, 2, val, 3); + + if (ret < 0) + dev_err(ftk_ts->dev, " i2c_transfer failed\n"); + + mdelay(1); + dev_info(ftk_ts->dev, "Chip ID = %x %x %x\n", val[0], val[1], val[2]); + + regAdd[0] = 0x9E; /* TS Soft Reset */ + ret = ftk_write_reg(ftk_ts, ®Add[0], 1); + mdelay(1); + + ret = request_firmware(&firmware, "ftk/ftk.bin", ftk_ts->dev); + if (ret < 0) + dev_err(ftk_ts->dev, " request fw fail ,err = %d\n", ret); + else{ + ret = verify_firmware(firmware); + if (ret == 0) { + load_patch(ftk_ts, firmware); + load_config(ftk_ts, firmware); + } + } + + release_firmware(firmware); + + regAdd[0] = 0xB3; + regAdd[1] = 0xFF; + regAdd[2] = 0xFF; + ftk_write_reg(ftk_ts, ®Add[0], 3); + mdelay(5); + + regAdd[0] = 0xA0; + ftk_write_reg(ftk_ts, ®Add[0], 1); + mdelay(5); + + if (ret < 0) + dev_err(ftk_ts->dev, "ftk Not Initialised\n"); + else + dev_info(ftk_ts->dev, "ftk Initialised\n"); + + return 0; +} + + +static enum hrtimer_restart st_ts_timer_func(struct hrtimer *timer) +{ + struct ftk_ts *ftkts = container_of(timer, struct ftk_ts, timer); + + queue_work(stmtouch_wq, &ftkts->work); + return HRTIMER_NORESTART; +} + + +static irqreturn_t ts_interrupt(int irq, void *handle) +{ + struct ftk_ts *ftk_ts = handle; + + disable_irq_nosync(ftk_ts->client->irq); + queue_work(stmtouch_wq, &ftk_ts->work); + return IRQ_HANDLED; +} + + +static u8 decode_data_packet(struct ftk_ts *ftkts, unsigned char data[], + unsigned char LeftEvent) +{ + u8 EventNum; + u8 TouchID, EventID; + u8 LastLeftEvent = 0; + u8 i, num_released_finger; + u8 valid_id = 0; + + for (EventNum = 0; EventNum < LeftEvent; EventNum++) { + LastLeftEvent = data[7 + EventNum * 8] & 0x0F; + TouchID = data[1 + EventNum * 8] & 0x0F; + EventID = data[EventNum * 8] & 0xFF; + + if ((EventID == LEAVE_EVENT) || (EventID == ENTER_EVENT) || + (EventID == MOTION_EVENT)) { + /* Enter, Leave or Motion Event */ + + if (TouchID < MAX_SUPPORTED_FINGERS) { + valid_id = 1; + + ID_Indx[TouchID] = EventID; + cor_xyz[TouchID][0] = + ((data[4 + EventNum * + 8] & + 0xF0) >> + 4) | ((data[2 + EventNum * 8]) << 4); + cor_xyz[TouchID][1] = + ((data[4 + EventNum * + 8] & + 0x0F) | + ((data[3 + EventNum * 8]) << 4)); + cor_xyz[TouchID][2] = data[5 + EventNum * 8]; + } + } else if (EventID == RESET_EVENT) { + /* Reset happened */ + for (i = 0; i < MAX_SUPPORTED_FINGERS; i++) + ID_Indx[i] = 0; + + touch_OFF(ftkts); + + input_sync(ftkts->input_dev); + init_ftk(ftkts); + return 0; + } + } + + if (valid_id) { + /* Report all fingers on panel */ + /* ---------------------------------- */ + num_released_finger = 0; + for (i = 0; i < MAX_SUPPORTED_FINGERS; i++) { + if (ID_Indx[i]) { + if (ID_Indx[i] == LEAVE_EVENT) { + ID_Indx[i] = 0; + num_released_finger++; + } + + touch_ON(ftkts, cor_xyz[i][0], cor_xyz[i][1], + cor_xyz[i][2], + i); + } else + num_released_finger++; + } + + input_sync(ftkts->input_dev); + /* ---------------------------------- */ + + /* Check if all fingers are released */ + /* ---------------------------------- */ + if (num_released_finger == MAX_SUPPORTED_FINGERS) { + /* All fingers are released */ + touch_OFF(ftkts); + input_sync(ftkts->input_dev); + } + /* ---------------------------------- */ + } + + return LastLeftEvent; +} + + +static void ts_tasklet_proc(struct work_struct *work) +{ + struct ftk_ts *ftkts = container_of(work, struct ftk_ts, work); + + unsigned char data[256]; + int ret; + u8 status; + u8 regAdd; + u8 i; + u8 FirstLeftEvent = 0; + + data[0] = 0xB0; + data[1] = 0x07; + ret = ftk_read_reg(ftkts, &data[0], 2, &status, 1); + + if (status & 0x40) { + regAdd = 0x85; + if (ftk_read_reg(ftkts, ®Add, 1, data, 8) == 0) { + FirstLeftEvent = decode_data_packet(ftkts, data, 1); + + /* Read and process 1 event (8bytes) at a time */ + for (i = 0; i < FirstLeftEvent; i++) { + regAdd = 0x85; + if (ftk_read_reg(ftkts, ®Add, 1, data, + 8) == 0) + FirstLeftEvent = decode_data_packet( + ftkts, data, 1); + } + } + } + + if (!ftkts->irq) + hrtimer_start(&ftkts->timer, ktime_set(0, 10000000), + HRTIMER_MODE_REL); + else + enable_irq(ftkts->client->irq); +} + + +static int stm_ts_probe(struct i2c_client *client, + const struct i2c_device_id *idp) +{ + struct ftk_ts *ftk_ts = NULL; + struct ftk_i2c_platform_data *pdata; + int ret = 0; + int err = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(ftk_ts->dev, "err = EIO!\n"); + err = EIO; + goto fail; + } + + ftk_ts = kzalloc(sizeof(struct ftk_ts), GFP_KERNEL); + if (!ftk_ts) { + dev_err(ftk_ts->dev, "err = ENOMEM!\n"); + err = ENOMEM; + goto fail; + } + + INIT_WORK(&ftk_ts->work, ts_tasklet_proc); + + ftk_ts->client = client; + i2c_set_clientdata(client, ftk_ts); + + pdata = client->dev.platform_data; + + if (pdata) + ftk_ts->power = pdata->power; + + if (ftk_ts->power) { + ret = ftk_ts->power(1); + + pdata->power(1); + + if (ret < 0) { + pr_err("ftk_probe power on failed\n"); + goto fail; + } + } + + ftk_ts->dev = &ftk_ts->client->dev; + ftk_ts->input_dev = input_allocate_device(); + ftk_ts->input_dev->dev.parent = &client->dev; + if (!ftk_ts->input_dev) { + dev_err(ftk_ts->dev, "err = ENOMEM!\n"); + err = ENOMEM; + goto fail; + } + ftk_ts->input_dev->name = "ftk"; + ftk_ts->input_dev->phys = "ftk/input0"; + ftk_ts->input_dev->id.bustype = BUS_I2C; + ftk_ts->input_dev->id.vendor = 0x0001; + ftk_ts->input_dev->id.product = 0x0002; + ftk_ts->input_dev->id.version = 0x0100; + + ftk_ts->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ftk_ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + set_bit(EV_SYN, ftk_ts->input_dev->evbit); + set_bit(EV_KEY, ftk_ts->input_dev->evbit); + set_bit(BTN_TOUCH, ftk_ts->input_dev->keybit); + set_bit(BTN_2, ftk_ts->input_dev->keybit); + set_bit(EV_ABS, ftk_ts->input_dev->evbit); + + input_set_abs_params(ftk_ts->input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, + 0, 0); + input_set_abs_params(ftk_ts->input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, + 0, 0); + input_set_abs_params(ftk_ts->input_dev, ABS_PRESSURE, PRESSURE_MIN, + PRESSURE_MAX, 0, 0); + input_set_abs_params(ftk_ts->input_dev, ABS_MT_TRACKING_ID, 0, 10, 0, + 0); + input_set_abs_params(ftk_ts->input_dev, ABS_MT_TOUCH_MAJOR, + PRESSURE_MIN, PRESSURE_MAX, 0, 0); + input_set_abs_params(ftk_ts->input_dev, ABS_MT_WIDTH_MAJOR, + PRESSURE_MIN, PRESSURE_MAX, 0, 0); + input_set_abs_params(ftk_ts->input_dev, ABS_MT_POSITION_X, X_AXIS_MIN, + X_AXIS_MAX, 0, 0); + input_set_abs_params(ftk_ts->input_dev, ABS_MT_POSITION_Y, Y_AXIS_MIN, + Y_AXIS_MAX, 0, 0); + + err = input_register_device(ftk_ts->input_dev); + if (err) { + dev_err(ftk_ts->dev, "input_register_device fail!\n"); + goto fail; + } + + err = init_ftk(ftk_ts); + if (err) { + dev_err(ftk_ts->dev, "init_ftk fail!\n"); + goto fail; + } + + ftk_ts->irq = client->irq; + + if (!ftk_ts->irq) { + hrtimer_init(&ftk_ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ftk_ts->timer.function = st_ts_timer_func; + hrtimer_start(&ftk_ts->timer, ktime_set(1, 0), + HRTIMER_MODE_REL); + } else { + if (request_irq + (ftk_ts->irq, ts_interrupt, IRQF_TRIGGER_LOW, + client->name, ftk_ts)) { + dev_err(ftk_ts->dev, "request_irq fail!\n"); + err = -EBUSY; + goto fail; + } else + dev_info(ftk_ts->dev, "request_irq success!\n"); + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + ftk_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ftk_ts->early_suspend.suspend = stm_ts_early_suspend; + ftk_ts->early_suspend.resume = stm_ts_late_resume; + register_early_suspend(&ftk_ts->early_suspend); +#endif + + return 0; +fail: + if (ftk_ts) { + if (ftk_ts->input_dev) + input_free_device(ftk_ts->input_dev); + kfree(ftk_ts); + } + dev_info(ftk_ts->dev, "--ftkts_probe ret=%d\n", err); + return err; +} + + +static int stm_ts_remove(struct i2c_client *client) +{ + struct ftk_ts *ts = i2c_get_clientdata(client); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif + + if (ts->irq) + free_irq(client->irq, ts); + else + hrtimer_cancel(&ts->timer); + + input_unregister_device(ts->input_dev); + kfree(ts); + return 0; +} + + +static int stm_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret, i; + u8 regAdd[3]; + + struct ftk_ts *ts = i2c_get_clientdata(client); + + if (ts->irq) + disable_irq(client->irq); + else + hrtimer_cancel(&ts->timer); + + ret = cancel_work_sync(&ts->work); + + regAdd[0] = 0x80; + regAdd[1] = 0x00; + ret = ftk_write_reg(ts, ®Add[0], 1); + mdelay(5); + + regAdd[0] = 0x88; + regAdd[1] = 0x00; + ret = ftk_write_reg(ts, ®Add[0], 1); + mdelay(5); + + regAdd[0] = 0xB0; + regAdd[1] = 0x06; + regAdd[2] = 0x00; + ret = ftk_write_reg(ts, ®Add[0], 3); + mdelay(5); + + for (i = 0; i < 10; i++) + ID_Indx[i] = 0; + + input_mt_sync(ts->input_dev); + input_sync(ts->input_dev); + + if (ret < 0) + dev_err(ts->dev, "stm_ts_suspend: i2c_smbus_write_byte_data failed\n"); + + return 0; +} + + +static int stm_ts_resume(struct i2c_client *client) +{ + int ret; + u8 regAdd[3]; + struct ftk_ts *ts = i2c_get_clientdata(client); + + if (ts->irq) + enable_irq(client->irq); + else + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + + regAdd[0] = 0x81; + regAdd[1] = 0x00; + ret = ftk_write_reg(ts, ®Add[0], 1); + mdelay(5); + + regAdd[0] = 0xB0; + regAdd[1] = 0x06; + regAdd[2] = 0x40; + ret = ftk_write_reg(ts, ®Add[0], 3); + mdelay(5); + + if (ret < 0) + dev_err(ts->dev, + "stm_ts_suspend: i2c_smbus_write_byte_data failed\n"); + + return 0; +} + + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void stm_ts_early_suspend(struct early_suspend *h) +{ + struct ftk_ts *ts; + + ts = container_of(h, struct ftk_ts, early_suspend); + stm_ts_suspend(ts->client, PMSG_SUSPEND); +} + + +static void stm_ts_late_resume(struct early_suspend *h) +{ + struct ftk_ts *ts; + + ts = container_of(h, struct ftk_ts, early_suspend); + stm_ts_resume(ts->client); +} + + +#endif + +static const struct i2c_device_id stm_ts_id[] = { + { "ftk", 0 }, + {} +}; + +static struct i2c_driver stm_ts_driver = { + .driver = { + .name = "ftk", + }, + .probe = stm_ts_probe, + .remove = stm_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = stm_ts_suspend, + .resume = stm_ts_resume, +#endif + .id_table = stm_ts_id, +}; + +static int __init stm_ts_init(void) +{ + stmtouch_wq = create_singlethread_workqueue("stmtouch_wq"); + if (!stmtouch_wq) + return -ENOMEM; + + return i2c_add_driver(&stm_ts_driver); +} + + +static void __exit stm_ts_exit(void) +{ + i2c_del_driver(&stm_ts_driver); + if (stmtouch_wq) + destroy_workqueue(stmtouch_wq); +} + + +MODULE_DESCRIPTION("STM MultiTouch IC Driver"); +MODULE_AUTHOR("Li Wu "); +MODULE_LICENSE("GPL"); + +module_init(stm_ts_init); +module_exit(stm_ts_exit);