blob: 6e850926e3784e60c21bbe58958b19935c481370 [file] [log] [blame]
/*
*
* Touch Screen I2C Driver for EETI Controller
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/freezer.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/kfifo.h>
#include <linux/version.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/i2c/egalax_i2c_ts.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif /* CONFIG_HAS_EARLYSUSPEND */
#define MAX_I2C_LEN 10
#define MAX_SUPPORT_POINT 4
#define REPORTID_MOUSE 0x01
#define REPORTID_VENDOR 0x03
#define REPORTID_MTOUCH 0x04
#ifdef CONFIG_TOUCHSCREEN_SCALE
#define X_ORIG 1366
#define Y_ORIG 768
static volatile int g_x_new = X_ORIG;
static volatile int g_y_new = Y_ORIG;
#define X_ORIG_OFF(x_new) ((X_ORIG - (x_new)) / 2)
#define Y_ORIG_OFF(y_new) ((Y_ORIG - (y_new)) / 2)
#define X_OFF(x_new) ((X_ORIG_OFF(x_new) * 2048) / X_ORIG)
#define Y_OFF(y_new) ((Y_ORIG_OFF(y_new) * 2048) / Y_ORIG)
#define X_SCALED(x, x_new) ((x_new) ? ((((x) - X_OFF(x_new)) * X_ORIG) / (x_new)) : (x))
#define Y_SCALED(y, y_new) ((y_new) ? ((((y) - Y_OFF(y_new)) * Y_ORIG) / (y_new)) : (y))
#define X_MIN(x_new) X_OFF(x_new)
#define X_MAX(x_new) (((X_ORIG_OFF(x_new) + (x_new)) * 2048) / X_ORIG)
#define Y_MIN(y_new) Y_OFF(y_new)
#define Y_MAX(y_new) (((Y_ORIG_OFF(y_new) + (y_new)) * 2048) / Y_ORIG)
#endif /* CONFIG_TOUCHSCREEN_SCALE */
#ifdef CONFIG_CP_TS_SOFTKEY
#define BTN_HOME 2
#define BTN_BACKK 1
#define BTN_MENU 3
#define BTN_SEARCH 0
#define MAX_BTN 4
#define BTN_REPORT_MOD 5
static unsigned short BtnState[MAX_BTN];
static struct input_dev *softkey_input_dev;
#endif /* CONFIG_CP_TS_SOFTKEY */
#define MAX_PROC_BUF_SIZE 256
#define PROC_PARENT_DIR "touchscreen"
#define PROC_ENTRY_DEBUG "debug"
#define PROC_ENTRY_CMD "cmd"
#define PROC_ENTRY_WAKEUP "wakeup"
#define PROC_ENTRY_X_SCALE "x-scale"
#define PROC_ENTRY_Y_SCALE "y-scale"
static const u8 cmd_str_query_firmware[MAX_I2C_LEN]={0x03, 0x03, 0x0A, 0x01, 0x44, 0, 0, 0, 0, 0};
static const u8 cmd_str_idle[MAX_I2C_LEN]={0x03, 0x06, 0x0A, 0x04, 0x36, 0x3F, 0x01, 0x05, 0, 0};
static const u8 cmd_str_sleep[MAX_I2C_LEN]={0x03, 0x05, 0x0A, 0x03, 0x36, 0x3F, 0x02, 0, 0, 0};
/* flag to turn on debug prints */
static volatile int gDbg = 0;
#define TS_DEBUG(format, args...) \
do { if (gDbg) printk(KERN_WARNING "[egalax_i2c]: " format, ## args); } while (0)
#define DBG() \
do { if (gDbg) printk("[%s]:%d => \n", __FUNCTION__, __LINE__); } while (0)
#define TS_ERR(fmt, args...) printk(KERN_ERR "[egalax_i2c]: " fmt, ## args)
#define TS_INFO(fmt, args...) printk(KERN_INFO "[egalax_i2c]: " fmt, ## args)
struct proc_dir
{
struct proc_dir_entry *parent;
};
struct point_data
{
short Status;
short X;
short Y;
};
struct _egalax_i2c {
struct workqueue_struct *ktouch_wq;
struct work_struct work;
struct mutex mutex_wq;
struct i2c_client *client;
struct egalax_i2c_ts_cfg hw_cfg;
#if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND)
atomic_t is_suspended;
#endif
struct proc_dir proc;
};
static struct input_dev *input_dev;
static struct _egalax_i2c *p_egalax_i2c_dev;
static struct point_data PointBuf[MAX_SUPPORT_POINT];
static int wakeup_controller(int gpio)
{
gpio_direction_output(gpio, 0);
msleep(250);
gpio_direction_input(gpio);
TS_INFO("controller wake up done\n");
return 0;
}
#ifdef CONFIG_CP_TS_SOFTKEY
static void report_softkey(int status, int btn_id)
{
int keyValue = 0;
switch (btn_id)
{
case BTN_HOME:
keyValue = KEY_HOME;
break;
case BTN_BACKK:
keyValue = KEY_BACK;
break;
case BTN_MENU:
keyValue = KEY_MENU;
break;
case BTN_SEARCH:
keyValue = KEY_SEARCH;
break;
default:
return;
}
TS_DEBUG("report soft button key:0x%02x status:%d\n", keyValue, status);
input_report_key(softkey_input_dev, keyValue, status);
input_sync(softkey_input_dev);
}
#endif
static int LastUpdateID = 0;
static void ProcessReport(unsigned char *buf, int buflen)
{
int i;
short X = 0, Y = 0, ContactID = 0, Status = 0;
if (buflen != MAX_I2C_LEN || buf[0] != REPORTID_MTOUCH)
{
TS_ERR("I2C incorrect buflen=%d\n", buflen);
return;
}
Status = buf[1] & 0x01;
ContactID = (buf[1] & 0x7C) >> 2;
X = ((buf[3] << 8) + buf[2]) >> 4;
Y = ((buf[5] << 8) + buf[4]) >> 4;
/* TODO: error if contactID > MAX_SUPPORT_POINT */
if (ContactID >= MAX_SUPPORT_POINT)
{
TS_ERR("Invalid contact id = %d\n", ContactID);
return;
}
#ifdef CONFIG_TOUCHSCREEN_SCALE
if ( !((g_x_new == X_ORIG) && (g_y_new = Y_ORIG)) && !((g_x_new == 0) && (g_y_new == 0)) )
{
if (X < X_MIN(g_x_new) || X > X_MAX(g_x_new) || Y < Y_MIN(g_y_new) || Y > Y_MAX(g_y_new))
return;
X = X_SCALED(X, g_x_new);
Y = Y_SCALED(Y, g_y_new);
}
#endif
PointBuf[ContactID].Status = Status;
PointBuf[ContactID].X = X;
PointBuf[ContactID].Y = Y;
TS_DEBUG("got touch point[%d]: status=%d X=%d Y=%d\n", ContactID, Status, X, Y);
if (!Status || (ContactID <= LastUpdateID))
{
for (i = 0; i < MAX_SUPPORT_POINT; i++)
{
if (PointBuf[i].Status > 0)
{
input_report_abs(input_dev, ABS_MT_TRACKING_ID, i);
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, PointBuf[i].Status);
input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 0);
input_report_abs(input_dev, ABS_MT_POSITION_X, PointBuf[i].X);
input_report_abs(input_dev, ABS_MT_POSITION_Y, PointBuf[i].Y);
TS_DEBUG("input sync point data [%d]!\n", i);
}
input_mt_sync(input_dev);
if (PointBuf[i].Status == 0)
PointBuf[i].Status--;
}
input_sync(input_dev);
TS_DEBUG("input sync point data done!\n");
}
LastUpdateID = ContactID;
}
static int egalax_i2c_measure(struct i2c_client *client)
{
u8 x_buf[MAX_I2C_LEN];
int count, loop = 3;
#ifdef CONFIG_CP_TS_SOFTKEY
int btn_id, btn_state;
#endif
DBG();
do
{
count = i2c_master_recv(client, x_buf, MAX_I2C_LEN);
} while (count == -EAGAIN && --loop);
if (count < 0 || (x_buf[0] != REPORTID_VENDOR && x_buf[0] != REPORTID_MTOUCH))
{
/*
* TODO: Ignore invalid data for now w/o printing it to the console until
* this gets sorted out with EETI/Compal
*/
//TS_ERR("I2C read error data with Len=%d header=%d\n", count, x_buf[0]);
return -EFAULT;
}
TS_DEBUG("read data with len=%d\n", count);
if (x_buf[0] == REPORTID_VENDOR)
{
TS_DEBUG("received command packet: "
"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
x_buf[0], x_buf[1], x_buf[2], x_buf[3], x_buf[4],
x_buf[5], x_buf[6], x_buf[7], x_buf[8], x_buf[9]);
}
if (count > 0 && x_buf[0] == REPORTID_MTOUCH)
{
ProcessReport(x_buf, count);
return count;
}
#ifdef CONFIG_CP_TS_SOFTKEY
if (count > 0 && x_buf[0] == REPORTID_VENDOR && x_buf[1]==6 && x_buf[4]==0x36 && x_buf[5]==0x2F)
{
TS_DEBUG("got virtual_key report: id=%d state=%d\n", x_buf[7], x_buf[6]);
btn_state = x_buf[6] & 0x01;
btn_id = x_buf[7];
if (btn_id >= 0 && btn_id < MAX_BTN)
{
if (btn_state == 0 && BtnState[btn_id])
{
report_softkey(btn_state, btn_id);
memset(BtnState, 0, sizeof(short) * MAX_BTN);
}
else if (btn_state)
{
if ((++BtnState[btn_id]) % BTN_REPORT_MOD == 0)
report_softkey(btn_state, btn_id);
}
}
return count;
}
#endif
return count;
}
static void egalax_i2c_wq(struct work_struct *work)
{
struct _egalax_i2c *egalax_i2c = container_of(work, struct _egalax_i2c, work);
struct i2c_client *client = egalax_i2c->client;
int gpio = irq_to_gpio(client->irq);
mutex_lock(&egalax_i2c->mutex_wq);
TS_DEBUG("egalax_i2c_wq run\n");
/* continue recv data while GPIO is pulled low */
while (!gpio_get_value(gpio))
{
egalax_i2c_measure(client);
schedule();
}
TS_DEBUG("egalax_i2c_wq leave\n");
mutex_unlock(&egalax_i2c->mutex_wq);
}
static irqreturn_t egalax_i2c_interrupt(int irq, void *dev_id)
{
struct _egalax_i2c *egalax_i2c = (struct _egalax_i2c *)dev_id;
TS_DEBUG("egalax_i2c_interrupt with irq:%d\n", irq);
/* postpone I2C transactions to the workqueue as it may block */
queue_work(egalax_i2c->ktouch_wq, &egalax_i2c->work);
return IRQ_HANDLED;
}
static int
proc_debug_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
int rc;
unsigned int debug;
unsigned char kbuf[MAX_PROC_BUF_SIZE];
if (count > MAX_PROC_BUF_SIZE)
count = MAX_PROC_BUF_SIZE;
rc = copy_from_user(kbuf, buffer, count);
if (rc)
{
printk(KERN_ERR "copy_from_user failed status=%d", rc);
return -EFAULT;
}
if (sscanf(kbuf, "%u", &debug) != 1)
{
printk(KERN_ERR "echo <debug> > /proc/%s/%s\n",
PROC_PARENT_DIR, PROC_ENTRY_DEBUG);
return count;
}
if (debug)
gDbg = 1;
else
gDbg = 0;
return count;
}
static int
proc_debug_read(char *buffer, char **start, off_t off, int count,
int *eof, void *data)
{
unsigned int len = 0;
if (off > 0)
return 0;
len += sprintf(buffer + len, "Debug print is %s\n", gDbg ? "enabled" : "disabled");
return len;
}
static int
proc_cmd_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
int rc;
unsigned int cmd;
u8 idle_interval, cmdbuf[MAX_I2C_LEN];
unsigned char kbuf[MAX_PROC_BUF_SIZE];
unsigned char cmd_str[3][10] = { "query", "idle", "sleep" };
if (count > MAX_PROC_BUF_SIZE)
count = MAX_PROC_BUF_SIZE;
rc = copy_from_user(kbuf, buffer, count);
if (rc)
{
printk(KERN_ERR "copy_from_user failed status=%d", rc);
return -EFAULT;
}
rc = sscanf(kbuf, "%u %c", &cmd, &idle_interval);
if (rc != 1 && rc != 2)
{
printk(KERN_ERR "echo <0=Query, 1=Idle, 2=Sleep> <idle_interval> > /proc/%s/%s\n",
PROC_PARENT_DIR, PROC_ENTRY_CMD);
return count;
}
switch (cmd)
{
case 0:
memcpy(cmdbuf, cmd_str_query_firmware, MAX_I2C_LEN);
break;
case 1:
memcpy(cmdbuf, cmd_str_idle, MAX_I2C_LEN);
if (rc == 2 && idle_interval < 10)
cmdbuf[7] = idle_interval;
break;
case 2:
memcpy(cmdbuf, cmd_str_sleep, MAX_I2C_LEN);
break;
default:
printk(KERN_ERR "echo <0=Query, 1=Idle, 2=Sleep> <idle_interval> > /proc/%s/%s\n",
PROC_PARENT_DIR, PROC_ENTRY_CMD);
return count;
}
printk(KERN_INFO "[egalax_i2c] About to send [%s] command\n", cmd_str[cmd]);
if (cmd == 1)
{
printk(KERN_INFO "[egalax_i2c] idle interval is %u ms\n",
(cmdbuf[7] + 1) * 50);
}
if (MAX_I2C_LEN == i2c_master_send(p_egalax_i2c_dev->client, cmdbuf,
MAX_I2C_LEN))
{
printk(KERN_INFO "[egalax_i2c]: Command string "
"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
"sent successfully\n",
cmdbuf[0], cmdbuf[1], cmdbuf[2], cmdbuf[3], cmdbuf[4],
cmdbuf[5], cmdbuf[6], cmdbuf[7], cmdbuf[8], cmdbuf[9]);
}
else
printk(KERN_ERR "[egalax_i2c]: Unable to send command over\n");
return count;
}
static int
proc_wakeup_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
int rc;
unsigned int wakeup;
unsigned char kbuf[MAX_PROC_BUF_SIZE];
if (count > MAX_PROC_BUF_SIZE)
count = MAX_PROC_BUF_SIZE;
rc = copy_from_user(kbuf, buffer, count);
if (rc)
{
printk(KERN_ERR "copy_from_user failed status=%d", rc);
return -EFAULT;
}
if (sscanf(kbuf, "%u", &wakeup) != 1)
{
printk(KERN_ERR "echo <wakeup> > /proc/%s/%s\n",
PROC_PARENT_DIR, PROC_ENTRY_WAKEUP);
return count;
}
if (wakeup)
wakeup_controller(p_egalax_i2c_dev->hw_cfg.gpio.event);
printk(KERN_ERR "[egalax_i2c]: Toggled GPIO %d to wake up the controller\n",
p_egalax_i2c_dev->hw_cfg.gpio.event);
return count;
}
#ifdef CONFIG_TOUCHSCREEN_SCALE
static int
proc_x_scale_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
int rc;
unsigned int x_scale;
unsigned char kbuf[MAX_PROC_BUF_SIZE];
if (count > MAX_PROC_BUF_SIZE)
count = MAX_PROC_BUF_SIZE;
rc = copy_from_user(kbuf, buffer, count);
if (rc)
{
printk(KERN_ERR "copy_from_user failed status=%d", rc);
return -EFAULT;
}
if (sscanf(kbuf, "%u", &x_scale) != 1)
{
printk(KERN_ERR "echo <x-scale> > /proc/%s/%s\n",
PROC_PARENT_DIR, PROC_ENTRY_X_SCALE);
return count;
}
g_x_new = x_scale;
return count;
}
static int
proc_x_scale_read(char *buffer, char **start, off_t off, int count,
int *eof, void *data)
{
unsigned int len = 0;
if (off > 0)
return 0;
len += sprintf( buffer + len,
"x-axis %sscaled (scale = %d)\n",
((g_x_new != 0) ? "non" : ""),
g_x_new );
return len;
}
static int
proc_y_scale_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
int rc;
unsigned int y_scale;
unsigned char kbuf[MAX_PROC_BUF_SIZE];
if (count > MAX_PROC_BUF_SIZE)
count = MAX_PROC_BUF_SIZE;
rc = copy_from_user(kbuf, buffer, count);
if (rc)
{
printk(KERN_ERR "copy_from_user failed status=%d", rc);
return -EFAULT;
}
if (sscanf(kbuf, "%u", &y_scale) != 1)
{
printk(KERN_ERR "echo <y-scale> > /proc/%s/%s\n",
PROC_PARENT_DIR, PROC_ENTRY_Y_SCALE);
return count;
}
g_y_new = y_scale;
return count;
}
static int
proc_y_scale_read(char *buffer, char **start, off_t off, int count,
int *eof, void *data)
{
unsigned int len = 0;
if (off > 0)
return 0;
len += sprintf( buffer + len,
"y-axis %sscaled (scale = %d)\n",
((g_y_new != 0) ? "non" : ""),
g_y_new );
return len;
}
#endif
static int proc_init(struct _egalax_i2c *egalax_i2c)
{
struct proc_dir *proc = &egalax_i2c->proc;
int rc;
struct proc_dir_entry *proc_debug, *proc_cmd, *proc_wakeup;
#ifdef CONFIG_TOUCHSCREEN_SCALE
struct proc_dir_entry *proc_x_scale, *proc_y_scale;
#endif
proc->parent = proc_mkdir(PROC_PARENT_DIR, NULL);
proc_debug = create_proc_entry(PROC_ENTRY_DEBUG, 0644, proc->parent);
if (proc_debug == NULL)
{
rc = -ENOMEM;
goto err_del_parent;
}
proc_debug->read_proc = proc_debug_read;
proc_debug->write_proc = proc_debug_write;
proc_debug->data = NULL;
proc_cmd = create_proc_entry(PROC_ENTRY_CMD, 0644, proc->parent);
if (proc_cmd == NULL)
{
rc = -ENOMEM;
goto err_del_debug;
}
proc_cmd->read_proc = NULL;
proc_cmd->write_proc = proc_cmd_write;
proc_cmd->data = NULL;
proc_wakeup = create_proc_entry(PROC_ENTRY_WAKEUP, 0644, proc->parent);
if (proc_wakeup == NULL)
{
rc = -ENOMEM;
goto err_del_cmd;
}
proc_wakeup->read_proc = NULL;
proc_wakeup->write_proc = proc_wakeup_write;
proc_wakeup->data = NULL;
#ifdef CONFIG_TOUCHSCREEN_SCALE
proc_x_scale = create_proc_entry(PROC_ENTRY_X_SCALE, 0644, proc->parent);
if (proc_x_scale == NULL)
{
rc = -ENOMEM;
goto err_del_wakeup;
}
proc_x_scale->read_proc = proc_x_scale_read;
proc_x_scale->write_proc = proc_x_scale_write;
proc_x_scale->data = NULL;
proc_y_scale = create_proc_entry(PROC_ENTRY_Y_SCALE, 0644, proc->parent);
if (proc_y_scale == NULL)
{
rc = -ENOMEM;
goto err_del_x_scale;
}
proc_y_scale->read_proc = proc_y_scale_read;
proc_y_scale->write_proc = proc_y_scale_write;
proc_y_scale->data = NULL;
#endif
return 0;
#ifdef CONFIG_TOUCHSCREEN_SCALE
err_del_x_scale:
remove_proc_entry(PROC_ENTRY_X_SCALE, proc->parent);
err_del_wakeup:
remove_proc_entry(PROC_ENTRY_WAKEUP, proc->parent);
#endif
err_del_cmd:
remove_proc_entry(PROC_ENTRY_CMD, proc->parent);
err_del_debug:
remove_proc_entry(PROC_ENTRY_DEBUG, proc->parent);
err_del_parent:
remove_proc_entry(PROC_PARENT_DIR, NULL);
return rc;
}
static int proc_term(struct _egalax_i2c *egalax_i2c)
{
struct proc_dir *proc = &egalax_i2c->proc;
#ifdef CONFIG_TOUCHSCREEN_SCALE
remove_proc_entry(PROC_ENTRY_X_SCALE, proc->parent);
remove_proc_entry(PROC_ENTRY_Y_SCALE, proc->parent);
#endif
remove_proc_entry(PROC_ENTRY_WAKEUP, proc->parent);
remove_proc_entry(PROC_ENTRY_CMD, proc->parent);
remove_proc_entry(PROC_ENTRY_DEBUG, proc->parent);
remove_proc_entry(PROC_PARENT_DIR, NULL);
return 0;
}
#if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND)
static int device_suspend(struct i2c_client *client)
{
int ret;
struct _egalax_i2c *egalax_i2c = i2c_get_clientdata(client);
/* already suspended */
if (atomic_read(&egalax_i2c->is_suspended))
return 0;
atomic_set(&egalax_i2c->is_suspended, 1);
/* send the command to put the controller into sleep */
ret = i2c_master_send(client, cmd_str_sleep, MAX_I2C_LEN);
if (ret != MAX_I2C_LEN)
{
TS_ERR("failed to send sleep command ret=%d\n", ret);
ret = -EFAULT;
atomic_set(&egalax_i2c->is_suspended, 0);
return ret;
}
TS_DEBUG("sleep command sent successfully\n");
/* disable interrupt */
disable_irq(client->irq);
/*
* flush the workqueue to make sure all outstanding work items are
* done
*/
flush_workqueue(egalax_i2c->ktouch_wq);
TS_INFO("device suspended\n");
return 0;
}
static int device_resume(struct i2c_client *client)
{
struct _egalax_i2c *egalax_i2c = i2c_get_clientdata(client);
/* already resumed */
if (atomic_read(&egalax_i2c->is_suspended) == 0)
return 0;
enable_irq(client->irq);
wakeup_controller(irq_to_gpio(client->irq));
atomic_set(&egalax_i2c->is_suspended, 0);
TS_INFO("device resumed\n");
return 0;
}
#endif
#ifdef CONFIG_PM
static int egalax_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
{
return device_suspend(client);
}
static int egalax_i2c_resume(struct i2c_client *client)
{
return device_resume(client);
}
#else
#define egalax_i2c_suspend NULL
#define egalax_i2c_resume NULL
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
static void egalax_i2c_early_suspend(struct early_suspend *h)
{
device_suspend(p_egalax_i2c_dev->client);
}
static void egalax_i2c_late_resume(struct early_suspend *h)
{
device_resume(p_egalax_i2c_dev->client);
}
/* we early suspend handler to be called after EARLY_SUSPEND_LEVEL_BLANK_SCREEN
handler is called, so we increase priority by 10 */
static struct early_suspend egalax_i2c_early_suspend_desc = {
.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 10,
.suspend = egalax_i2c_early_suspend,
.resume = egalax_i2c_late_resume,
};
#endif /* CONFIG_HAS_EARLYSUSPEND */
static struct input_dev *allocate_Input_Dev(void)
{
int ret;
struct input_dev *pInputDev = NULL;
pInputDev = input_allocate_device();
if (pInputDev == NULL)
{
TS_ERR("Failed to allocate input device\n");
return NULL;
}
pInputDev->name = "eGalax Touch Screen";
pInputDev->phys = "I2C";
pInputDev->id.bustype = BUS_I2C;
pInputDev->id.vendor = 0x0EEF;
pInputDev->id.product = 0x0020;
pInputDev->id.version = 0x0000;
set_bit(EV_ABS, pInputDev->evbit);
input_set_abs_params(pInputDev, ABS_MT_POSITION_X, 0, 2047, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_POSITION_Y, 0, 2047, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(pInputDev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
ret = input_register_device(pInputDev);
if (ret)
{
TS_ERR("Unable to register input device\n");
input_free_device(pInputDev);
pInputDev = NULL;
return NULL;
}
#ifdef CONFIG_CP_TS_SOFTKEY
softkey_input_dev = input_allocate_device();
if (softkey_input_dev == NULL)
{
TS_ERR("Failed to allocate input device for softkeys\n");
goto input_done;
}
softkey_input_dev->name = "softkey";
softkey_input_dev->phys = "what?";
softkey_input_dev->id.bustype = BUS_I2C;
softkey_input_dev->id.vendor = 0x0001;
softkey_input_dev->id.product = 0x0002;
softkey_input_dev->id.version = 0x0100;
set_bit(EV_KEY, softkey_input_dev->evbit);
__set_bit(KEY_SEARCH, softkey_input_dev->keybit);
__set_bit(KEY_MENU, softkey_input_dev->keybit);
__set_bit(KEY_BACK, softkey_input_dev->keybit);
__set_bit(KEY_HOME, softkey_input_dev->keybit);
ret = input_register_device(softkey_input_dev);
if (ret)
{
TS_ERR("Unable to register input device for soft keys\n");
input_free_device(softkey_input_dev);
softkey_input_dev = NULL;
goto input_done;
}
input_done:
#endif
return pInputDev;
}
static int __devinit egalax_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret, cnt;
u8 x_buf[MAX_I2C_LEN]= {0};
int gpio = irq_to_gpio(client->irq);
struct egalax_i2c_ts_cfg *cfg;
DBG();
if (client->dev.platform_data == NULL)
{
TS_ERR("Missing board dependent confgiurations\n");
ret = -EFAULT;
goto err_exit;
}
cfg = (struct egalax_i2c_ts_cfg *)client->dev.platform_data;
if (cfg->gpio.event < 0)
{
TS_ERR("Invalid GPIO: event=%d\n", cfg->gpio.event);
ret = -EFAULT;
goto err_exit;
}
p_egalax_i2c_dev = (struct _egalax_i2c *)kzalloc(sizeof(struct _egalax_i2c),
GFP_KERNEL);
if (!p_egalax_i2c_dev)
{
TS_ERR("Unable to request memory for device\n");
ret = -ENOMEM;
goto err_exit;
}
/* store hardware/board dependent data */
memcpy(&p_egalax_i2c_dev->hw_cfg, cfg, sizeof(p_egalax_i2c_dev->hw_cfg));
p_egalax_i2c_dev->client = client;
mutex_init(&p_egalax_i2c_dev->mutex_wq);
#if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND)
atomic_set(&p_egalax_i2c_dev->is_suspended, 0);
#endif
p_egalax_i2c_dev->ktouch_wq = create_workqueue("egalax_touch_wq");
if (p_egalax_i2c_dev->ktouch_wq == NULL)
{
TS_ERR("Unable to create workqueue\n");
ret = -ENOMEM;
goto err_free_dev;
}
INIT_WORK(&p_egalax_i2c_dev->work, egalax_i2c_wq);
i2c_set_clientdata(client, p_egalax_i2c_dev);
/* reserve GPIO for touchscreen event interrupt */
ret = gpio_request(cfg->gpio.event, "egalax i2c ts event");
if (ret < 0)
{
TS_ERR("Unable to request gpio=%d\n", cfg->gpio.event);
goto err_del_wq;
}
gpio_direction_input(cfg->gpio.event);
/* reserve GPIO for touchscreen controller reset */
if (cfg->gpio.reset >= 0)
{
ret = gpio_request(cfg->gpio.reset, "egalax i2c ts reset");
if (ret < 0)
{
TS_ERR("Unable to request GPIO pin %d\n", cfg->gpio.reset);
goto err_free_event_gpio;
}
gpio_direction_output(cfg->gpio.reset, 1);
/* now reset the touchscreen controller */
gpio_set_value(cfg->gpio.reset, 1);
msleep(250);
gpio_set_value(cfg->gpio.reset, 0);
msleep(250);
gpio_set_value(cfg->gpio.reset, 1);
}
/*
* initiate an I2C read to put the touchscreen controller into a known
* state
*/
{
int i;
ret = i2c_master_recv(client, x_buf, MAX_I2C_LEN);
if (ret > 0)
{
printk(KERN_INFO "eGalax I2C touchscreen message:\n");
for (i = 0; i < MAX_I2C_LEN; i++)
printk("%2.2x ", x_buf[i]);
printk("\n");
}
else
{
TS_ERR("unable to talk to the touchscreen controller\n");
ret = -ENODEV;
goto err_free_reset_gpio;
}
}
/* reserve the irq line */
ret = request_irq(client->irq, egalax_i2c_interrupt, IRQF_TRIGGER_FALLING,
client->name, p_egalax_i2c_dev);
if (ret)
{
TS_ERR("request_irq(%d) failed\n", client->irq);
goto err_free_reset_gpio;
}
/* drain the FIFO so the INT line can go back to high */
cnt = 0;
while (!gpio_get_value(gpio))
{
ret = i2c_master_recv(client, x_buf, MAX_I2C_LEN);
if (ret < 0)
cnt++;
else
printk(".");
if (cnt >= 3)
break;
}
printk("\n");
ret = proc_init(p_egalax_i2c_dev);
if (ret)
{
TS_ERR("proc fs install failed\n");
goto err_free_irq;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
register_early_suspend(&egalax_i2c_early_suspend_desc);
TS_INFO("register for early suspend\n");
#endif /* CONFIG_HAS_EARLYSUSPEND */
TS_INFO("eGalax I2C touchscreen driver probed\n");
TS_INFO("reset=%d event=%d irq=%d\n", cfg->gpio.reset, cfg->gpio.event,
client->irq);
return 0;
err_free_irq:
free_irq(client->irq, p_egalax_i2c_dev);
err_free_reset_gpio:
if (cfg->gpio.reset >= 0)
gpio_free(cfg->gpio.reset);
err_free_event_gpio:
gpio_free(cfg->gpio.event);
err_del_wq:
i2c_set_clientdata(client, NULL);
if (p_egalax_i2c_dev->ktouch_wq)
{
destroy_workqueue(p_egalax_i2c_dev->ktouch_wq);
}
err_free_dev:
kfree(p_egalax_i2c_dev);
p_egalax_i2c_dev = NULL;
err_exit:
return ret;
}
static int __devexit egalax_i2c_remove(struct i2c_client *client)
{
struct _egalax_i2c *egalax_i2c = i2c_get_clientdata(client);
DBG();
proc_term(egalax_i2c);
if (p_egalax_i2c_dev->ktouch_wq)
{
destroy_workqueue(p_egalax_i2c_dev->ktouch_wq);
}
free_irq(client->irq, egalax_i2c);
if (egalax_i2c->hw_cfg.gpio.reset >= 0)
gpio_free(egalax_i2c->hw_cfg.gpio.reset);
gpio_free(egalax_i2c->hw_cfg.gpio.event);
i2c_set_clientdata(client, NULL);
kfree(egalax_i2c);
p_egalax_i2c_dev = NULL;
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&egalax_i2c_early_suspend_desc);
TS_INFO("unregister for early suspend\n");
#endif /* CONFIG_HAS_EARLYSUSPEND */
return 0;
}
static struct i2c_device_id egalax_i2c_idtable[] = {
{ "egalax_i2c", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, egalax_i2c_idtable);
static struct i2c_driver egalax_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "egalax_i2c",
},
.id_table = egalax_i2c_idtable,
.class = I2C_CLASS_TOUCHSCREEN,
.probe = egalax_i2c_probe,
.remove = __devexit_p(egalax_i2c_remove),
.suspend = egalax_i2c_suspend,
.resume = egalax_i2c_resume,
};
static void egalax_i2c_ts_exit(void)
{
DBG();
i2c_del_driver(&egalax_i2c_driver);
if (input_dev)
{
input_unregister_device(input_dev);
input_free_device(input_dev);
input_dev = NULL;
TS_INFO("Input device unregistered\n");
}
#ifdef CONFIG_CP_TS_SOFTKEY
if (softkey_input_dev)
{
input_unregister_device(softkey_input_dev);
input_free_device(softkey_input_dev);
softkey_input_dev = NULL;
}
#endif
}
static int egalax_i2c_ts_init(void)
{
int result;
DBG();
input_dev = allocate_Input_Dev();
if (input_dev == NULL)
{
TS_ERR("allocate_Input_Dev failed\n");
result = -ENOMEM;
goto fail;
}
TS_INFO("Input device registered\n");
memset(PointBuf, 0, sizeof(struct point_data) * MAX_SUPPORT_POINT);
#ifdef CONFIG_CP_TS_SOFTKEY
memset(BtnState, 0, sizeof(short) * MAX_BTN);
#endif
return i2c_add_driver(&egalax_i2c_driver);
fail:
egalax_i2c_ts_exit();
return result;
}
module_init(egalax_i2c_ts_init);
module_exit(egalax_i2c_ts_exit);
MODULE_DESCRIPTION("egalax touch screen i2c driver");
MODULE_LICENSE("GPL");