lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1354620389-1841-1-git-send-email-willy.woo@gmail.com>
Date:	Tue,  4 Dec 2012 06:26:29 -0500
From:	Li Wu <willy.woo@...il.com>
To:	willy.woo@...il.com, Dmitry Torokhov <dmitry.torokhov@...il.com>,
	Li Wu <li.wu@...com>, Henrik Rydberg <rydberg@...omail.se>,
	Andrew Morton <akpm@...ux-foundation.org>,
	Joe Perches <joe@...ches.com>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	"David S. Miller" <davem@...emloft.net>,
	Mauro Carvalho Chehab <mchehab@...hat.com>,
	Shawn Landden <shawnlandden@...il.com>,
	Felipe Balbi <balbi@...com>,
	Viresh Kumar <viresh.kumar@...aro.org>,
	Jianchun <jcbian@...cir.com.cn>,
	David Dajun Chen <dchen@...semi.com>,
	Ashish Jangam <ashish.jangam@...tcummins.com>,
	<simon.budig@...nelconcepts.de>, <rachna@...com>,
	linux-kernel@...r.kernel.org, linux-input@...r.kernel.org,
	device-drivers-devel@...ckfin.uclinux.org
Cc:	victor.phay@...com
Subject: [PATCH v2 1/1] Input: STMicroelectronics multi touch capacitive touchscreen ftk

This is a initial driver for STMicroelectronics multi touch
capacitive touchscren FingertipK. It supports maximum 10 fingers,
based on I2C interface.

STMicroelectronics will maintain and update this driver regularly.

Tested on Beagleboard, Android ICS.

Suggested by Dmitry, rebase and squash 2 commits to 1 patch.

Consolidates comments from review and format improvement.

This version consolidates comments from Felipe and Joe.
1: Changed all CaMeLcAse.
2: Removed CONFIG_HAS_EARLYSUSPEND since it's not supported.
3: Removed unnecessary function prototype.
4: Removed extra blank lines.
5: Changed two function names to all lower casees.
6: Removed extra space before actual message.
7: Changed to dev_dbg() for debug message.
8: Exit if request_firmware() fails.
9: Added a comment before mdelay().
10: Used threaded IRQ instead.
11: Removed unnecessary dev_info message.
12: Put dev_parent assign after error check.
13: some minor format improvement.

Tested on Android ICS, beagleboard.

Signed-off-by: Li Wu <willy.woo@...il.com>
---
 MAINTAINERS                        |    8 +
 drivers/input/touchscreen/Kconfig  |   11 +
 drivers/input/touchscreen/Makefile |    1 +
 drivers/input/touchscreen/ftk.c    |  638 ++++++++++++++++++++++++++++++++++++
 4 files changed, 658 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 <li.wu@...com>
+L:      device-drivers-devel@...ckfin.uclinux.org
+W:      http://www.st.com
+S:      Supported
+F:      drivers/input/touchscreen/ftk.c
+
+
 ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR)
 M:	Jiri Kosina <jkosina@...e.cz>
 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..fa98409
