@@ -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
@@ -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
new file mode 100644
@@ -0,0 +1,846 @@
+/*
+ * Raydium RM31100_ts touchscreen driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * 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/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#ifdef CONFIG_MISC_DEV
+#include <linux/miscdevice.h>
+#endif
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/input/mt.h>
+
+#define rm31100 0x0
+#define rm3110x 0x1
+
+#define INVALID_DATA 0xff
+#define MAX_REPORT_TOUCHED_POINTS 10
+
+#define I2C_CLIENT_ADDR 0x39
+#define I2C_DMA_CLIENT_ADDR 0x5A
+
+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;
+};
+
+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 ABS min*/
+ u32 dis_max_x;/* display resoltion ABS max*/
+ 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 unit*/
+ u32 res_y;
+ u32 swap_xy;
+ u8 nfingers;
+ u32 irq_gpio;
+ int resout_gpio;
+ bool wakeup;
+ u32 irq_cfg;
+};
+
+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,
+ .touch_bytes = 6,
+ .touch_meta_data = 1,
+ .finger_size = 70,
+ },
+};
+
+struct rm31100_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ 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 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 = ®
+
+ 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_client_dma(struct i2c_client *client, u8 *buf, int num)
+{
+ struct i2c_msg xfer_msg[1];
+
+ xfer_msg[0].addr = I2C_DMA_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 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 *dev, u16 x, u16 y,
+ u8 pressure, u8 id)
+{
+ struct input_dev *input_dev = dev->input;
+ if (dev->pdata->swap_xy)
+ swap(x, y);
+
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, dev->dd->finger_size);
+/*
+ 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 = 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_mt_report_pointer_emulation(ts->input, true);
+ input_sync(ts->input);
+}
+
+/*static void rm31100_ts_xy_worker(struct work_struct *work) JL remove*/
+static void rm31100_ts_xy_worker(struct rm31100_ts *work)
+{
+ int rc;
+ u8 client_dma_package[4] = {0x0f, 0x00, 0x20, 0x81};
+ struct rm31100_ts *ts = work;
+
+ if (ts->is_suspended == true) {
+ dev_dbg(&ts->client->dev, "TS is supended\n");
+ ts->int_pending = true;
+ return;
+ }
+
+ mutex_lock(&ts->access_lock);
+ /* read data from DATA_REG */
+ /*RM31100 DMA Mode*/
+ rc = rm31100_ts_write_client_dma(ts->client, client_dma_package, 0x04);
+ if (rc < 0) {
+ dev_err(&ts->client->dev, "write client dma failed\n");
+ goto schedule;
+ }
+ 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);
+schedule:
+ mutex_unlock(&ts->access_lock);
+}
+
+static irqreturn_t rm31100_ts_irq(int irq, void *dev_id)
+{
+ struct rm31100_ts *ts = dev_id;
+
+ rm31100_ts_xy_worker(ts);
+ 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;
+
+ if (device_may_wakeup(dev)) {
+ /* mark suspend flag */
+ ts->is_suspended = true;
+ enable_irq_wake(ts->pen_irq);
+ }
+
+ disable_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);
+
+ ts->is_suspended = false;
+
+ if (ts->int_pending == true)
+ ts->int_pending = false;
+ } else {
+ if (ts->pdata->power_on) {
+ rc = ts->pdata->power_on(1);
+ if (rc) {
+ dev_err(dev, "unable to resume\n");
+ return rc;
+ }
+ }
+
+ 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;
+}
+
+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";
+ input_device->id.bustype = BUS_I2C;
+ input_device->dev.parent = &ts->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(BTN_TOUCH, input_device->keybit);
+
+
+ if (ts->device_id == rm31100) {
+ /* set up virtual key */
+ __set_bit(EV_KEY, input_device->evbit);
+ }
+ 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_TOUCH_MAJOR,
+ 0, 0xFF, 0, 0);
+ rc = input_register_device(input_device);
+ if (rc)
+ goto error_unreg_device;
+
+ return 0;
+
+error_unreg_device:
+ input_free_device(input_device);
+error_alloc_dev:
+ ts->input = NULL;
+ 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;
+ }
+
+ /* 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->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);
+ return rc;
+}
+
+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->access_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 error_goio_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 error_dev_release;
+ }
+
+ mutex_unlock(&ts->access_lock);
+
+ return;
+
+error_dev_release:
+ input_free_device(ts->input);
+error_goio_release:
+ if (ts->pdata->resout_gpio >= 0)
+ gpio_free(ts->pdata->resout_gpio);
+error_free_mem:
+ mutex_unlock(&ts->access_lock);
+ kfree(ts);
+ 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*/;
+ union i2c_smbus_data dummy;
+
+ 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); JL remove*/
+ mutex_init(&ts->access_lock);
+
+ async_schedule(rm_initialize_async, ts);
+
+ device_init_wakeup(&client->dev, ts->pdata->wakeup);
+ return 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 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); JL remove*/
+ 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",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &rm31100_ts_pm_ops,
+#endif
+ },
+ .probe = rm31100_ts_probe,
+ .remove = 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.
+ */
+module_init(rm31100_ts_init);
+
+static void __exit rm31100_ts_exit(void)
+{
+ 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");