/* drivers/input/keyboard/synaptics_i2c_rmi.c
*
* Copyright (C) 2007 Google, Inc.
*
* 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/module.h>;
#include <linux/delay.h>;
#include <linux/earlysuspend.h>;
#include <linux/hrtimer.h>;
#include <linux/i2c.h>;
#include <linux/input.h>;
#include <linux/interrupt.h>;
#include <linux/io.h>;
#include <linux/platform_device.h>;
#include <linux/synaptics_i2c_rmi.h>;
#include <asm/gpio.h>;
#include <plat/gpio-cfg.h>;
static struct workqueue_struct *synaptics_wq;
struct synaptics_ts_data {
uint16_t addr;
struct i2c_client *client;
struct input_dev *input_dev;
int use_irq;
bool has_relative_report;
struct hrtimer timer;
struct work_struct work;
uint16_t max[2];
int snap_state[2][2];
int snap_down_on[2];
int snap_down_off[2];
int snap_up_on[2];
int snap_up_off[2];
int snap_down[2];
int snap_up[2];
uint32_t flags;
int reported_finger_count;
int8_t sensitivity_adjust;
int (*power)(int on);
struct early_suspend early_suspend;
};
#ifdef CONFIG_HAS_EARLYSUSPEND
static void synaptics_ts_early_suspend(struct early_suspend *h);
static void synaptics_ts_late_resume(struct early_suspend *h);
#endif
static int synaptics_init_panel(struct synaptics_ts_data *ts)
{
int ret;
ret = i2c_smbus_write_byte_data(ts->;client, 0x5b, 0x01);
if (ret < 0)
printk(KERN_ERR "i2c_smbus_write_byte_data failed for RESET/n");
return ret;
}
static void synaptics_ts_work_func(struct work_struct *work)
{
int i;
int ret;
int bad_data = 0;
struct i2c_msg msg[2];
uint8_t start_reg;
uint8_t buf[24];
struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work);
int buf_len = 24;
int absmark=0;
int fingermark=0;
long int f1x,f1y,f1wx,f1wy,f1z,f2x,f2y,f2wx,f2wy,f2z;
int ftemp;
buf[0] = i2c_smbus_read_byte_data(ts->;client, 0x12);
buf[1] = i2c_smbus_read_byte_data(ts->;client, 0x13);
buf[2] = i2c_smbus_read_byte_data(ts->;client, 0x14);
buf[3] = i2c_smbus_read_byte_data(ts->;client, 0x15);
buf[4] = i2c_smbus_read_byte_data(ts->;client, 0x16);
buf[5] = i2c_smbus_read_byte_data(ts->;client, 0x17);
buf[6] = i2c_smbus_read_byte_data(ts->;client, 0x18);
buf[7] = i2c_smbus_read_byte_data(ts->;client, 0x19);
buf[8] = i2c_smbus_read_byte_data(ts->;client, 0x1a);
buf[9] = i2c_smbus_read_byte_data(ts->;client, 0x1b);
buf[10] = i2c_smbus_read_byte_data(ts->;client, 0x1c);
buf[11] = i2c_smbus_read_byte_data(ts->;client, 0x1d);
buf[12] = i2c_smbus_read_byte_data(ts->;client, 0x1e);
buf[13] = i2c_smbus_read_byte_data(ts->;client, 0x1f);
printk(KERN_INFO "M8 READ buf: %X %X %X %X %X %X %X %X %X %X %X %X %X %X/n",
buf[0],buf[1],buf[2],buf[3],buf[4],
buf[5],buf[6],buf[7],buf[8],buf[9],
buf[10],buf[11],buf[12],buf[13]);
if(buf[2]==4) absmark=1;
if(buf[3]==0x0a) fingermark=2;
else if(buf[3]==0x01) fingermark=1;
else fingermark=0;
f1x=buf[4];
f1x=f1x<<4;
ftemp=(buf[6]&0x0f);
f1x=f1x+ftemp;
f1y=buf[5];
f1y=f1y<<4;
ftemp=(buf[6]&0xf0)>;>;4;
f1y=f1y+ftemp;
f1wx=(buf[7]&0xf0)>;>;4;
f1wy=(buf[7]&0x0f);
f1z=buf[8];
f2x=buf[9];
f2x=f2x<<4;
ftemp=(buf[11]&0x0f);
f2x=f2x+ftemp;
f2y=buf[10];
f2y=f2y<<4;
ftemp=(buf[11]&0xf0)>;>;4;
f2y=f2y+ftemp;
f2wx=(buf[12]&0xf0)>;>;4;
f2wy=(buf[12]&0x0f);
f2z=buf[13];
ftemp=f1y;
f1y=f1x;
f1x=2872-ftemp;
ftemp=f2y;
f2y=f2x;
f2x=2872-ftemp;
printk(KERN_INFO "f:%d,1x:%ld,1y:%ld,1wx:%ld,1wy:%ld,1z:%ld,2x:%ld,2y:%ld,2wx:%ld,2wy:%ld,2z:%ld/n",fingermark,f1x,f1y,f1wx,f1wy,f1z,f2x,f2y,f2wx,f2wy,f2z);
if(fingermark==2)
{
input_report_key(ts->;input_dev,ABS_MT_TRACKING_ID,0);
input_report_abs(ts->;input_dev, ABS_MT_TOUCH_MAJOR, f1z);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_X, f1x);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_Y, f1y);
input_mt_sync(ts->;input_dev);
input_report_key(ts->;input_dev,ABS_MT_TRACKING_ID,1);
input_report_abs(ts->;input_dev, ABS_MT_TOUCH_MAJOR, f2z);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_X, f2x);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_Y, f2y);
input_mt_sync(ts->;input_dev)
input_sync(ts->;input_dev);
}
else if(fingermark==1)
{
input_report_key(ts->;input_dev,ABS_MT_TRACKING_ID,0);
input_report_abs(ts->;input_dev, ABS_MT_TOUCH_MAJOR, f1z);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_X, f1x);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_Y, f1y);
input_mt_sync(ts->;input_dev);
input_sync(ts->;input_dev);
}
else
{
input_report_abs(ts->;input_dev, ABS_MT_TOUCH_MAJOR, 0);
input_mt_sync(ts->;input_dev);
input_sync(ts->;input_dev);
}
/*if(f1z)
{
input_report_abs(ts->;input_dev, ABS_X, f1x);
input_report_abs(ts->;input_dev, ABS_Y, f1y);
input_report_abs(ts->;input_dev, ABS_PRESSURE, f1z);
if(fingermark==2)
{
input_report_key(ts->;input_dev, BTN_2, 1);
input_sync(ts->;input_dev);
input_report_abs(ts->;input_dev, ABS_HAT0X, f2x);
input_report_abs(ts->;input_dev, ABS_HAT0Y, f2y);
input_report_key(ts->;input_dev, BTN_TOUCH, 1);
}
else if(fingermark==1)
{
input_report_key(ts->;input_dev, BTN_2, 0);
input_report_key(ts->;input_dev, BTN_TOUCH, 1);
}
else
{
input_report_key(ts->;input_dev, BTN_TOUCH, 0);
}
}
else
{
input_report_key(ts->;input_dev, BTN_TOUCH, 0);
input_report_abs(ts->;input_dev, ABS_MT_TOUCH_MAJOR, 0);
input_report_abs(ts->;input_dev, ABS_MT_WIDTH_MAJOR, 0);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_X, f1x);
input_report_abs(ts->;input_dev, ABS_MT_POSITION_Y, f1y);
input_mt_sync(ts->;input_dev);
}
input_sync(ts->;input_dev);*/
if (ts->;use_irq)
enable_irq(ts->;client->;irq);
}
static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer)
{
struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer);
/* printk("synaptics_ts_timer_func/n"); */
queue_work(synaptics_wq, &ts->;work);
hrtimer_start(&ts->;timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id)
{
struct synaptics_ts_data *ts = dev_id;
disable_irq_nosync(ts->;client->;irq);
queue_work(synaptics_wq, &ts->;work);
return IRQ_HANDLED;
}
static int synaptics_ts_probe(
struct i2c_client *client, const struct i2c_device_id *id)
{
struct synaptics_ts_data *ts;
int ret = 0;
unsigned long irqflags;
long int max_x,max_y,max_temp;
if (!i2c_check_functionality(client->;adapter, I2C_FUNC_I2C)) {
printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C/n");
ret = -ENODEV;
goto err_check_functionality_failed;
}
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (ts == NULL) {
ret = -ENOMEM;
goto err_alloc_data_failed;
}
INIT_WORK(&ts->;work, synaptics_ts_work_func);
ts->;client = client;
i2c_set_clientdata(client, ts);
ts->;input_dev = input_allocate_device();
if (ts->;input_dev == NULL) {
ret = -ENOMEM;
printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device/n");
goto err_input_dev_alloc_failed;
}
ts->;input_dev->;name = "synaptics-rmi-touchscreen";
set_bit(EV_SYN, ts->;input_dev->;evbit);
set_bit(EV_KEY, ts->;input_dev->;evbit);
set_bit(BTN_TOUCH, ts->;input_dev->;keybit);
set_bit(BTN_2, ts->;input_dev->;keybit);
set_bit(EV_ABS, ts->;input_dev->;evbit);
/*max_x = i2c_smbus_read_byte_data(ts->;client, 0x28);
max_temp = i2c_smbus_read_byte_data(ts->;client, 0x29);
max_x=max_x+max_temp<<8;
max_y = i2c_smbus_read_byte_data(ts->;client, 0x2a);
max_temp = i2c_smbus_read_byte_data(ts->;client, 0x2b);
max_y=max_y+max_temp<<8;
printk(KERN_INFO "max_x:%ld,max_y:%ld/n",max_x,max_y);*/
max_x=0x77b;
max_y=0xb38;
input_set_abs_params(ts->;input_dev, ABS_X, 0, max_y, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_Y, 0, max_x, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_HAT0X, 0, max_y, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_HAT0Y, 0, max_x, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_MT_POSITION_X, 0, max_y, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_MT_POSITION_Y, 0, max_x, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->;input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
/* ts->;input_dev->;name = ts->;keypad_info->;name; */
ret = input_register_device(ts->;input_dev);
if (ret) {
printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device/n", ts->;input_dev->;name);
goto err_input_register_device_failed;
}
/*add by meizu to support interrupt mode*/
s3c_gpio_cfgpin(S3C64XX_GPN(15),S3C_GPIO_SFN(2));
client->;irq = gpio_to_irq(S3C64XX_GPN(15));
irqflags = IRQF_TRIGGER_LOW;
/*add end*/
if (client->;irq) {
ret = request_irq(client->;irq, synaptics_ts_irq_handler, irqflags, client->;name, ts);
#if 0
if (ret == 0) {
ret = i2c_smbus_write_byte_data(ts->;client, 0xf1, 0x01); /* enable abs int */
if (ret)
free_irq(client->;irq, ts);
}
#endif
if (ret == 0)
ts->;use_irq = 1;
else
dev_err(&client->;dev, "request_irq failed/n");
}
if (!ts->;use_irq) {
hrtimer_init(&ts->;timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->;timer.function = synaptics_ts_timer_func;
hrtimer_start(&ts->;timer, ktime_set(10, 0), HRTIMER_MODE_REL);
}
#ifdef CONFIG_HAS_EARLYSUSPEND
ts->;early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ts->;early_suspend.suspend = synaptics_ts_early_suspend;
ts->;early_suspend.resume = synaptics_ts_late_resume;
register_early_suspend(&ts->;early_suspend);
#endif
printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode/n", ts->;input_dev->;name, ts->;use_irq ? "interrupt" : "polling");
return 0;
err_input_register_device_failed:
input_free_device(ts->;input_dev);
err_input_dev_alloc_failed:
err_detect_failed:
err_power_failed:
kfree(ts);
err_alloc_data_failed:
err_check_functionality_failed:
return ret;
}
static int synaptics_ts_remove(struct i2c_client *client)
{
struct synaptics_ts_data *ts = i2c_get_clientdata(client);
unregister_early_suspend(&ts->;early_suspend);
if (ts->;use_irq)
free_irq(client->;irq, ts);
else
hrtimer_cancel(&ts->;timer);
input_unregister_device(ts->;input_dev);
kfree(ts);
return 0;
}
static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg)
{
int ret;
struct synaptics_ts_data *ts = i2c_get_clientdata(client);
if (ts->;use_irq)
disable_irq(client->;irq);
else
hrtimer_cancel(&ts->;timer);
ret = cancel_work_sync(&ts->;work);
if (ret && ts->;use_irq) /* if work was pending disable-count is now 2 */
enable_irq(client->;irq);
ret = i2c_smbus_write_byte_data(ts->;client, 0xf1, 0); /* disable interrupt */
if (ret < 0)
printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed/n");
ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */
if (ret < 0)
printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed/n");
if (ts->;power) {
ret = ts->;power(0);
if (ret < 0)
printk(KERN_ERR "synaptics_ts_resume power off failed/n");
}
return 0;
}
static int synaptics_ts_resume(struct i2c_client *client)
{
int ret;
struct synaptics_ts_data *ts = i2c_get_clientdata(client);
if (ts->;power) {
ret = ts->;power(1);
if (ret < 0)
printk(KERN_ERR "synaptics_ts_resume power on failed/n");
}
synaptics_init_panel(ts);
if (ts->;use_irq)
enable_irq(client->;irq);
if (!ts->;use_irq)
hrtimer_start(&ts->;timer, ktime_set(1, 0), HRTIMER_MODE_REL);
else
i2c_smbus_write_byte_data(ts->;client, 0xf1, 0x01); /* enable abs int */
return 0;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void synaptics_ts_early_suspend(struct early_suspend *h)
{
struct synaptics_ts_data *ts;
ts = container_of(h, struct synaptics_ts_data, early_suspend);
synaptics_ts_suspend(ts->;client, PMSG_SUSPEND);
}
static void synaptics_ts_late_resume(struct early_suspend *h)
{
struct synaptics_ts_data *ts;
ts = container_of(h, struct synaptics_ts_data, early_suspend);
synaptics_ts_resume(ts->;client);
}
#endif
static const struct i2c_device_id synaptics_ts_id[] = {
{ SYNAPTICS_I2C_RMI_NAME, 0 },
{ }
};
static struct i2c_driver synaptics_ts_driver = {
.probe = synaptics_ts_probe,
.remove = synaptics_ts_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = synaptics_ts_suspend,
.resume = synaptics_ts_resume,
#endif
.id_table = synaptics_ts_id,
.driver = {
.name = SYNAPTICS_I2C_RMI_NAME,
},
};
static int __devinit synaptics_ts_init(void)
{
synaptics_wq = create_singlethread_workqueue("synaptics_wq");
if (!synaptics_wq)
return -ENOMEM;
return i2c_add_driver(&synaptics_ts_driver);
}
static void __exit synaptics_ts_exit(void)
{
i2c_del_driver(&synaptics_ts_driver);
if (synaptics_wq)
destroy_workqueue(synaptics_wq);
}
module_init(synaptics_ts_init);
module_exit(synaptics_ts_exit);
MODULE_DESCRIPTION("Synaptics Touchscreen Driver");
MODULE_LICENSE("GPL");