--- /dev/null
+++ b/drivers/input/touchscreen/ftk.c
@@ -0,0 +1,638 @@
+/*
+ * drivers/input/touchscreen/ftk.c
+ *
+ * Driver for STMicroelectronics FTK capacity touchscreen
+ *
+ * Author: JH Jang <jh.jang@...com>
+ *         Victor Phay <victor.phay@...com>
+ *         Li Wu <li.wu@...com>, <willy.woo@...il.com>
+ *
+ * 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 <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+/*
+ * 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
+#define NUM_DIM                       3
+
+#define FTK_SOFT_RESET                0x9E
+#define FTK_TSSON                     0x83
+#define FTK_READ_OLDESE_EVENT         0x85
+#define FTK_BASE_ADDR                 0xB0
+#define FTK_TYPE_B1                   0xB1
+#define FTK_TYPE_B3                   0xB3
+
+
+static struct i2c_driver stm_ts_driver;
+static struct workqueue_struct *stmtouch_wq;
+static int cor_xyz[MAX_SUPPORTED_FINGERS][NUM_DIM];
+static unsigned char id_indx[MAX_SUPPORTED_FINGERS];
+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);
+};
+
+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 reg_add[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;
+	}
+
+	reg_add[0] = FTK_BASE_ADDR;
+	reg_add[1] = 0x05;       /* Set Interrupt Polarity */
+	reg_add[2] = 0x00;       /* '00' - level interrupt */
+	ftk_write_reg(ftk_ts, &reg_add[0], 3);
+	mdelay(5);
+
+	reg_add[0] = FTK_BASE_ADDR;
+	reg_add[1] = 0x06;       /* Enable Touch Detect Interrupt */
+	reg_add[2] = 0x40;       /* 0xC0 */
+	ftk_write_reg(ftk_ts, &reg_add[0], 3);
+	mdelay(5);
+
+	reg_add[0] = FTK_BASE_ADDR;
+	reg_add[1] = 0x07;       /* Read 0x07 to clear ISR */
+	ret = ftk_read_reg(ftk_ts, &reg_add[0], 2, &val[0], 1);
+	mdelay(5);
+
+	reg_add[0] = FTK_READ_OLDESE_EVENT;
+	ret = ftk_read_reg(ftk_ts, &reg_add[0], 1, &val[0], 8);
+	mdelay(20);
+
+	reg_add[0] = FTK_READ_OLDESE_EVENT;
+	ret = ftk_read_reg(ftk_ts, &reg_add[0], 1, &val[0], 8);
+	mdelay(20);
+
+	reg_add[0] = FTK_BASE_ADDR;
+	reg_add[1] = 0x03;
+	ret = ftk_read_reg(ftk_ts, reg_add, 2, val, 1);
+	mdelay(5);
+	dev_info(ftk_ts->dev, "Patch loaded, Version =%X\n", val[0]);
+
+	reg_add[0] = FTK_TSSON;       /* TS Sense on */
+	reg_add[1] = 0x00;
+	ret = ftk_write_reg(ftk_ts, &reg_add[0], 1);
+	mdelay(5);
+	return ret;
+}
+
+static u8 load_patch(struct ftk_ts *ftk_ts, const struct firmware *firmware)
+{
+	u32 write_addr, j = 0, i = 0;
+	u16 patch_length = 0;
+	u8 byte_work[256 + 3] = { 0 };
+	u8 reg_add[3] = { 0 };
+
+	patch_length = firmware->data[0] * 256 + firmware->data[1];
+
+	while (j < patch_length) {
+		write_addr = P70_PATCH_ADDR_START + j;
+
+		reg_add[0] = FTK_TYPE_B3;
+		reg_add[1] = (write_addr >> 24) & 0xFF;
+		reg_add[2] = (write_addr >> 16) & 0xFF;
+		ftk_write_reg(ftk_ts, &reg_add[0], 3);
+
+		byte_work[0] = FTK_TYPE_B1;
+		byte_work[1] = (write_addr >> 8) & 0xFF;
+		byte_work[2] = write_addr & 0xFF;
+
+		i = 0;
+		while ((j < firmware->size) && (i < 256)) {
+			byte_work[i + 3] = firmware->data[j + 2];
+			i++;
+			j++;
+		}
+		ftk_write_reg(ftk_ts, &byte_work[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 reg_add[7];
+	u8 val[8];
+	int ret;
+
+	reg_add[0] = FTK_BASE_ADDR;
+	reg_add[1] = 0x00;
+	ret = ftk_read_reg(ftk_ts, reg_add, 2, val, 3);
+
+	if (ret < 0)
+		dev_err(ftk_ts->dev, "i2c_transfer failed\n");
+
+	mdelay(1);
+	dev_dbg(ftk_ts->dev, "Chip ID = %x %x %x\n", val[0], val[1], val[2]);
+
+	reg_add[0] = FTK_SOFT_RESET; /* TS Soft Reset */
+	ret = ftk_write_reg(ftk_ts, &reg_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);
+		return ret;
+	}
+
+	ret = verify_firmware(firmware);
+	if (ret == 0) {
+		load_patch(ftk_ts, firmware);
+		load_config(ftk_ts, firmware);
+
+	}
+
+	release_firmware(firmware);
+
+	reg_add[0] = FTK_TYPE_B3;
+	reg_add[1] = 0xFF;
+	reg_add[2] = 0xFF;
+	ftk_write_reg(ftk_ts, &reg_add[0], 3);
+
+	/*wait till this operation ends, to be safe */
+	mdelay(5);
+
+	reg_add[0] = 0xA0;
+	ftk_write_reg(ftk_ts, &reg_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 left_event)
+{
+	u8 event_num;
+	u8 touch_id, event_id;
+	u8 last_left_event = 0;
+	u8 i, num_released_finger;
+	u8 valid_id = 0;
+
+	for (event_num = 0; event_num < left_event; event_num++) {
+		last_left_event = data[7 + event_num * 8] & 0x0F;
+		touch_id = data[1 + event_num * 8] & 0x0F;
+		event_id = data[event_num * 8] & 0xFF;
+
+		if ((event_id == LEAVE_EVENT) || (event_id == ENTER_EVENT) ||
+		    (event_id == MOTION_EVENT)) {
+			/* Enter, Leave or Motion Event */
+			if (touch_id < MAX_SUPPORTED_FINGERS) {
+				valid_id = 1;
+
+				id_indx[touch_id] = event_id;
+				cor_xyz[touch_id][0] =
+					((data[4 + event_num *
+					       8] &
+					  0xF0) >>
+					 4) | ((data[2 + event_num * 8]) << 4);
+				cor_xyz[touch_id][1] =
+					((data[4 + event_num *
+					       8] &
+					  0x0F) |
+					 ((data[3 + event_num * 8]) << 4));
+				cor_xyz[touch_id][2] = data[5 + event_num * 8];
+			}
+		} else if (event_id == 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 last_left_event;
+}
+
+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 reg_add;
+	u8 i;
+	u8 first_left_event = 0;
+
+	data[0] = FTK_BASE_ADDR;
+	data[1] = 0x07;
+	ret = ftk_read_reg(ftkts, &data[0], 2, &status, 1);
+
+	if (status & 0x40) {
+		reg_add = FTK_READ_OLDESE_EVENT;
+		if (ftk_read_reg(ftkts, &reg_add, 1, data, 8) == 0) {
+			first_left_event = decode_data_packet(ftkts, data, 1);
+
+			/* Read and process 1 event (8bytes) at a time */
+			for (i = 0; i < first_left_event; i++) {
+				reg_add = FTK_READ_OLDESE_EVENT;
+				if (ftk_read_reg(ftkts, &reg_add, 1, data,
+						 8) == 0)
+					first_left_event = 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();
+	if (!ftk_ts->input_dev) {
+		dev_err(ftk_ts->dev, "err = ENOMEM!\n");
+		err = ENOMEM;
+		goto fail;
+	}
+	ftk_ts->input_dev->dev.parent = &client->dev;
+	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 {
+		ret = devm_request_threaded_irq(ftk_ts->dev, ftk_ts->irq,
+						NULL, ts_interrupt,
+						IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+						client->name,
+						ftk_ts);
+		if (ret) {
+			dev_err(ftk_ts->dev, "request_irq  fail!\n");
+			err = -EBUSY;
+			goto fail;
+		}
+	}
+
+	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);
+
+	if (ts->irq)
+		devm_free_irq(ts->dev, client->irq, ts);
+	else
+		hrtimer_cancel(&ts->timer);
+
+	input_unregister_device(ts->input_dev);
+	kfree(ts);
+	return 0;
+}
+
+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,
+	.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 <li.wu@...com>");
+MODULE_LICENSE("GPL");
+
+module_init(stm_ts_init);
+module_exit(stm_ts_exit);
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