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

this patch is porting Raydium I2C touch driver. Developer can enable
 Raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM31100"
 in config/base.config

Change-Id: Idae54cc4bca17f321a1d0895a8b59680bf9af859

Signed-off-by: jeffrey....@rad-ic.com

chromeos: config: add Raydium touch default config

Add to default table and set as not to use

Signed-off-by: jeffrey....@rad-ic.com
---
 chromeos/config/base.config         |    1 +
 drivers/input/touchscreen/RM31100.c | 1037 +++++++++++++++++++++++++++++++++++
 include/linux/input/RM31100.h       |   60 ++
 3 files changed, 1098 insertions(+)
 create mode 100644 drivers/input/touchscreen/RM31100.c
 create mode 100644 include/linux/input/RM31100.h

diff --git a/chromeos/config/base.config b/chromeos/config/base.config
index 59fa689..45b1023 100644
--- a/chromeos/config/base.config
+++ b/chromeos/config/base.config
@@ -1727,6 +1727,7 @@ CONFIG_TOUCHSCREEN_ATMEL_MXT=y
 # CONFIG_TOUCHSCREEN_TSC2007 is not set
 # CONFIG_TOUCHSCREEN_TSC_SERIO is not set
 CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+# CONFIG_TOUCHSCREEN_RM_TS is not set
 # CONFIG_TOUCHSCREEN_WACOM_I2C is not set
 # CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
 # CONFIG_TPS6105X is not set
