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: <1417429031-4576-1-git-send-email-jeffrey.lin@rad-ic.com>
Date:	Mon,  1 Dec 2014 18:17:11 +0800
From:	"jeffrey.lin" <yajohn@...il.com>
To:	dmitry.torokhov@...il.com, rydberg@...omail.se, shc_work@...l.ru,
	bleung@...omium.org, lee.jones@...aro.org,
	charliemooney@...omium.org
Cc:	KP.li@...-ic.com, linux-kernel@...r.kernel.org,
	linux-input@...r.kernel.org, "jeffrey.lin" <jeffrey.lin@...-ic.com>
Subject: [PATCH] driver: input: touchscreen: add Raydium i2c touchscreen driver

From: "jeffrey.lin" <jeffrey.lin@...-ic.com>

This patch is porting Raydium I2C touch driver. Developer can enable
raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM_TS".

Change-Id: I5f33cfdf0e895de6e7d535c11dd4b3ce8b49fa48
Signed-off-by: jeffrey.lin@...-ic.com
---
 drivers/input/touchscreen/Kconfig      |  12 +
 drivers/input/touchscreen/Makefile     |   1 +
 drivers/input/touchscreen/rm31100_ts.c | 968 +++++++++++++++++++++++++++++++++
 include/linux/input/rm31100_ts.h       |  60 ++
 4 files changed, 1041 insertions(+)
 create mode 100644 drivers/input/touchscreen/rm31100_ts.c
 create mode 100644 include/linux/input/rm31100_ts.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3ce9181..d0324d2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -955,4 +955,16 @@ config TOUCHSCREEN_ZFORCE
 	  To compile this driver as a module, choose M here: the
 	  module will be called zforce_ts.
 
