blob: 55a7d545a6606d1f0f42dd86bc027b19592b96c1 [file] [log] [blame]
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/delay.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/wakelock.h>
#include <linux/earlysuspend.h>
#include <linux/suspend.h>
#endif
#include <asm/irq.h>
#include <mach/gpio.h>
#include <mach/map.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/kthread.h>
/* 20 ms */
#define TOUCH_READ_TIME (HZ/200)
#define TOUCH_INT_PIN EXYNOS4_GPX3(1)
#define TOUCH_INT_PIN_SHIFT 1
#define TOUCH_RST_PIN EXYNOS4_GPE3(5)
#define TOUCHSCREEN_MINX 0
#define TOUCHSCREEN_MAXX 3968
#define TOUCHSCREEN_MINY 0
#define TOUCHSCREEN_MAXY 2304
#define INPUT_REPORT(x, y, p) \
{ \
input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); \
input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); \
input_report_abs(tsdata->input, ABS_MT_TOUCH_MAJOR, p); \
input_mt_sync(tsdata->input); \
}
struct unidisplay_ts_data {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work work;
int irq;
};
wait_queue_head_t idle_wait;
static struct task_struct *kidle_task;
static irqreturn_t unidisplay_ts_isr(int irq, void *dev_id);
static void unidisplay_ts_config(void)
{
s3c_gpio_cfgpin(TOUCH_INT_PIN, S3C_GPIO_SFN(0x0F));
s3c_gpio_setpull(TOUCH_INT_PIN, S3C_GPIO_PULL_UP);
if (gpio_request(TOUCH_INT_PIN, "TOUCH_INT_PIN")) {
printk(KERN_ERR "%s : gpio request failed.\n", __func__);
return;
}
gpio_direction_input(TOUCH_INT_PIN);
gpio_free(TOUCH_INT_PIN);
s3c_gpio_setpull(TOUCH_RST_PIN, S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(TOUCH_RST_PIN, S3C_GPIO_OUTPUT);
if (gpio_request(TOUCH_RST_PIN, "TOUCH_RST_PIN")) {
printk(KERN_ERR "%s : gpio request failed.\n", __func__);
return;
}
gpio_direction_output(TOUCH_RST_PIN, 1);
gpio_free(TOUCH_RST_PIN);
}
static void unidisplay_ts_start(void)
{
if (gpio_request(TOUCH_RST_PIN, "TOUCH_RST_PIN")) {
printk(KERN_ERR "%s : gpio request failed.\n", __func__);
return;
}
gpio_set_value(TOUCH_RST_PIN, 0);
gpio_free(TOUCH_RST_PIN);
}
static void unidisplay_ts_stop(void)
{
if (gpio_request(TOUCH_RST_PIN, "TOUCH_RST_PIN")) {
printk(KERN_ERR "%s : gpio request failed.\n", __func__);
return;
}
gpio_set_value(TOUCH_RST_PIN, 1);
gpio_free(TOUCH_RST_PIN);
}
static void unidisplay_ts_reset(void)
{
unidisplay_ts_stop();
udelay(100);
unidisplay_ts_start();
}
static int unidisplay_ts_read_irq(void)
{
return (readl(S5P_VA_GPIO2 + 0xC64) >> (TOUCH_INT_PIN_SHIFT)) & 0x1;
}
static irqreturn_t unidisplay_ts_isr(int irq, void *dev_id)
{
if (!unidisplay_ts_read_irq())
wake_up_interruptible(&(idle_wait));
return IRQ_HANDLED;
}
int unidisplay_ts_thread(void *kthread)
{
struct unidisplay_ts_data *tsdata =
(struct unidisplay_ts_data *)kthread;
u8 buf[9];
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
int ret;
u8 type = 0;
int pendown;
do {
interruptible_sleep_on(&idle_wait);
disable_irq(tsdata->irq);
while (!kthread_should_stop()) {
pendown = !unidisplay_ts_read_irq();
if (pendown) {
u8 addr = 0x10;
memset(buf, 0, sizeof(buf));
ret = i2c_master_send(tsdata->client, &addr, 1);
if (ret != 1) {
dev_err(&tsdata->client->dev,\
"Unable to write to i2c touchscreen\n");
enable_irq(tsdata->irq);
continue;
}
ret = i2c_master_recv(tsdata->client, buf, 9);
if (ret != 9) {
dev_err(&tsdata->client->dev,\
"Unable to read to i2c touchscreen!\n");
enable_irq(tsdata->irq);
continue;
}
type = buf[0];
if (type & 0x1) {
x1 = buf[2];
x1 <<= 8;
x1 |= buf[1];
y1 = buf[4];
y1 <<= 8;
y1 |= buf[3];
INPUT_REPORT(x1, y1, 1);
}
if (type & 0x2) {
x2 = buf[6];
x2 <<= 8;
x2 |= buf[5];
y2 = buf[8];
y2 <<= 8;
y2 |= buf[7];
INPUT_REPORT(x2, y2, 2);
}
input_sync(tsdata->input);
interruptible_sleep_on_timeout(&idle_wait,\
TOUCH_READ_TIME);
} else {
if (type & 1) {
INPUT_REPORT(x1, y1, 0);
x1 = 0;
y1 = 0;
}
if (type & 2) {
INPUT_REPORT(x2, y2, 0);
x2 = 0;
y2 = 0;
}
input_sync(tsdata->input);
INPUT_REPORT(0, 0, 0);
INPUT_REPORT(0, 0, 0);
input_sync(tsdata->input);
break;
}
}
enable_irq(tsdata->irq);
} while (!kthread_should_stop());
return 0;
}
static int unidisplay_ts_open(struct input_dev *dev)
{
return 0;
}
static void unidisplay_ts_close(struct input_dev *dev)
{
}
static int unidisplay_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct unidisplay_ts_data *tsdata;
int error;
unidisplay_ts_config();
unidisplay_ts_reset();
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
printk(KERN_ERR "unidisplay_ts_probe: i2c function check failed.\n");
return -ENODEV;
}
tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
if (!tsdata) {
dev_err(&client->dev, "failed to allocate driver data!\n");
error = -ENOMEM;
dev_set_drvdata(&client->dev, NULL);
return error;
}
dev_set_drvdata(&client->dev, tsdata);
tsdata->input = input_allocate_device();
if (!tsdata->input) {
dev_err(&client->dev, "failed to allocate input device!\n");
input_free_device(tsdata->input);
kfree(tsdata);
return -ENOMEM;
}
tsdata->input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |\
BIT_MASK(EV_ABS);
set_bit(EV_SYN, tsdata->input->evbit);
set_bit(EV_KEY, tsdata->input->evbit);
set_bit(EV_ABS, tsdata->input->evbit);
tsdata->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
set_bit(0, tsdata->input->absbit);
set_bit(1, tsdata->input->absbit);
set_bit(2, tsdata->input->absbit);
input_set_abs_params(tsdata->input, ABS_X, TOUCHSCREEN_MINX,\
TOUCHSCREEN_MAXX, 0, 0);
input_set_abs_params(tsdata->input, ABS_Y, TOUCHSCREEN_MINY,\
TOUCHSCREEN_MAXY, 0, 0);
input_set_abs_params(tsdata->input, ABS_HAT0X, TOUCHSCREEN_MINX,\
TOUCHSCREEN_MAXX, 0, 0);
input_set_abs_params(tsdata->input, ABS_HAT0Y, TOUCHSCREEN_MINY,\
TOUCHSCREEN_MAXY, 0, 0);
input_set_abs_params(tsdata->input, ABS_MT_POSITION_X,\
TOUCHSCREEN_MINX, TOUCHSCREEN_MAXX, 0, 0);
input_set_abs_params(tsdata->input, ABS_MT_POSITION_Y, \
TOUCHSCREEN_MINY, TOUCHSCREEN_MAXY, 0, 0);
input_set_abs_params(tsdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(tsdata->input, ABS_MT_WIDTH_MAJOR, 0, 25, 0, 0);
tsdata->input->name = client->name;
tsdata->input->id.bustype = BUS_I2C;
tsdata->input->dev.parent = &client->dev;
tsdata->input->open = unidisplay_ts_open;
tsdata->input->close = unidisplay_ts_close;
input_set_drvdata(tsdata->input, tsdata);
tsdata->client = client;
tsdata->irq = client->irq;
if (input_register_device(tsdata->input)) {
input_free_device(tsdata->input);
kfree(tsdata);
return -EIO;
}
device_init_wakeup(&client->dev, 1);
init_waitqueue_head(&idle_wait);
kidle_task = kthread_run(unidisplay_ts_thread, tsdata, "kidle_timeout");
if (IS_ERR(kidle_task)) {
printk(KERN_ERR "error k thread run\n");
return -1;
}
if (request_irq(tsdata->irq, unidisplay_ts_isr,\
IRQF_DISABLED | IRQF_TRIGGER_FALLING, client->name, tsdata)) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
input_unregister_device(tsdata->input);
}
return 0;
}
static int unidisplay_ts_remove(struct i2c_client *client)
{
struct unidisplay_ts_data *tsdata = dev_get_drvdata(&client->dev);
disable_irq(tsdata->irq);
free_irq(tsdata->irq, tsdata);
input_unregister_device(tsdata->input);
kfree(tsdata);
dev_set_drvdata(&client->dev, NULL);
return 0;
}
static int unidisplay_ts_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct unidisplay_ts_data *tsdata = dev_get_drvdata(&client->dev);
disable_irq(tsdata->irq);
return 0;
}
static int unidisplay_ts_resume(struct i2c_client *client)
{
struct unidisplay_ts_data *tsdata = dev_get_drvdata(&client->dev);
enable_irq(tsdata->irq);
return 0;
}
static const struct i2c_device_id unidisplay_ts_i2c_id[] = {
{ "unidisplay_ts", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, unidisplay_ts_i2c_id);
static struct i2c_driver unidisplay_ts_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "unidisplay touchscreen driver",
},
.probe = unidisplay_ts_probe,
.remove = unidisplay_ts_remove,
.suspend = unidisplay_ts_suspend,
.resume = unidisplay_ts_resume,
.id_table = unidisplay_ts_i2c_id,
};
static int __init unidisplay_ts_init(void)
{
return i2c_add_driver(&unidisplay_ts_i2c_driver);
}
static void __exit unidisplay_ts_exit(void)
{
i2c_del_driver(&unidisplay_ts_i2c_driver);
}
module_init(unidisplay_ts_init);
module_exit(unidisplay_ts_exit);
MODULE_AUTHOR("JHKIM");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("unidisplay Touch-screen Driver");