2014-11-14 07:19:48

by jeffrey.lin

[permalink] [raw]
Subject: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

From: "jeffrey.lin" <[email protected]>

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: [email protected]

chromeos: config: add Raydium touch default config

Add to default table and set as not to use

Signed-off-by: [email protected]
---
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 http://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 http://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


2014-11-14 17:44:04

by Benson Leung

[permalink] [raw]
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

Hi Jeffrey,


On Thu, Nov 13, 2014 at 11:19 PM, jeffrey.lin <[email protected]> wrote:
> 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

This looks like it came from the chromiumos kernel. When you send a
patch to the linux-input list, make sure to strip out this stuff in
the commit message.

>
> Signed-off-by: [email protected]
>
> chromeos: config: add Raydium touch default config
>
> Add to default table and set as not to use
>
> Signed-off-by: [email protected]
> ---
> chromeos/config/base.config | 1 +

This file doesn't exist in upstream linux (only exists in
chromeos-kernels) so don't include this in the patch.

Basically, make sure whatever you send to the list actually applies to
mainline linux or linux-next.

> 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


Thanks

--
Benson Leung
Software Engineer, Chrom* OS
[email protected]

2014-11-14 18:00:47

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH] driver: input :touchscreen : add Raydium I2C touch driver

Hi Jeffrey,

On Fri, Nov 14, 2014 at 03:19:22PM +0800, jeffrey.lin wrote:
> From: "jeffrey.lin" <[email protected]>
>
> this patch is porting Raydium I2C touch driver.

Thank you for your submission.

> Developer can enable
> Raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM31100"
> in config/base.config

This does not make sense in context of mainline kernel, it is Chromeos
(or Android?) construct.

Also, for mainline submission, please get rid of EARLYSUSPEND bits,
convert the driver to threaded interrupt handler instead of private
workqueue, remove driver specific device interface, #if 0., DEBUG_PRINT,
convert to MT-B protocol.

Thank you.

--
Dmitry