2010-08-27 11:19:57 +02:00

830 lines
19 KiB
C

/*
* Copyright (C) 2007-2008 HTC Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/input.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/hrtimer.h>
#include <linux/elan_i2c.h>
#include <linux/earlysuspend.h>
#include <linux/gpio.h>
static const char EKT8232NAME[] = "elan-touch";
#define ELAN_TS_FUZZ 0
#define ELAN_TS_FLAT 0
#define IDX_PACKET_SIZE 9
enum {
STATE_DEEP_SLEEP = 0,
STATE_NORMAL = 1U,
STATE_MASK = 0x08,
cmd_reponse_packet = 0x52,
read_cmd_packet = 0x53,
write_cmd_packet = 0x54,
hello_packet = 0x55,
enable_int = 0xa6,
disable_int = 0x56,
idx_coordinate_packet = 0x5a,
};
enum {
idx_finger_width = 7,
idx_finger_state = 8,
};
static struct workqueue_struct *elan_wq;
static struct ekt8232_data {
int intr_gpio;
int use_irq;
/* delete when finish migration */
int fw_ver;
struct hrtimer timer;
struct work_struct work;
struct i2c_client *client;
struct input_dev *input;
wait_queue_head_t wait;
int (*power)(int on);
struct early_suspend early_suspend;
} ekt_data;
#ifdef CONFIG_HAS_EARLYSUSPEND
static void elan_ts_early_suspend(struct early_suspend *h);
static void elan_ts_late_resume(struct early_suspend *h);
#endif
static ssize_t touch_vendor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
sprintf(buf, "%s_%#x\n", EKT8232NAME, ekt_data.fw_ver);
ret = strlen(buf) + 1;
return ret;
}
static DEVICE_ATTR(vendor, 0444, touch_vendor_show, NULL);
static struct kobject *android_touch_kobj;
static int touch_sysfs_init(void)
{
int ret ;
android_touch_kobj = kobject_create_and_add("android_touch", NULL) ;
if (android_touch_kobj == NULL) {
printk(KERN_INFO
"touch_sysfs_init: subsystem_register failed\n");
ret = -ENOMEM;
goto err;
}
ret = sysfs_create_file(android_touch_kobj, &dev_attr_vendor.attr);
if (ret) {
printk(KERN_INFO
"touch_sysfs_init: sysfs_create_group failed\n");
goto err4;
}
return 0 ;
err4:
kobject_del(android_touch_kobj);
err:
return ret ;
}
static int ekt8232_detect_int_level(void)
{
unsigned v;
v = gpio_get_value(ekt_data.intr_gpio);
/* printk("ekt8232_detect_int_level: v = %0x\n", v); */
return v;
}
static int __ekt8232_poll(struct i2c_client *client)
{
int status = 0, retry = 10;
do {
status = ekt8232_detect_int_level();
dev_dbg(&client->dev, "%s: status = %d\n", __func__, status);
retry--;
mdelay(20);
} while (status == 1 && retry > 0);
dev_dbg(&client->dev, "%s: poll interrupt status %s\n",
__func__, status == 1 ? "high" : "low");
return (status == 0 ? 0 : -ETIMEDOUT);
}
static int ekt8232_poll(struct i2c_client *client)
{
return __ekt8232_poll(client);
}
static int ekt8232_get_data(struct i2c_client *client, uint8_t *cmd,
uint8_t *buf, size_t size, int sleep)
{
int rc;
unsigned time_out = msecs_to_jiffies(10);
dev_dbg(&client->dev, "%s: enter.\n", __func__);
if (buf == NULL)
return -EINVAL;
if ((i2c_master_send(client, cmd, 4)) != 4) {
dev_err(&client->dev,
"%s: i2c_master_send failed\n", __func__);
return -EINVAL;
}
if (sleep == 1) {
rc = wait_event_timeout(ekt_data.wait,
i2c_master_recv(client, buf, size) == size &&
buf[0] == cmd_reponse_packet, time_out);
if (rc == 0) {
dev_err(&client->dev,
"%s: i2c_master_recv failed\n", __func__);
return -ETIMEDOUT;
}
} else {
rc = ekt8232_poll(client);
if (rc < 0)
return -EINVAL;
else {
if (i2c_master_recv(client, buf, size) != size ||
buf[0] != cmd_reponse_packet)
return -EINVAL;
}
}
return 0;
}
static int __hello_packet_handler(struct i2c_client *client)
{
int rc;
uint8_t buf_recv[4] = { 0 };
rc = ekt8232_poll(client);
if (rc < 0) {
dev_err(&client->dev, "%s: failed!\n", __func__);
return -EINVAL;
}
rc = i2c_master_recv(client, buf_recv, 4);
if (rc != 4) {
dev_err(&client->dev,
"%s: get hello packet failed!, rc = %d\n",
__func__, rc);
return rc;
} else {
int i;
dev_dbg(&client->dev,
"dump hello packet: %0x, %0x, %0x, %0x\n",
buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]);
for (i = 0; i < 4; i++)
if (buf_recv[i] != hello_packet)
return -EINVAL;
}
return 0;
}
static int __fw_packet_handler(struct i2c_client *client)
{
int rc;
int major, minor;
uint8_t cmd[] = { read_cmd_packet, 0x00, 0x00, 0x01 };
uint8_t buf_recv[4] = { 0 };
rc = ekt8232_get_data(client, cmd, buf_recv, 4, 0);
if (rc < 0)
return rc;
major = ((buf_recv[1] & 0x0f) << 4) | ((buf_recv[2] & 0xf0) >> 4);
minor = ((buf_recv[2] & 0x0f) << 4) | ((buf_recv[3] & 0xf0) >> 4);
/* delete after migration */
ekt_data.fw_ver = major << 8 | minor;
printk(KERN_INFO "%s: firmware version: 0x%x\n",
__func__, ekt_data.fw_ver);
return 0;
}
static int __set_report_type(struct i2c_client *client)
{
return 0;
}
static inline int ekt8232_parse_xy(uint8_t *data, uint16_t *x, uint16_t *y)
{
*x = (data[0] & 0xf0);
*x <<= 4;
*x |= data[1];
*y = (data[0] & 0x0f);
*y <<= 8;
*y |= data[2];
return 0;
}
/* ekt8232_ts_init -- hand shaking with touch panel
*
* 1. recv hello packet
* 2. check its' firmware version
* 3. set up sensitivity, report rate, ...
*/
static int ekt8232_ts_init(struct i2c_client *client)
{
int rc;
rc = __hello_packet_handler(client);
if (rc < 0)
goto hand_shake_failed;
dev_dbg(&client->dev, "%s: hello packet got.\n", __func__);
rc = __fw_packet_handler(client);
if (rc < 0)
goto hand_shake_failed;
dev_dbg(&client->dev, "%s: firmware checking done.\n", __func__);
rc = __set_report_type(client);
if (rc < 0)
goto hand_shake_failed;
dev_dbg(&client->dev,
"%s: channging operating mode done.\n", __func__);
if (ekt_data.fw_ver == 0x103) {
uint8_t cmd[4] = {0x5c, 0x10, 0x00, 0x01};
if ((i2c_master_send(client, cmd, 4)) != 4) {
dev_err(&client->dev,
"%s: set adc failed\n", __func__);
}
cmd[0] = 0x54;
cmd[0] = 0x43;
cmd[0] = 0x00;
cmd[0] = 0x01;
if ((i2c_master_send(client, cmd, 4)) != 4) {
dev_err(&client->dev,
"%s: set gain failed\n", __func__);
}
}
hand_shake_failed:
return rc;
}
static int ekt8232_set_power_state(struct i2c_client *client, int state)
{
uint8_t cmd[] = {write_cmd_packet, 0x50, 0x00, 0x01};
dev_dbg(&client->dev, "%s: enter.\n", __func__);
cmd[1] |= (state << 3);
dev_dbg(&client->dev,
"dump cmd: %02x, %02x, %02x, %02x\n",
cmd[0], cmd[1], cmd[2], cmd[3]);
if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd)) {
dev_err(&client->dev,
"%s: i2c_master_send failed\n", __func__);
return -EINVAL;
}
return 0;
}
static int ekt8232_get_power_state(struct i2c_client *client)
{
int rc = 0;
uint8_t cmd[] = { read_cmd_packet, 0x50, 0x00, 0x01 };
uint8_t buf[4], power_state;
rc = ekt8232_get_data(client, cmd, buf, 4, 0);
if (rc)
return rc;
else {
power_state = buf[1];
dev_dbg(&client->dev, "dump repsponse: %0x\n", power_state);
power_state = (power_state & STATE_MASK) >> 3;
dev_dbg(&client->dev, "power state = %s\n",
power_state == STATE_DEEP_SLEEP ?
"Deep Sleep" : "Normal/Idle");
return power_state;
}
}
static int ekt8232_recv_data(struct i2c_client *client, uint8_t *buf)
{
int rc, bytes_to_recv = IDX_PACKET_SIZE;
int retry = 5;
if (ekt_data.fw_ver == 0x101)
bytes_to_recv = 8;
if (buf == NULL)
return -EINVAL;
memset(buf, 0, bytes_to_recv);
rc = i2c_master_recv(client, buf, bytes_to_recv);
if (rc != bytes_to_recv) {
dev_err(&client->dev,
"%s: i2c_master_recv error?! \n", __func__);
/* power off level shift */
ekt_data.power(0);
msleep(5);
/* power on level shift */
ekt_data.power(1);
/* re-initial */
if (ekt_data.fw_ver > 0x101) {
msleep(100);
rc = ekt8232_ts_init(client);
} else {
do {
rc = ekt8232_set_power_state(client,
STATE_NORMAL);
rc = ekt8232_get_power_state(client);
if (rc != STATE_NORMAL)
dev_err(&client->dev,
"%s: wake up tp failed! \
err = %d\n",
__func__, rc);
else
break;
} while (--retry);
}
if (ekt8232_detect_int_level() == 0)
queue_work(elan_wq, &ekt_data.work);
return -EINVAL;
}
return rc;
}
static inline void ekt8232_parse_width(uint8_t data, uint8_t *w1, uint8_t *w2)
{
*w1 = *w2 = 0;
*w1 = (data & 0xf0) >> 4;
*w2 = data & 0x0f;
}
static void ekt8232_report_data(struct i2c_client *client, uint8_t *buf)
{
static unsigned report_time;
unsigned report_time2;
switch (buf[0]) {
case idx_coordinate_packet: {
uint16_t x1, x2, y1, y2;
uint8_t finger_stat, w1 = 1, w2 = 1;
ekt8232_parse_xy(&buf[1], &x1, &y1);
if (ekt_data.fw_ver == 0x101) {
finger_stat = buf[7] >> 1;
} else {
ekt8232_parse_width(buf[idx_finger_width], &w1, &w2);
finger_stat = buf[idx_finger_state] >> 1;
}
if (finger_stat != 0) {
input_report_abs(ekt_data.input, ABS_X, x1);
if (ekt_data.fw_ver == 0x101)
input_report_abs(ekt_data.input, ABS_Y,
(544 - 1) - y1);
else
input_report_abs(ekt_data.input, ABS_Y, y1);
/* only report finger width at y */
input_report_abs(ekt_data.input, ABS_TOOL_WIDTH, w2);
}
dev_dbg(&client->dev,
"x1 = %d, y1 = %d, \
w1 = %d, w2 = %d, finger status = %d\n",
x1, y1, w1, w2, finger_stat);
input_report_abs(ekt_data.input, ABS_PRESSURE, 100);
input_report_key(ekt_data.input, BTN_TOUCH, finger_stat);
input_report_key(ekt_data.input, BTN_2, finger_stat == 2);
if (finger_stat > 1) {
ekt8232_parse_xy(&buf[4], &x2, &y2);
dev_dbg(&client->dev, "x2 = %d, y2 = %d\n", x2, y2);
input_report_abs(ekt_data.input, ABS_HAT0X, x2);
input_report_abs(ekt_data.input, ABS_HAT0Y, y2);
}
input_sync(ekt_data.input);
break;
}
default:
dev_err(&client->dev,
"%s: Unknown packet type: %0x\n", __func__, buf[0]);
break;
}
report_time2 = jiffies;
dev_dbg(&client->dev,
"report time = %d\n",
jiffies_to_msecs(report_time2 - report_time));
report_time = report_time2;
}
static void ekt8232_work_func(struct work_struct *work)
{
int rc;
uint8_t buf[IDX_PACKET_SIZE] = { 0 };
struct i2c_client *client = ekt_data.client;
/* dev_dbg(&client->dev, "%s: enter. \n", __func__); */
/* this means that we have already serviced it */
if (ekt8232_detect_int_level())
return;
rc = ekt8232_recv_data(client, buf);
if (rc < 0)
return;
ekt8232_report_data(client, buf);
}
static irqreturn_t ekt8232_ts_interrupt(int irq, void *dev_id)
{
/* the queue_work has spin_lock protection */
/* disable_irq(irq); */
queue_work(elan_wq, &ekt_data.work);
return IRQ_HANDLED;
}
static enum hrtimer_restart ekt8232_ts_timer_func(struct hrtimer *timer)
{
queue_work(elan_wq, &ekt_data.work);
hrtimer_start(&ekt_data.timer,
ktime_set(0, 12500000),
HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
static int ekt8232_register_interrupt(struct i2c_client *client)
{
int err = 0;
if (client->irq) {
ekt_data.use_irq = 1;
err = request_irq(client->irq, ekt8232_ts_interrupt, 0,
EKT8232NAME, &ekt_data);
if (err < 0) {
dev_err(&client->dev,
"%s(%s): Can't allocate irq %d\n",
__FILE__, __func__, client->irq);
ekt_data.use_irq = 0;
}
}
if (!ekt_data.use_irq) {
hrtimer_init(&ekt_data.timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ekt_data.timer.function = ekt8232_ts_timer_func;
hrtimer_start(&ekt_data.timer, ktime_set(1, 0),
HRTIMER_MODE_REL);
}
dev_dbg(&client->dev,
"elan starts in %s mode.\n",
ekt_data.use_irq == 1 ? "interrupt":"polling");
return 0;
}
static int ekt8232_probe(
struct i2c_client *client, const struct i2c_device_id *id)
{
int err = 0;
struct elan_i2c_platform_data *pdata;
int x_max, y_max;
uint8_t x_resolution_cmd[] = { read_cmd_packet, 0x60, 0x00, 0x01 };
uint8_t y_resolution_cmd[] = { read_cmd_packet, 0x63, 0x00, 0x01 };
uint8_t buf_recv[4] = { 0 };
elan_wq = create_singlethread_workqueue("elan_wq");
if (!elan_wq) {
err = -ENOMEM;
goto fail;
}
printk(KERN_INFO "ekt8232_probe enter.\n");
dev_dbg(&client->dev, "ekt8232_probe enter.\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev,
"No supported i2c func what we need?!!\n");
err = -ENOTSUPP;
goto fail;
}
ekt_data.client = client;
strlcpy(client->name, EKT8232NAME, I2C_NAME_SIZE);
i2c_set_clientdata(client, &ekt_data);
INIT_WORK(&ekt_data.work, ekt8232_work_func);
init_waitqueue_head(&ekt_data.wait);
ekt_data.input = input_allocate_device();
if (ekt_data.input == NULL) {
err = -ENOMEM;
goto fail;
}
pdata = client->dev.platform_data;
if (likely(pdata != NULL)) {
ekt_data.intr_gpio =
((struct elan_i2c_platform_data *)pdata)->intr_gpio;
ekt_data.power =
((struct elan_i2c_platform_data *)pdata)->power;
ekt_data.power(1);
dev_info(&client->dev, "touch panel is powered on. \n");
mdelay(500); /* elan will be ready after about 500 ms */
} else {
dev_err(&client->dev, "without platform data??!!\n");
}
err = ekt8232_ts_init(client);
if (err < 0) {
printk(KERN_INFO "looks like it's not Elan, so..i'll quit\n");
err = -ENODEV;
goto fail;
}
if (pdata) {
while (pdata->version > ekt_data.fw_ver) {
printk(KERN_INFO "ekt8232_probe: old tp detected, "
"panel version = 0x%x\n",
ekt_data.fw_ver);
pdata++;
}
}
printk(KERN_INFO "ekt8232_register_input\n");
ekt_data.input->name = EKT8232NAME;
ekt_data.input->id.bustype = BUS_I2C;
set_bit(EV_SYN, ekt_data.input->evbit);
set_bit(EV_KEY, ekt_data.input->evbit);
set_bit(BTN_TOUCH, ekt_data.input->keybit);
set_bit(BTN_2, ekt_data.input->keybit);
set_bit(EV_ABS, ekt_data.input->evbit);
if (ekt_data.fw_ver >= 0x104) {
err = ekt8232_get_data(ekt_data.client, x_resolution_cmd,
buf_recv, 4, 0);
if (err < 0) {
dev_err(&client->dev,
"%s: get x resolution failed, err = %d\n",
__func__, err);
goto fail;
}
x_max = ((buf_recv[3] & 0xf0) << 4) | ((buf_recv[2] & 0xff));
printk(KERN_INFO "ekt8232_probe: x_max: %d\n", x_max);
err = ekt8232_get_data(ekt_data.client, y_resolution_cmd,
buf_recv, 4, 0);
if (err < 0) {
dev_err(&client->dev,
"%s: get y resolution failed, err = %d\n",
__func__, err);
goto fail;
}
y_max = ((buf_recv[3] & 0xf0) << 4) | ((buf_recv[2] & 0xff));
printk(KERN_INFO "ekt8232_probe: y_max: %d\n", y_max);
input_set_abs_params(ekt_data.input, ABS_X,
pdata->abs_x_min, x_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_Y,
pdata->abs_y_min, y_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_HAT0X,
pdata->abs_x_min, x_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_HAT0Y,
pdata->abs_y_min, y_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
} else {
input_set_abs_params(ekt_data.input, ABS_X,
pdata->abs_x_min, pdata->abs_x_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_Y,
pdata->abs_y_min, pdata->abs_y_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_HAT0X,
pdata->abs_x_min, pdata->abs_x_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_HAT0Y,
pdata->abs_y_min, pdata->abs_y_max,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_PRESSURE, 0, 255,
ELAN_TS_FUZZ, ELAN_TS_FLAT);
input_set_abs_params(ekt_data.input, ABS_TOOL_WIDTH, 1, 8,
1, ELAN_TS_FLAT);
}
err = input_register_device(ekt_data.input);
if (err < 0) {
dev_err(&client->dev,
"%s: input_register_device failed, err = %d\n",
__func__, err);
goto fail;
}
ekt8232_register_interrupt(ekt_data.client);
/* checking the interrupt to avoid missing any interrupt */
if (ekt8232_detect_int_level() == 0)
ekt8232_ts_interrupt(client->irq, NULL);
#ifdef CONFIG_HAS_EARLYSUSPEND
ekt_data.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ekt_data.early_suspend.suspend = elan_ts_early_suspend;
ekt_data.early_suspend.resume = elan_ts_late_resume;
register_early_suspend(&ekt_data.early_suspend);
#endif
touch_sysfs_init();
return 0;
fail:
input_free_device(ekt_data.input);
if (elan_wq)
destroy_workqueue(elan_wq);
return err;
}
static int ekt8232_remove(struct i2c_client *client)
{
struct ekt8232_data *tp = i2c_get_clientdata(client);
if (elan_wq)
destroy_workqueue(elan_wq);
dev_dbg(&client->dev, "%s: enter.\n", __func__);
input_unregister_device(tp->input);
if (ekt_data.use_irq)
free_irq(client->irq, tp);
else
hrtimer_cancel(&ekt_data.timer);
return 0;
}
static int ekt8232_suspend(struct i2c_client *client, pm_message_t mesg)
{
uint8_t cmd[4];
int rc = 0;
dev_dbg(&client->dev, "%s: enter. irq = %d\n", __func__, client->irq);
cancel_work_sync(&ekt_data.work);
rc = ekt8232_set_power_state(client, STATE_DEEP_SLEEP);
/*
rc = ekt8232_get_power_state(client);
if (rc < 0 || rc != STATE_DEEP_SLEEP)
dev_err(&client->dev,
"%s: put tp into sleep failed, err = %d!\n",
__func__, rc);
*/
/* disable tp interrupt */
if (ekt_data.fw_ver > 0x101) {
memset(cmd, disable_int, 4);
if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd))
dev_err(&client->dev,
"%s: tp disable interrupt failed\n", __func__);
}
/* power off level shift */
ekt_data.power(0);
return 0;
}
static int ekt8232_resume(struct i2c_client *client)
{
int rc = 0, retry = 5;
dev_dbg(&client->dev,
"%s: enter. irq = %d\n", __func__, client->irq);
disable_irq(client->irq);
/* power on level shift */
ekt_data.power(1);
/* re-initial */
if (ekt_data.fw_ver > 0x101) {
msleep(500);
rc = ekt8232_ts_init(client);
} else {
do {
rc = ekt8232_set_power_state(client, STATE_NORMAL);
rc = ekt8232_get_power_state(client);
if (rc != STATE_NORMAL)
dev_err(&client->dev,
"%s: wake up tp failed! err = %d\n",
__func__, rc);
else
break;
} while (--retry);
}
enable_irq(client->irq);
if (ekt8232_detect_int_level() == 0)
ekt8232_ts_interrupt(client->irq, NULL);
return 0;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void elan_ts_early_suspend(struct early_suspend *h)
{
struct i2c_client *client = ekt_data.client;
dev_dbg(&client->dev, "%s enter.\n", __func__);
ekt8232_suspend(client, PMSG_SUSPEND);
}
static void elan_ts_late_resume(struct early_suspend *h)
{
struct i2c_client *client = ekt_data.client;
dev_dbg(&client->dev, "%s enter.\n", __func__);
ekt8232_resume(client);
}
#endif
/* -------------------------------------------------------------------- */
static const struct i2c_device_id ekt8232_ts_id[] = {
{ ELAN_8232_I2C_NAME, 0 },
{ }
};
static struct i2c_driver ekt8232_driver = {
.probe = ekt8232_probe,
.remove = ekt8232_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = ekt8232_suspend,
.resume = ekt8232_resume,
#endif
.id_table = ekt8232_ts_id,
.driver = {
.name = ELAN_8232_I2C_NAME,
},
};
static int __init ekt8232_init(void)
{
return i2c_add_driver(&ekt8232_driver);
}
static void __exit ekt8232_exit(void)
{
i2c_del_driver(&ekt8232_driver);
}
module_init(ekt8232_init);
module_exit(ekt8232_exit);
MODULE_AUTHOR("Shan-Fu Chiou <sfchiou@gmail.com>, "
"Jay Tu <jay_tu@htc.com>");
MODULE_DESCRIPTION("ELAN ekt8232 driver");
MODULE_LICENSE("GPL");