diff --git a/drivers/input/touchscreen/RM31100.c 
b/drivers/input/touchscreen/RM31100.c
new file mode 100644
index 0000000..58eb850
--- /dev/null
+++ b/drivers/input/touchscreen/RM31100.c
@@ -0,0 +1,1037 @@
+/* Source for:
+ * Raydium RM31100 Prototype touchscreen driver.
+ * drivers/input/touchscreen/RM31100.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/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.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+/*#include <plat/gpio-cfg.h>*/
+#include <linux/miscdevice.h>
+/*#include <asm/uaccess.h> copy_to_user() */
+#include <linux/uaccess.h>
+
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+
+/* Early-suspend level */
+#define RM31100_TS_SUSPEND_LEVEL 1
+#endif
+
+#define RM31100            0x0
+#define RM3110x            0x1
+
+#define INVALID_DATA   0xff
+
+#define TOUCHSCREEN_TIMEOUT    (msecs_to_jiffies(10))
+#define INITIAL_DELAY          (msecs_to_jiffies(25000))
+
+#define EVAL_REPORT_RATE    1
+#define ENABLE_DEBUG_MSG    0
+
+#if ENABLE_DEBUG_MSG
+#define DEBUG_PRINT     printk
+#else
+#define DEBUG_PRINT(arg...)
+#endif
+
+#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 workqueue_struct *wq;
+       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;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+       struct early_suspend early_suspend;
+#endif
+};
+
+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);
+}
+
+static int dev_open(struct inode *inode, struct file *filp)
+{
+       /*printk("***%s***", __func__);*/
+       mutex_lock(&pts->access_lock);
+       return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+       /*printk("***%s***", __func__);*/
+       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;
+
+#if 0
+       DEBUG_PRINT("===%s===", __func__);
+       copy_to_user(buf, &out[idx], count);
+       idx = (idx+3);
+       if (idx >= sizeof(out)) {
+               idx = 0;
+               return 0;
+       }
+       return 3;
+#else
+       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);
+
+       DEBUG_PRINT("dev_read(): count=%zu, data = ", count);
+       for (i = 0; i < count; i++)
+               DEBUG_PRINT("%hhx ", kbuf[i]);
+
+       DEBUG_PRINT("\n");
+
+       if (copy_to_user(buf, kbuf, count) == 0)
+               return count;
+       else
+               return -EFAULT;
+#endif
+}
+
+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) {
+               DEBUG_PRINT("kmalloc() fail\n");
+               return -ENOMEM;
+       }
+
+       if (copy_from_user(kbuf, buf, count) == 0) {
+               DEBUG_PRINT("dev_write(): count=%zu, data = ", count);
+               for (i = 0; i < count; i++)
+                       DEBUG_PRINT("%hhx ", kbuf[i]);
+
+               DEBUG_PRINT("\n");
+               pts->client->addr = I2C_CLIENT_ADDR;
+               if (RM31100_ts_write(pts->client, kbuf, count) < 0)
+                       status = -EFAULT;
+               else
+                       status = count;
+       } else {
+               DEBUG_PRINT("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,
+};
+
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+#if 0
+       unsigned char tmp[100];
+
+       /*i2c_master_recv(pts->client, tmp, 1);*/
+       if (RM31100_ts_read(pts->client, 50, tmp, 61) < 0)
+               DEBUG_PRINT("RM31100_ts_read fail\n");
+
+       return snprintf(buf, PAGE_SIZE, "%hhu\n", tmp[0]);
+#else
+       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);
+       /*
+       printk("\n");
+       for (i = 0; i < num; i++)
+       {
+       printk("%hhu ", buf[i]);
+       }
+       printk("\n");
+       */
+       return 0;
+#endif
+}
+
+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};
+#if 0
+       unsigned char val, ret;
+
+       ret = sscanf(buf, "%hhx", &val);
+/*
+       if (RM31100_ts_write(pts->client, &val, 1) < 0)
+               printk("RM31100_ts_write fail\n");
+*/
+
+       return count;
+#else
+       pts->client->addr = I2C_CLIENT_ADDR;
+       RM31100_ts_write(pts->client, pkt, sizeof(pkt));
+       /*RM31100_ts_write_reg_u8(pts->client, 12, 17);*/
+#endif
+       /*printk("***%s***", __func__);*/
+       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);
+
+       DEBUG_PRINT("%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]);
+                       report_data(ts, x, y, pressure, id);
+               }
+       } else
+               input_mt_sync(ts->input);
+
+#if 0
+       for (i = 0; i < ts->prev_touches - (char)touches; i++) {
+               input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, 0);
+               input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, 0);
+               input_mt_sync(ts->input);
+       }
+#endif
+
+       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*/
+
+
+       ts = container_of(work, struct RM31100_ts,
+               work.work);
+       DEBUG_PRINT("****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*/
+#if 1 /*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);
+#endif
+
+       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;
+/*printk("Report Rate = %d(100 frames / %d us\n",100000000 / us, us);*/
+               cnt = 0;
+       }
+#endif /* EVAL_REPORT_RATE*/
+schedule:
+
+       mutex_unlock(&ts->access_lock);
+       DEBUG_PRINT("****Leave RM31100_ts_xy_worker******\n");
+       enable_irq(ts->pen_irq);
+}
+
+static irqreturn_t RM31100_ts_irq(int irq, void *dev_id)
+{
+       struct RM31100_ts *ts = dev_id;
+
+       disable_irq_nosync(irq);
+
+       queue_delayed_work(ts->wq, &ts->work, 0);
+
+       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;
+       /*printk("RM31100_ts_init_ts!!\n");*/
+       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;
+       }
+#if 1 /* w001 */
+       else {
+               ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+               ts->dd->touch_meta_data;
+               ts->dd->touch_index = 0x0;
+       }
+#endif
+       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;
+       }
+
+       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 = 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);
+       }
+
+       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);
+#if 0
+       input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+                       ts->pdata->min_touch, ts->pdata->max_touch, 0, 0);
+       input_set_abs_params(input_device, ABS_MT_WIDTH_MAJOR,
+                       ts->pdata->min_width, ts->pdata->max_width, 0, 0);
+#endif
+       input_set_abs_params(input_device, ABS_MT_TRACKING_ID,
+                       ts->pdata->min_tid, ts->pdata->max_tid, 0, 0);
+
+       ts->wq = create_singlethread_workqueue("kworkqueue_ts");
+       if (!ts->wq) {
+               dev_err(&client->dev, "Could not create workqueue\n");
+               goto error_wq_create;
+       }
+
+       INIT_DELAYED_WORK(&ts->work, RM31100_ts_xy_worker);
+
+       rc = input_register_device(input_device);
+       if (rc)
+               goto error_unreg_device;
+
+       /*printk("RM31100_init_OKK\n"); */
+       return 0;
+
+error_unreg_device:
+       destroy_workqueue(ts->wq);
+error_wq_create:
+       input_free_device(input_device);
+error_alloc_dev:
+       kfree(ts->touch_data);
+       return rc;
+}
+
+#ifdef CONFIG_PM
+static int RM31100_ts_suspend(struct device *dev)
+{
+       struct RM31100_ts *ts = dev_get_drvdata(dev);
+       int rc = 0;
+       /*printk("****RM31100_ts_suspend******\n"); */
+       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);
+
+               rc = cancel_delayed_work_sync(&ts->work);
+
+               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;
+                       }
+               }
+       }
+       /*printk("****Leave RM31100_ts_suspend******\n"); */
+       return 0;
+}
+
+static int RM31100_ts_resume(struct device *dev)
+{
+       struct RM31100_ts *ts = dev_get_drvdata(dev);
+
+       int rc = 0;
+       /*printk("**** RM31100_ts_resume******\n"); */
+       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;
+
+                       /* start a delayed work */
+                       queue_delayed_work(ts->wq, &ts->work, 0);
+               }
+               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");
+               }
+       }
+       /*printk("**** Leave RM31100_ts_resume******\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;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void RM31100_ts_early_suspend(struct early_suspend *h)
+{
+       struct RM31100_ts *ts =
+               container_of(h, struct RM31100_ts, early_suspend);
+
+       RM31100_ts_suspend(&ts->client->dev);
+}
+
+static void RM31100_ts_late_resume(struct early_suspend *h)
+{
+       struct RM31100_ts *ts = container_of(h,
+               struct RM31100_ts, early_suspend);
+
+       RM31100_ts_resume(&ts->client->dev);
+}
+#endif
+
+static struct dev_pm_ops RM31100_ts_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+       .suspend = RM31100_ts_suspend,
+       .resume = RM31100_ts_resume,
+#endif
+};
+#endif
+
+/*static int __devinit RM31100_ts_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)*/
+static int __init 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;
+
+/*printk("RM31100_ts_probe\n"); */
+       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;
+       }
+
+       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_dbg(&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;
+               }
+       }
+
+       /* 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 (id->driver_data == RM3110x)
+               temp_reg = 0x01;
+       else if (id->driver_data == RM31100)
+               temp_reg = 0x00;
+       else
+               temp_reg = 0x05;
+
+/*printk("RM31100 read one byte to make sure i2c device exists\n");*/
+
+       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;
+       }
+
+       ts->is_suspended = false;
+       ts->int_pending = false;
+       mutex_init(&ts->sus_lock);
+       mutex_init(&ts->access_lock);
+
+       rc = RM31100_ts_init_ts(client, ts);
+       if (rc < 0) {
+               dev_err(&client->dev, "RM31100 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);
+
+config_irq_gpio:
+       /* 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 error_irq_gpio_req;
+       }
+
+       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 error_irq_gpio_dir;
+       }
+
+       ts->pen_irq = gpio_to_irq(ts->pdata->irq_gpio);
+       rc = request_irq(ts->pen_irq, RM31100_ts_irq,
+                               IRQF_TRIGGER_FALLING,
+                               ts->client->dev.driver->name, ts);
+       if (rc) {
+               dev_err(&ts->client->dev, "could not request irq\n");
+               goto error_req_irq_fail;
+       }
+
+       device_init_wakeup(&client->dev, ts->pdata->wakeup);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+                                               RM31100_TS_SUSPEND_LEVEL;
+       ts->early_suspend.suspend = RM31100_ts_early_suspend;
+       ts->early_suspend.resume = RM31100_ts_late_resume;
+       register_early_suspend(&ts->early_suspend);
+#endif
+
+       rc = misc_register(&raydium_ts_miscdev);
+       if (rc) {
+               dev_err(&ts->client->dev,
+               "Raydium TS: cannot register miscdev:%d\n", rc);
+               goto error_reg_misc_dev;
+       }
+
+
+       return 0;
+error_reg_misc_dev:
+error_req_irq_fail:
+error_irq_gpio_dir:
+       gpio_free(ts->pdata->irq_gpio);
+error_irq_gpio_req:
+error_resout_gpio_dir:
+       if (ts->pdata->resout_gpio >= 0)
+               gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+       destroy_workqueue(ts->wq);
+       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);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+       unregister_early_suspend(&ts->early_suspend);
+#endif
+       pm_runtime_set_suspended(&client->dev);
+       pm_runtime_disable(&client->dev);
+
+       device_init_wakeup(&client->dev, 0);
+
+       cancel_delayed_work_sync(&ts->work);
+
+       free_irq(ts->pen_irq, ts);
+
+       gpio_free(ts->pdata->irq_gpio);
+
+       if (ts->pdata->resout_gpio >= 0)
+               gpio_free(ts->pdata->resout_gpio);
+
+       destroy_workqueue(ts->wq);
+
+       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 = "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;
+       /*printk("Raydium I2C Driver Initial!!!!!!!\n");*/
+       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.h b/include/linux/input/RM31100.h
new file mode 100644
index 0000000..714a14b
--- /dev/null
+++ b/include/linux/input/RM31100.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-input" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to