blob: 062727b177ac8c192d8971d0f82d43bb7c80ddb0 [file] [log] [blame]
/* 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, &regaddr, 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");