+config TOUCHSCREEN_RM_TS
+	tristate "Raydium I2C Touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have Raydium series I2C touchscreen,
+	  such as RM31100 , connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rm31100_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 687d5a7..aae4af2 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -78,3 +78,4 @@ 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_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)	+= rm31100_ts.o
diff --git a/drivers/input/touchscreen/rm31100_ts.c b/drivers/input/touchscreen/rm31100_ts.c
new file mode 100644
index 0000000..6cb09a4
--- /dev/null
+++ b/drivers/input/touchscreen/rm31100_ts.c
@@ -0,0 +1,968 @@
+/* Source for:
+ * Raydium rm31100_ts Prototype touchscreen driver.
+ * drivers/input/touchscreen/rm31100_ts.c
+ *
+ * Copyright (C) 2012,
+ *
+ * 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.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ *
+ * History:
+ *			(C) 2012 Raydium - Update for GPL distribution
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+#include <linux/async.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/input/rm31100_ts.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+/*#include <plat/gpio-cfg.h>*/
+#ifdef CONFIG_MISC_DEV
+#include <linux/miscdevice.h>
+#endif
+/*#include <asm/uaccess.h> copy_to_user() */
+#include <linux/uaccess.h>
+
+#define rm31100	0x0
+#define rm3110x	0x1
+
+#define INVALID_DATA	0xff
+#define MAX_REPORT_TOUCHED_POINTS	10
+
+#define TOUCHSCREEN_TIMEOUT	(msecs_to_jiffies(10))
+#define INITIAL_DELAY		(msecs_to_jiffies(25000))
+
+#define EVAL_REPORT_RATE    1
+
+#define I2C_CLIENT_ADDR         0x39
+#define I2C_DMA_CLIENT_ADDR     0x5A
+#undef CONFIG_PM
+struct rm31100_ts_data {
+	u8 x_index;
+	u8 y_index;
+	u8 z_index;
+	u8 id_index;
+	u8 touch_index;
+	u8 data_reg;
+	u8 status_reg;
+	u8 data_size;
+	u8 touch_bytes;
+	u8 update_data;
+	u8 touch_meta_data;
+	u8 finger_size;
+};
+
+static struct rm31100_ts_data devices[] = {
+	[0] = {
+		.x_index = 2,
+		.y_index = 4,
+		.z_index = 6,
+		.id_index = 1,
+		.data_reg = 0x1,
+		.status_reg = 0,
+		.update_data = 0x0,/*0x4*/
+		.touch_bytes = 6,
+		.touch_meta_data = 1,
+		.finger_size = 70,
+	},
+};
+
+struct rm31100_ts {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct delayed_work work;
+	struct rm3110x_ts_platform_data *pdata;
+	struct rm31100_ts_data *dd;
+	u8 *touch_data;
+	u8 device_id;
+	u8 prev_touches;
+	bool is_suspended;
+	bool int_pending;
+	struct mutex sus_lock;
+	struct mutex access_lock;
+	u32 pen_irq;
+};
+
+struct rm31100_ts *pts;
+/*
+static inline u16 join_bytes(u8 a, u8 b)
+{
+	u16 ab = 0;
+	ab = ab | a;
+	ab = ab << 8 | b;
+	return ab;
+}
+*/
+static s32 rm31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 data;
+
+	data = i2c_smbus_write_byte_data(client, reg, val);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static s32 rm31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+	s32 data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+						 data, reg);
+
+	return data;
+}
+
+static int rm31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[2];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = 1;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = &reg;
+
+	xfer_msg[1].addr = client->addr;
+	xfer_msg[1].len = num;
+	xfer_msg[1].flags = I2C_M_RD;
+	xfer_msg[1].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int rm31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+	struct i2c_msg xfer_msg[1];
+
+	xfer_msg[0].addr = client->addr;
+	xfer_msg[0].len = num;
+	xfer_msg[0].flags = 0;
+	xfer_msg[0].buf = buf;
+
+	return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+#ifdef CONFIG_MISC_DEV
+static int dev_open(struct inode *inode, struct file *filp)
+{
+	mutex_lock(&pts->access_lock);
+	return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+	mutex_unlock(&pts->access_lock);
+	return 0;
+}
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	struct i2c_msg xfer_msg;
+	/*static char out[] = "1234567890";*/
+	/*static int idx;*//*= 0; remove by checkpatch*/
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL)
+		return -ENOMEM;
+
+	/*xfer_msg.addr = pts->client->addr;*/
+	xfer_msg.addr = I2C_CLIENT_ADDR;
+	xfer_msg.len = count;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = kbuf;
+
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	if (copy_to_user(buf, kbuf, count) == 0)
+		return count;
+	else
+		return -EFAULT;
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
+{
+	u8 *kbuf;
+	ssize_t status = 0;
+	int i;
+
+	kbuf = kmalloc(count, GFP_KERNEL);
+	if (kbuf == NULL) {
+		dev_err("kmalloc() fail\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kbuf, buf, count) == 0) {
+		pts->client->addr = I2C_CLIENT_ADDR;
+		if (rm31100_ts_write(pts->client, kbuf, count) < 0)
+			status = -EFAULT;
+		else
+			status = count;
+	} else {
+		dev_err("copy_from_user() fail\n");
+		status = -EFAULT;
+	}
+
+	kfree(kbuf);
+	return status;
+}
+
+static struct file_operations dev_fops = {
+	.owner = THIS_MODULE,
+	.open = dev_open,
+	.release = dev_release,
+	.read = dev_read,
+	.write = dev_write,
+	/*.unlocked_ioctl = dev_ioctl,*/
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "raydium_ts",
+	.fops = &dev_fops,
+};
+#endif
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+	struct i2c_msg xfer_msg;
+	int num = 10;
+	char buf[100];
+	/*int i;*/
+
+	xfer_msg.addr = pts->client->addr;
+	xfer_msg.len = num;
+	xfer_msg.flags = I2C_M_RD;
+	xfer_msg.buf = buf;
+	pts->client->addr = I2C_CLIENT_ADDR;
+	i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+	return 0;
+}
+
+ssize_t store(struct device_driver *drv, const char *buf, size_t count)
+{
+	/*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
+	unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+
+	pts->client->addr = I2C_CLIENT_ADDR;
+	rm31100_ts_write(pts->client, pkt, sizeof(pkt));
+
+	return sizeof(pkt);
+}
+
+DRIVER_ATTR(myAttr, 0x777, show, store);
+
+static void report_data(struct rm31100_ts *ts, u16 x, u16 y, u8 pressure, u8 id)
+{
+	if (ts->pdata->swap_xy)
+		swap(x, y);
+
+	/* handle inverting coordinates */
+	if (ts->pdata->invert_x)
+		x = ts->pdata->res_x - x;
+	if (ts->pdata->invert_y)
+		y = ts->pdata->res_y - y;
+/*
+	input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
+	input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+	input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+	input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, pressure);
+	input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, ts->dd->finger_size);
+	input_mt_sync(ts->input);
+*/
+/*For protocol B*/
+	input_mt_slot(ts->input_dev, id);
+	input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+	input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+	input_report_abs(ts->input_dev, ABS_MT_PRESSURE, pressure);
+/*
+	dev_dbg("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
+		__func__, id, x, y, pressure);
+*/
+}
+
+static void process_rm31100_data(struct RM31100_ts *ts)
+{
+	u8 id, pressure, touches, i;
+	u16 x, y;
+
+	touches = ts->touch_data[ts->dd->touch_index];
+
+	if (touches > 0) {
+		for (i = 0; i < touches; i++) {
+			id = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->id_index];
+			pressure = ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->z_index];
+			/*
+			x = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->x_index + 1],
+			ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->x_index]);
+			y = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->y_index + 1],
+			ts->touch_data[i * ts->dd->touch_bytes +
+				ts->dd->y_index]);
+			*/
+			x = get_unaligned_le16(ts->touch_data[i * ts->dd->
+				touch_bytes + ts->dd->x_index]);
+			y = get_unaligned_le16(ts->touch_data[i * ts->dd->
+				touch_bytes + ts->dd->y_index]);
+			report_data(ts, x, y, pressure, id);
+		}
+	} else
+		input_mt_sync(ts->input);
+
+	ts->prev_touches = touches;
+	input_report_key(ts->input, BTN_TOUCH, 1);
+	input_sync(ts->input);
+}
+
+static void rm31100_ts_xy_worker(struct work_struct *work)
+{
+	int rc;
+	u8 DMAAddress[4];
+	struct rm31100_ts *ts;
+#if EVAL_REPORT_RATE
+	static struct timeval tv_start;
+	struct timeval tv_now;
+	static int cnt = -1;
+	int us;
+#endif /* EVAL_REPORT_RATE*/
+
+	/*dev_dbg("****rm31100_ts_xy_worker******\n");*/
+	mutex_lock(&ts->sus_lock);
+	if (ts->is_suspended == true) {
+		dev_dbg(&ts->client->dev, "TS is supended\n");
+		ts->int_pending = true;
+		mutex_unlock(&ts->sus_lock);
+		return;
+	}
+	mutex_unlock(&ts->sus_lock);
+
+	mutex_lock(&ts->access_lock);
+	/* read data from DATA_REG */
+	/*RM31100 DMA Mode*/
+	/*T010 OR w001+T012*/
+	DMAAddress[0] = 0x0F;
+	DMAAddress[1] = 0x00;
+	DMAAddress[2] = 0x20;
+	DMAAddress[3] = 0x81;/* Turn on DMA Mode*/
+	ts->client->addr = I2C_DMA_CLIENT_ADDR;
+	rc = rm31100_ts_write(ts->client, DMAAddress, 0x04);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed\n");
+		goto schedule;
+	}
+	ts->client->addr = I2C_CLIENT_ADDR;
+	rc = rm31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+	ts->dd->data_size);
+
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "read failed\n");
+		goto schedule;
+	}
+
+	if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+		goto schedule;
+
+	/* write to STATUS_REG to release lock */
+	rc = rm31100_ts_write_reg_u8(ts->client,
+		ts->dd->status_reg, ts->dd->update_data);
+	if (rc < 0) {
+		dev_err(&ts->client->dev, "write failed, try once more\n");
+
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0)
+			dev_err(&ts->client->dev, "write failed, exiting\n");
+	}
+
+	process_rm31100_data(ts);
+
+#if EVAL_REPORT_RATE
+	cnt++;
+
+	if (cnt == 0)/* first time this function is executed.*/
+		do_gettimeofday(&tv_start);
+	else if (cnt == 100) {
+		do_gettimeofday(&tv_now);
+		us = 1000000 * (tv_now.tv_sec - tv_start.tv_sec)
+			+ tv_now.tv_usec - tv_start.tv_usec;
+		tv_start.tv_sec = tv_now.tv_sec;
+		tv_start.tv_usec = tv_now.tv_usec;
+		cnt = 0;
+	}
+#endif /* EVAL_REPORT_RATE*/
+schedule:
+
+	mutex_unlock(&ts->access_lock);
+	/*dev_dbg("****Leave rm31100_ts_xy_worker******\n");*/
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+	struct rm31100_ts *ts = dev_id;
+
+/*For protocol B*/
+	input_sync(g_input_dev);
+
+	rm31100_ts_xy_worker(&ts->work);
+	return IRQ_HANDLED;
+}
+
+static int rm31100_ts_init_ts(struct i2c_client *client, struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+
+	ts->dd = &devices[ts->device_id];
+
+	if (!ts->pdata->nfingers) {
+		dev_err(&client->dev, "Touches information not specified\n");
+		return -EINVAL;
+	}
+
+	if (ts->device_id == rm3110x) {
+		if (ts->pdata->nfingers > 2) {
+			dev_err(&client->dev, "Touches >=1 & <= 2\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->dd->touch_bytes;
+		ts->dd->touch_index = 0x0;
+	} else if (ts->device_id == rm31100) {
+		if (ts->pdata->nfingers > 10) {
+			dev_err(&client->dev, "Touches >=1 & <= 10\n");
+			return -EINVAL;
+		}
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+						ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+	/* w001 */
+	else {
+		ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+		ts->dd->touch_meta_data;
+		ts->dd->touch_index = 0x0;
+	}
+
+	ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+	if (!ts->touch_data) {
+		pr_err("%s: Unable to allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rm31100_ts_suspend(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+	int rc = 0, i;
+
+	if (device_may_wakeup(dev)) {
+		/* mark suspend flag */
+		mutex_lock(&ts->sus_lock);
+		ts->is_suspended = true;
+		mutex_unlock(&ts->sus_lock);
+
+		enable_irq_wake(ts->pen_irq);
+	} else {
+		disable_irq_nosync(ts->pen_irq);
+/*For protocol B*/
+	for (i = 0; i < MAX_REPORT_TOUCHED_POINTS; i++) {
+		input_mt_slot(ts->input_dev, i);
+
+		input_mt_report_slot_state(
+			ts->input_dev,
+			MT_TOOL_FINGER, false);
+
+		input_report_key(
+			ts->input_dev,
+			BTN_TOOL_RUBBER, false);
+	}
+	input_sync(ts->input_dev);
+
+		if (rc) {
+			/* missed the worker, write to STATUS_REG to
+			   acknowledge interrupt */
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg, ts->dd->update_data);
+			if (rc < 0) {
+				dev_err(&ts->client->dev,
+					"write failed, try once more\n");
+
+				rc = rm31100_ts_write_reg_u8(ts->client,
+					ts->dd->status_reg,
+					ts->dd->update_data);
+				if (rc < 0)
+					dev_err(&ts->client->dev,
+						"write failed, exiting\n");
+			}
+
+			enable_irq(ts->pen_irq);
+		}
+
+		gpio_free(ts->pdata->irq_gpio);
+
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(0);
+			if (rc) {
+				dev_err(dev, "unable to goto suspend\n");
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int rm31100_ts_resume(struct device *dev)
+{
+	struct rm31100_ts *ts = dev_get_drvdata(dev);
+
+	int rc = 0;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(ts->pen_irq);
+
+		mutex_lock(&ts->sus_lock);
+		ts->is_suspended = false;
+
+		if (ts->int_pending == true)
+			ts->int_pending = false;
+
+		mutex_unlock(&ts->sus_lock);
+
+	} else {
+		if (ts->pdata->power_on) {
+			rc = ts->pdata->power_on(1);
+			if (rc) {
+				dev_err(dev, "unable to resume\n");
+				return rc;
+			}
+		}
+
+		/* configure touchscreen interrupt gpio */
+		rc = gpio_request(ts->pdata->irq_gpio, "rm31100_irq_gpio");
+		if (rc) {
+			pr_err("%s: unable to request gpio %d\n",
+				__func__, ts->pdata->irq_gpio);
+			goto err_power_off;
+		}
+		if (ts->pdata->irq_cfg) {
+			s3c_gpio_cfgpin(ts->pdata->irq_gpio,
+				ts->pdata->irq_cfg);
+			s3c_gpio_setpull(ts->pdata->irq_gpio,
+				S3C_GPIO_PULL_NONE);
+		}
+
+		rc = gpio_direction_input(ts->pdata->irq_gpio);
+		if (rc) {
+			pr_err("%s: unable to set direction for gpio %d\n",
+				__func__, ts->pdata->irq_gpio);
+			goto err_gpio_free;
+		}
+
+		enable_irq(ts->pen_irq);
+
+		/* Clear the status register of the TS controller */
+		rc = rm31100_ts_write_reg_u8(ts->client,
+			ts->dd->status_reg, ts->dd->update_data);
+		if (rc < 0) {
+			dev_err(&ts->client->dev,
+				"write failed, try once more\n");
+
+			rc = rm31100_ts_write_reg_u8(ts->client,
+				ts->dd->status_reg,
+				ts->dd->update_data);
+			if (rc < 0)
+				dev_err(&ts->client->dev,
+					"write failed, exiting\n");
+		}
+	}
+
+	return 0;
+err_gpio_free:
+	gpio_free(ts->pdata->irq_gpio);
+err_power_off:
+	if (ts->pdata->power_on)
+		rc = ts->pdata->power_on(0);
+	return rc;
+}
+
+static struct dev_pm_ops rm31100_ts_pm_ops = {
+	.suspend = rm31100_ts_suspend,
+	.resume = rm31100_ts_resume,
+};
+#endif
+
+static int rm_input_dev_create(struct rm31100_ts *ts)
+{
+	struct input_dev *input_device;
+	int rc = 0;
+	ts->prev_touches = 0;
+
+	input_device = input_allocate_device();
+	if (!input_device) {
+		rc = -ENOMEM;
+		goto error_alloc_dev;
+	}
+
+	ts->input = input_device;
+	input_device->name = "raydium_ts"/*ts->pdata->ts_name*/;
+	input_device->id.bustype = BUS_I2C;
+	input_device->dev.parent = &client->dev;
+	input_set_drvdata(input_device, ts);
+
+	__set_bit(EV_ABS, input_device->evbit);
+	__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+	/*__set_bit(EV_SYN, input_device->evbit);*/
+	/*__set_bit(BTN_TOUCH, input_device->keybit);*/
+
+
+	if (ts->device_id == rm31100) {
+		/* set up virtual key */
+		__set_bit(EV_KEY, input_device->evbit);
+		/* set dummy key to make driver work with virtual keys */
+		input_set_capability(input_device, EV_KEY, KEY_PROG1);
+	}
+	/*For protocol B*/
+	input_mt_init_slots(input_device,
+		MAX_REPORT_TOUCHED_POINTS,
+		0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_X,
+		ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+		ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+	input_set_abs_params(input_device, ABS_MT_PRESSURE,
+		0, 0xFF, 0, 0);
+	/*input_set_abs_params(input_device, ABS_MT_TRACKING_ID,
+			ts->pdata->min_tid, ts->pdata->max_tid, 0, 0);*/
+	rc = input_register_device(input_device);
+	if (rc)
+		goto error_unreg_device;
+
+	return 0;
+
+error_unreg_device:
+error_wq_create:
+	input_free_device(input_device);
+error_alloc_dev:
+	kfree(ts->touch_data);
+	return rc;
+}
+
+static int rm31100_initialize(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+	int rc = 0, retry_cnt = 0, temp_reg;
+	/* power on the device */
+	if (ts->pdata->power_on) {
+		rc = ts->pdata->power_on(1);
+		if (rc) {
+			pr_err("%s: Unable to power on the device\n", __func__);
+			goto error_dev_setup;
+		}
+	}
+
+	/* read one byte to make sure i2c device exists */
+	if (ts->device_id == rm3110x)
+		temp_reg = 0x01;
+	else if (ts->device_id == rm31100)
+		temp_reg = 0x00;
+	else
+		temp_reg = 0x05;
+
+	rc = rm31100_ts_read_reg_u8(client, temp_reg);
+	if (rc < 0) {
+		dev_err(&client->dev, "i2c sanity check failed\n");
+		goto error_power_on;
+	}
+
+	rc = rm31100_ts_init_ts(client, ts);
+	if (rc < 0) {
+		dev_err(&client->dev, "rm31100_ts init failed\n");
+		goto error_mutex_destroy;
+	}
+
+	if (ts->pdata->resout_gpio < 0)
+		goto config_irq_gpio;
+
+	/* configure touchscreen reset out gpio */
+	rc = gpio_request(ts->pdata->resout_gpio, "rm31100_resout_gpio");
+	if (rc) {
+		pr_err("%s: unable to request gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_uninit_ts;
+	}
+
+	rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+	if (rc) {
+		pr_err("%s: unable to set direction for gpio %d\n",
+			__func__, ts->pdata->resout_gpio);
+		goto error_resout_gpio_dir;
+	}
+	/* reset gpio stabilization time */
+	msleep(20);
+
+	return 0;
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->sus_lock);
+	mutex_destroy(&ts->access_lock);
+error_power_on:
+/*	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);*/
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+}
+
+static void rm_initialize_async(void *data, async_cookie_t cookie)
+{
+	struct rm31100_ts *ts = data;
+	struct i2c_client *client = ts->client;
+	unsigned long irqflags;
+	int err = 0;
+
+	mutex_lock(&ts->sus_lock);
+
+	err = rm31100_initialize(client);
+	if (err < 0) {
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+		goto error_free_mem;
+	}
+
+	err = rm_input_dev_create(ts);
+	if (err) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		goto err_release;
+	}
+
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	err = request_threaded_irq(ts->pen_irq, NULL,
+				   rm31100_ts_irq,
+				   irqflags | IRQF_ONESHOT,
+				   ts->client->dev.driver->name, ts);
+	if (err) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto err_release;
+	}
+
+	mutex_unlock(&ts->sus_lock);
+
+	return;
+err_release:
+error_free_mem:
+	kfree(ts);
+	mutex_unlock(&ts->sus_lock);
+	rm31100_ts_remove(client);
+	return;
+}
+
+
+/*static int __devinit rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)*/
+static int rm31100_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct rm31100_ts *ts;
+	struct rm3110x_ts_platform_data *pdata = client->dev.platform_data;
+	int rc/*, temp_reg*/;
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev, "I2C functionality not supported\n");
+		return -EIO;
+	}
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+		I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
+		return -ENODEV;
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+	pts = ts;
+
+	/* Enable runtime PM ops, start in ACTIVE mode */
+	rc = pm_runtime_set_active(&client->dev);
+	if (rc < 0)
+		dev_warn(&client->dev, "unable to set runtime pm state\n");
+	pm_runtime_enable(&client->dev);
+
+	ts->client = client;
+	ts->pdata = pdata;
+	i2c_set_clientdata(client, ts);
+	ts->device_id = id->driver_data;
+
+	if (ts->pdata->dev_setup) {
+		rc = ts->pdata->dev_setup(1);
+		if (rc < 0) {
+			dev_err(&client->dev, "dev setup failed\n");
+			goto error_touch_data_alloc;
+		}
+	}
+
+
+	ts->is_suspended = false;
+	ts->int_pending = false;
+	mutex_init(&ts->sus_lock);
+	mutex_init(&ts->access_lock);
+
+	async_schedule(rm_initialize_async, ts);
+
+	device_init_wakeup(&client->dev, ts->pdata->wakeup);
+	return 0;
+error_reg_misc_dev:
+error_req_irq_fail:
+
+error_resout_gpio_dir:
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+	input_unregister_device(ts->input);
+	kfree(ts->touch_data);
+error_mutex_destroy:
+	mutex_destroy(&ts->sus_lock);
+	mutex_destroy(&ts->access_lock);
+error_power_on:
+/*	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);*/
+error_dev_setup:
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+error_touch_data_alloc:
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+	kfree(ts);
+	return rc;
+}
+
+/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
+static int __exit rm31100_ts_remove(struct i2c_client *client)
+{
+	struct rm31100_ts *ts = i2c_get_clientdata(client);
+
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_disable(&client->dev);
+
+	device_init_wakeup(&client->dev, 0);
+	free_irq(ts->pen_irq, ts);
+
+	gpio_free(ts->pdata->irq_gpio);
+
+	if (ts->pdata->resout_gpio >= 0)
+		gpio_free(ts->pdata->resout_gpio);
+	input_unregister_device(ts->input);
+
+	mutex_destroy(&ts->sus_lock);
+	mutex_destroy(&ts->access_lock);
+
+	if (ts->pdata->power_on)
+		ts->pdata->power_on(0);
+
+	if (ts->pdata->dev_setup)
+		ts->pdata->dev_setup(0);
+
+	kfree(ts->touch_data);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id rm31100_ts_id[] = {
+	{"RM31100", rm31100},
+	{"RM3110x", rm3110x},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, RM31100_ts_id);
+
+
+static struct i2c_driver rm31100_ts_driver = {
+	.driver = {
+		.name = "raydium_ts",/*rm31100_ts*/
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &rm31100_ts_pm_ops,
+#endif
+	},
+	.probe = rm31100_ts_probe,
+	/*.remove		= __devexit_p(RM31100_ts_remove),*/
+	.remove = __exit_p(rm31100_ts_remove),
+	.id_table = rm31100_ts_id,
+};
+
+static int __init rm31100_ts_init(void)
+{
+	int rc;
+	int rc2;
+
+	rc = i2c_add_driver(&rm31100_ts_driver);
+
+	rc2 = driver_create_file(&rm31100_ts_driver.driver,
+		&driver_attr_myAttr);
+
+	return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+late_initcall(RM31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+	return i2c_del_driver(&RM31100_ts_driver);
+}
+module_exit(RM31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rm31100-rm3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:rm31100_ts");
diff --git a/include/linux/input/rm31100_ts.h b/include/linux/input/rm31100_ts.h
new file mode 100644
index 0000000..2397436
--- /dev/null
+++ b/include/linux/input/rm31100_ts.h
@@ -0,0 +1,60 @@
+/* Header file for:
+ * Raydium rm31100 Prototype touchscreen driver.
+ *
+ * Copyright (C) 2012, Raydium Semiconductor, Inc.
+ *
+ * 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.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor at www.rad-ic.com
+ *
+ * History:
+ *			(C) 2012 Raydium - Update for GPL distribution
+ *			(C) 2009 Enea - Original prototype
+ *
+ */
+#ifndef __RM3110xTS_H__
+#define __RM3110xTS_H__
+
+
+/* rm3110x platform data
+ */
+struct rm3110x_ts_platform_data {
+	int (*power_on)(int on);
+	int (*dev_setup)(bool on);
+	const char *ts_name;
+	u32 dis_min_x; /* display resoltion */
+	u32 dis_max_x;
+	u32 dis_min_y;
+	u32 dis_max_y;
+	u32 min_touch; /* no.of touches supported */
+	u32 max_touch;
+	u32 min_tid; /* track id */
+	u32 max_tid;
+	u32 min_width;/* size of the finger */
+	u32 max_width;
+	u32 res_x; /* TS resolution */
+	u32 res_y;
+	u32 swap_xy;
+	u32 flags;
+	u16 invert_x;
+	u16 invert_y;
+	u8 nfingers;
+	u32 irq_gpio;
+	int resout_gpio;
+	bool wakeup;
+	u32 irq_cfg;
+};
+
+#endif
-- 
2.1.2

--
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