/* drivers/input/touchscreen/touchscreen.c | |
* | |
* TouchScreen driver. | |
* | |
* Copyright (c) | |
* | |
* 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/i2c.h> | |
#include <linux/input.h> | |
#include <linux/delay.h> | |
#include <linux/slab.h> | |
#include <linux/interrupt.h> | |
#include <mach/irqs.h> | |
#include <linux/kernel.h> | |
#include <linux/semaphore.h> | |
#include <linux/mutex.h> | |
#include <linux/module.h> | |
#include <linux/gpio.h> | |
#include <linux/syscalls.h> | |
#include <linux/unistd.h> | |
#include <linux/uaccess.h> | |
#include <linux/fs.h> | |
#include <linux/string.h> | |
#include <linux/timer.h> | |
#include <linux/of.h> | |
#include <linux/of_gpio.h> | |
#include <linux/irq.h> | |
#include <mach/gpio_cfg.h> | |
#include <linux/input/touchscreen.h> | |
static struct mutex i2c_rw_access; | |
static u8 s_pmode = 0; | |
static struct input_dev *tp_input_dev; | |
static struct touchscreen_operations *ts_operations[MAX_TOUCHSCREEN_DEV_TYPE] = { NULL }; | |
static struct touchscreen_register *touchscreen_dev_register_tbl[MAX_TOUCHSCREEN_DEV_TYPE] = { NULL }; | |
static struct ts_data *s_touchscreen_data = NULL; | |
enum touchscreen_type touchscreen_detected_type = MAX_TOUCHSCREEN_DEV_TYPE; | |
void touchscreen_set_operations(enum touchscreen_type id, struct touchscreen_operations *pt_operations) | |
{ | |
ts_operations[id] = pt_operations; | |
} | |
void touchscreen_register(enum touchscreen_type id, struct touchscreen_register *pt_register) | |
{ | |
touchscreen_dev_register_tbl[id] = pt_register; | |
} | |
int tp_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen) | |
{ | |
int ret = 0; | |
if (client == NULL) | |
{ | |
dev_err(&client->dev, "[IIC][%s]i2c_client==NULL!\n", __func__); | |
return -1; | |
} | |
mutex_lock(&i2c_rw_access); | |
if (readlen > 0) | |
{ | |
if (writelen > 0) | |
{ | |
struct i2c_msg msgs[] = | |
{ | |
{ | |
.addr = client->addr, | |
.flags = 0, | |
.len = writelen, | |
.buf = writebuf, | |
}, | |
{ | |
.addr = client->addr, | |
.flags = I2C_M_RD, | |
.len = readlen, | |
.buf = readbuf, | |
}, | |
}; | |
ret = i2c_transfer(client->adapter, msgs, 2); | |
if (ret < 0) | |
{ | |
dev_err(&client->dev, "%s [IIC]: i2c_transfer(write) error, ret=%d!!\n", __func__,ret); | |
} | |
} | |
else | |
{ | |
struct i2c_msg msgs[] = | |
{ | |
{ | |
.addr = client->addr, | |
.flags = I2C_M_RD, | |
.len = readlen, | |
.buf = readbuf, | |
}, | |
}; | |
ret = i2c_transfer(client->adapter, msgs, 1); | |
if (ret < 0) | |
{ | |
dev_err(&client->dev, "%s IIC]: i2c_transfer(read) error, ret=%d!!\n", __func__,ret); | |
} | |
} | |
} | |
mutex_unlock(&i2c_rw_access); | |
return ret; | |
} | |
int tp_i2c_write(struct i2c_client *client, char *writebuf, int writelen) | |
{ | |
int ret = 0; | |
if (client == NULL) | |
{ | |
dev_err(&client->dev, "[IIC][%s]i2c_client==NULL!\n", __func__); | |
return -1; | |
} | |
mutex_lock(&i2c_rw_access); | |
if (writelen > 0) | |
{ | |
struct i2c_msg msgs[] = | |
{ | |
{ | |
.addr = client->addr, | |
.flags = 0, | |
.len = writelen, | |
.buf = writebuf, | |
}, | |
}; | |
ret = i2c_transfer(client->adapter, msgs, 1); | |
if (ret < 0) | |
{ | |
dev_err(&client->dev, "%s [IIC]: i2c_transfer(write) error, ret=%d!!.\n", __func__,ret); | |
} | |
} | |
mutex_unlock(&i2c_rw_access); | |
return ret; | |
} | |
int tp_read(struct i2c_client *client, u8 regaddr, u8 len, u8 *regvalue) //len:1 hyn_i2c_read_reg | |
{ | |
return tp_i2c_read(client, ®addr, 1, regvalue, len); | |
} | |
int tp_write(struct i2c_client *client, u8 regaddr, u8 regvalue) //len: 2( any value) hyn_i2c_write_reg | |
{ | |
u8 buf[2] = {0}; | |
buf[0] = regaddr; | |
buf[1] = regvalue; | |
return tp_i2c_write(client, buf, sizeof(buf)); | |
} | |
static int tp_read_touchdata(struct ts_data *data) | |
{ | |
int ret = -1; | |
if (NULL == ts_operations[touchscreen_detected_type]) { | |
return -1; | |
} | |
if (NULL == ts_operations[touchscreen_detected_type]->ts_read_touchdata) { | |
return -1; | |
} | |
ret = ts_operations[touchscreen_detected_type]->ts_read_touchdata(data); | |
if (ret >= 0) | |
{ | |
return 0; | |
} | |
return ret; | |
} | |
static void tp_report_values(struct ts_data *data) | |
{ | |
struct ts_event *event = data->events; | |
if (data->touch_points > 0) { | |
input_report_abs(data->input_dev, ABS_X, event[0].x); | |
input_report_abs(data->input_dev, ABS_Y, event[0].y); | |
input_report_key(data->input_dev, BTN_TOUCH, 1); | |
input_sync(data->input_dev); | |
} else if (data->touch_points == 0) { | |
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0); | |
input_report_key(data->input_dev, BTN_TOUCH, 0); /*Up*/ | |
input_sync(data->input_dev); | |
} | |
} | |
static irqreturn_t tp_ts_interrupt(int irq, void *dev_id) | |
{ | |
struct ts_data *touchscreen_data = dev_id; | |
int ret = 0; | |
//printk("[TP]tp_ts_interrupt happen \n"); | |
ret = tp_read_touchdata(touchscreen_data); | |
if (ret == 0) | |
tp_report_values(touchscreen_data); | |
return IRQ_HANDLED; | |
} | |
#ifdef CONFIG_SYSFS | |
static ssize_t tp_sysfs_show(struct device *dev, | |
struct device_attribute *attr, char *buf); | |
static ssize_t tp_sysfs_store(struct device *dev, | |
struct device_attribute *attr, const char *buf, size_t count); | |
DEVICE_ATTR(tp_mode, S_IWUSR | S_IRUGO, tp_sysfs_show, tp_sysfs_store); | |
static struct attribute * tp_sysfs_attrs[] = { | |
&dev_attr_tp_mode.attr, | |
NULL | |
}; | |
static const struct attribute_group tp_sysfs_attr_group = { | |
.attrs = tp_sysfs_attrs, | |
}; | |
static int tp_sysfs_create_group(struct ts_data *touchscreen_data) | |
{ | |
return sysfs_create_group(&touchscreen_data->input_dev->dev.kobj, &tp_sysfs_attr_group); | |
} | |
static void tp_sysfs_remove_group(struct ts_data *touchscreen_data) | |
{ | |
sysfs_remove_group(&touchscreen_data->client->dev.kobj, &tp_sysfs_attr_group); | |
} | |
static ssize_t tp_sysfs_show(struct device *dev, | |
struct device_attribute *attr, char *buf) | |
{ | |
u8 val = 0; | |
int ret = 0; | |
ret = tp_read(s_touchscreen_data->client, TP_REG_ID_PMODE, 1, &val); | |
if(ret < 0) | |
val = 0xff; | |
return scnprintf(buf, PAGE_SIZE, "%d",val); | |
} | |
static ssize_t tp_sysfs_store(struct device *dev, | |
struct device_attribute *attr, const char *buf, size_t count) | |
{ | |
u8 pmode_sleep = 0x03; | |
unsigned long pmode = 0; | |
u8 read_val = 0xff; | |
if(strict_strtoul(buf, 0, &pmode)) | |
return -EINVAL; | |
if (pmode) { | |
if(TP_SLEEP_OFF == s_touchscreen_data->pmode) | |
return count; | |
gpio_set_value(PIN_TP_RST, 0); | |
msleep(20); | |
gpio_set_value(PIN_TP_RST, 1); | |
msleep(200); | |
s_touchscreen_data->pmode = TP_SLEEP_OFF; | |
//tp_write(s_touchscreen_data->client, TP_REG_ID_PMODE, &s_pmode); | |
//printk("tp_sysfs_store sleep out read_val = %d, pmode = %d ,s_pmode = %d\n",read_val,pmode,s_pmode); | |
} else { | |
if(TP_SLEEP_ON == s_touchscreen_data->pmode) | |
return count; | |
tp_write(s_touchscreen_data->client, TP_REG_ID_PMODE, pmode_sleep); | |
s_touchscreen_data->pmode = TP_SLEEP_ON; | |
//printk("tp_sysfs_store sleep in read_val = %d, pmode = %d ,s_pmode = %d\n",read_val,pmode,s_pmode); | |
} | |
return count; | |
} | |
#else | |
static int tp_sysfs_create_group(struct ts_data *touchscreen_data) | |
{ | |
return 0; | |
} | |
static void tp_sysfs_remove_group(struct ts_data *touchscreen_data) | |
{ | |
return; | |
} | |
#endif | |
static int ts_init_gpio(void) | |
{ | |
int err; | |
/* ÅäÖô¥ÃþÆÁʹÓõÄirq reset GPIO Òý½Å*/ | |
err = gpio_request(PIN_TP_IRQ, "touchscreen_irq"); | |
if (err) { | |
printk( "[TP]request touchscreen irq gpio failed\n"); | |
return -1; | |
} | |
zx29_gpio_config(PIN_TP_IRQ, TP_IRQ_FUN_SEL); | |
zx29_gpio_set_inttype(PIN_TP_IRQ, IRQ_TYPE_EDGE_FALLING); | |
zx29_gpio_pd_pu_set(PIN_TP_IRQ, IO_CFG_PULL_DISABLE); | |
err = gpio_request(PIN_TP_RST, "touchscreen_reset"); | |
if (err) { | |
printk( "[TP]request touchscreen reset gpio failed\n"); | |
return -1; | |
} | |
zx29_gpio_config(PIN_TP_RST, TP_RST_GPIO_SEL); | |
gpio_direction_output(PIN_TP_RST, GPIO_OUT); | |
gpio_set_value(PIN_TP_RST, 0); | |
msleep(20); | |
gpio_set_value(PIN_TP_RST, 1); | |
msleep(200); | |
return 0; | |
} | |
static int touchscreen_dev_detect(struct i2c_client *client) | |
{ | |
int ret = 0; | |
u32 dev_index = 0; | |
ret = ts_init_gpio(); | |
if(ret < 0) | |
{ | |
return -1; | |
} | |
for (dev_index =0; dev_index < MAX_TOUCHSCREEN_DEV_TYPE; dev_index++) { | |
if (touchscreen_detected_type != MAX_TOUCHSCREEN_DEV_TYPE) { | |
printk(KERN_ERR "[TP]touchscreen_dev_detect:touchscreen_type=%d have been detected!\n",touchscreen_detected_type); | |
return 0; | |
} | |
if ( NULL == touchscreen_dev_register_tbl[dev_index]) { | |
printk(KERN_ERR "[TP]touchscreen_dev_detect:touchscreen_type=%d have been detected!\n",touchscreen_detected_type); | |
return -1; | |
} | |
if (touchscreen_dev_register_tbl[dev_index]->ts_id_detect != NULL) { | |
ret = touchscreen_dev_register_tbl[dev_index]->ts_id_detect(client); | |
printk("[TP]touchscreen_dev_detect dev_index = 0x%x, ret = 0x%x\n",dev_index,ret); | |
if (ret != 0) { | |
} | |
else{ | |
break; | |
} | |
} | |
} | |
if (touchscreen_detected_type == MAX_TOUCHSCREEN_DEV_TYPE) { | |
printk(KERN_ERR "[TP]touchscreen_dev_detect:do not have the driver for this touchscreen type!\n"); | |
return -ENXIO; | |
} | |
return 0; | |
} | |
static int ts_probe(struct i2c_client *client, | |
const struct i2c_device_id *id) | |
{ | |
struct ts_data *touchscreen_data; | |
struct input_dev *input_dev; | |
int err = 0; | |
int ret = -1; | |
mutex_init(&i2c_rw_access); | |
ret = touchscreen_dev_detect(client); | |
if(ret < 0) | |
{ | |
dev_err(&client->dev, "[TP]%s: touchscreen_dev_detect irq failed\n", __func__); | |
return -1; | |
} | |
touchscreen_data = kzalloc(sizeof(struct ts_data), GFP_KERNEL); | |
s_touchscreen_data = touchscreen_data; | |
if (!touchscreen_data) { | |
err = -ENOMEM; | |
goto exit_alloc_data_failed; | |
} | |
i2c_set_clientdata(client, touchscreen_data); | |
touchscreen_data->client = client; | |
touchscreen_data->x_max = 4095; | |
touchscreen_data->y_max = 4095; | |
/* ×¢²á¶ÁÈ¡ÖÐ¶Ï */ | |
err = devm_request_threaded_irq(&client->dev, gpio_to_irq(PIN_TP_IRQ), | |
NULL, tp_ts_interrupt, IRQF_ONESHOT, | |
client->dev.driver->name, touchscreen_data); | |
if (err < 0) { | |
dev_err(&client->dev, "[TP]%s: request irq failed\n", __func__); | |
goto exit_irq_request_failed; | |
} | |
/* ·ÖÅäÒ»¸öʼþÉϱ¨½á¹¹Ìå */ | |
input_dev = input_allocate_device(); | |
if (!input_dev) { | |
err = -ENOMEM; | |
dev_err(&client->dev, "[TP]failed to allocate input device\n"); | |
goto exit_input_dev_alloc_failed; | |
} | |
touchscreen_data->input_dev = input_dev; | |
/* ×¢²áÉ豸֧³ÖµÄevent ÀàÐÍ */ | |
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | |
set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit); | |
set_bit(ABS_MT_POSITION_X, input_dev->absbit); | |
set_bit(ABS_MT_POSITION_Y, input_dev->absbit); | |
set_bit(ABS_MT_PRESSURE, input_dev->absbit); | |
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, touchscreen_data->x_max, 0, 0); | |
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, touchscreen_data->y_max, 0, 0); | |
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0); | |
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, PRESS_MAX, 0, 0); | |
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, CFG_MAX_TOUCH_POINTS, 0, 0); | |
input_set_abs_params(input_dev, ABS_X, 0, touchscreen_data->x_max, 0, 0); | |
input_set_abs_params(input_dev, ABS_Y, 0, touchscreen_data->y_max, 0, 0); | |
input_set_abs_params(input_dev, ABS_PRESSURE, 0, PRESS_MAX, 0, 0); | |
/* EV KEY °´¼üʼþ EV_ABS ¾ø¶Ô×ø±ê£¬Èç´¥ÃþÆÁÉϱ¨µÄ×ø±ê */ | |
set_bit(EV_KEY, input_dev->evbit); | |
set_bit(EV_ABS, input_dev->evbit); | |
input_dev->name = TOUCHSCREEN_NAME; | |
/* ×¢²áÕâ¸öinput É豸 */ | |
err = input_register_device(input_dev); | |
if (err) { | |
dev_err(&client->dev, | |
"[TP]ts_probe: failed to register input device: %s\n", | |
dev_name(&client->dev)); | |
goto exit_input_register_device_failed; | |
} | |
tp_sysfs_create_group(touchscreen_data); | |
return 0; | |
exit_input_register_device_failed: | |
input_free_device(input_dev); | |
exit_input_dev_alloc_failed: | |
exit_irq_request_failed: | |
i2c_set_clientdata(client, NULL); | |
kfree(touchscreen_data); | |
exit_alloc_data_failed: | |
return err; | |
} | |
#ifdef CONFIG_PM | |
static int ts_suspend(struct i2c_client *client, pm_message_t mesg) | |
{ | |
u8 pmode_sleep = 0x03; | |
struct ts_data *ts = i2c_get_clientdata(client); | |
dev_dbg(&ts->client->dev, "[TP]ts_suspend \n"); | |
disable_irq(PIN_TP_IRQ); | |
tp_write(s_touchscreen_data->client, TP_REG_ID_PMODE, pmode_sleep); | |
return 0; | |
} | |
static int ts_resume(struct i2c_client *client) | |
{ | |
struct ts_data *ts = i2c_get_clientdata(client); | |
dev_dbg(&ts->client->dev, "[TP]ts_resume.\n"); | |
if (gpio_is_valid(PIN_TP_RST)) { | |
gpio_set_value(PIN_TP_RST, 0); | |
msleep(20); | |
gpio_set_value(PIN_TP_RST, 1); | |
msleep(200); | |
} | |
enable_irq(PIN_TP_IRQ); | |
return 0; | |
} | |
#endif | |
static int ts_remove(struct i2c_client *client) | |
{ | |
struct ts_data *touchscreen_data; | |
touchscreen_data = i2c_get_clientdata(client); | |
tp_sysfs_remove_group(touchscreen_data); | |
input_unregister_device(touchscreen_data->input_dev); | |
kfree(touchscreen_data); | |
i2c_set_clientdata(client, NULL); | |
return 0; | |
} | |
static const struct i2c_device_id ts_id[] = { | |
{TOUCHSCREEN_NAME, 0}, | |
{} | |
}; | |
MODULE_DEVICE_TABLE(i2c, ts_id); | |
#define ts_match_table NULL | |
static struct i2c_driver ts_driver = { | |
.probe = ts_probe, | |
.remove = ts_remove, | |
.id_table = ts_id, | |
.driver = { | |
.name = TOUCHSCREEN_NAME, | |
.owner = THIS_MODULE, | |
.of_match_table = ts_match_table, | |
}, | |
#ifdef CONFIG_PM | |
.suspend = ts_suspend, | |
.resume = ts_resume, | |
#endif | |
}; | |
static int __init ts_init(void) | |
{ | |
int ret; | |
ret = i2c_add_driver(&ts_driver); | |
if (ret) | |
pr_err("[TP]Adding touchscreen driver failed (errno = %d)\n", ret); | |
return ret; | |
} | |
static void __exit ts_exit(void) | |
{ | |
i2c_del_driver(&ts_driver); | |
} | |
module_init(ts_init); | |
module_exit(ts_exit); | |
MODULE_AUTHOR("Dyy"); | |
MODULE_DESCRIPTION("TouchScreen driver"); | |
MODULE_LICENSE("GPL"); | |