diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-04-19 23:35:23 +0400 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-04-19 23:35:23 +0400 |
commit | b653b77a84c1be41ae737806acb66deec731c8fb (patch) | |
tree | c7362466592c6b6235f1480f6eaa9dd0a3606953 | |
parent | 68fa27d1420e8b590d310d2b310e68010b4c825d (diff) | |
parent | cfa9fbfdddd980e45116e94384d6cf4833cdf44d (diff) | |
download | linux-topics-b653b77a84c1be41ae737806acb66deec731c8fb.tar.gz |
Merge branch 'rebase-samslt-touch' into merge-linux-linaro
-rw-r--r-- | arch/arm/configs/exynos4_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/irqs.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-origen.c | 6 | ||||
-rw-r--r-- | configs/origen.conf | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 7 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/unidisplay_ts.c | 406 |
7 files changed, 423 insertions, 0 deletions
diff --git a/arch/arm/configs/exynos4_defconfig b/arch/arm/configs/exynos4_defconfig index 42289fb55c0..6a19e5b567b 100644 --- a/arch/arm/configs/exynos4_defconfig +++ b/arch/arm/configs/exynos4_defconfig @@ -57,6 +57,7 @@ CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_UNIDISPLAY_TS=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_SAMSUNG=y CONFIG_SERIAL_SAMSUNG_CONSOLE=y diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h index bcc14e25156..ab390108bf1 100644 --- a/arch/arm/mach-exynos/include/mach/irqs.h +++ b/arch/arm/mach-exynos/include/mach/irqs.h @@ -462,6 +462,7 @@ #define S5P_GPIOINT_BASE (S5P_EINT_BASE1 + 32) #define IRQ_GPIO_END (S5P_GPIOINT_BASE + S5P_GPIOINT_COUNT) #define IRQ_TIMER_BASE (IRQ_GPIO_END + 64) +#define IRQ_TS (S5P_EINT_BASE1 + 25) /* Set the default NR_IRQS */ diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 65b1af7f73f..b2a248020b9 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -469,6 +469,12 @@ static struct i2c_board_info i2c0_devs[] __initdata = { .platform_data = &origen_max8997_pdata, .irq = IRQ_EINT(4), }, +#ifdef CONFIG_TOUCHSCREEN_UNIDISPLAY_TS + { + I2C_BOARD_INFO("unidisplay_ts", 0x41), + .irq = IRQ_TS, + }, +#endif }; static struct s3c_sdhci_platdata origen_hsmmc0_pdata __initdata = { diff --git a/configs/origen.conf b/configs/origen.conf index c5f430e01ef..1b2fdb2e62c 100644 --- a/configs/origen.conf +++ b/configs/origen.conf @@ -36,6 +36,7 @@ CONFIG_ATH6KL=y CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_UNIDISPLAY_TS=y CONFIG_VT_HW_CONSOLE_BINDING=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_SAMSUNG=y diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1e7c563c090..bd650ff1852 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -851,4 +851,11 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_UNIDISPLAY_TS + tristate "Pixcir capacitive touchscreen driver" + depends on I2C + help + Say Y here if you have a Pixcir capacitive based touchscreen + controller. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 175d641404c..ce7273b7147 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_UNIDISPLAY_TS) += unidisplay_ts.o diff --git a/drivers/input/touchscreen/unidisplay_ts.c b/drivers/input/touchscreen/unidisplay_ts.c new file mode 100644 index 00000000000..b0504d4c77a --- /dev/null +++ b/drivers/input/touchscreen/unidisplay_ts.c @@ -0,0 +1,406 @@ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/gpio.h> + +#include <mach/regs-gpio.h> + +#include <plat/gpio-cfg.h> + +/* 20 ms */ +#define TOUCH_READ_TIME msecs_to_jiffies(20) + +#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 TOUCH_DEBUG +#ifdef TOUCH_DEBUG +#define DEBUG_PRINT(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_PRINT(fmt, args...) +#endif + +#define INPUT_REPORT(x, y, p, val1, val2) \ + { \ + 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_report_abs(tsdata->input, ABS_PRESSURE, val1); \ + input_report_key(tsdata->input, BTN_TOUCH, val2); \ + input_mt_sync(tsdata->input); \ + } + +struct unidisplay_ts_data { + struct i2c_client *client; + struct input_dev *input; + struct task_struct *kidle_task; + wait_queue_head_t idle_wait; + struct delayed_work work; + int irq; + unsigned int irq_pending; +}; + + +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")) { + pr_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")) { + pr_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")) { + pr_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")) { + pr_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_pen_up(void) +{ + return (gpio_get_value(TOUCH_INT_PIN) & 0x1); +} + +static irqreturn_t unidisplay_ts_isr(int irq, void *dev_id) +{ + struct unidisplay_ts_data *tsdata = dev_id; + if (irq == tsdata->irq) { + disable_irq_nosync(tsdata->irq); + tsdata->irq_pending = 1; + wake_up(&tsdata->idle_wait); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int unidisplay_ts_thread(void *kthread) +{ + struct unidisplay_ts_data *tsdata = kthread; + struct task_struct *tsk = current; + int ret = 0; + struct sched_param param = { .sched_priority = 1 }; + sched_setscheduler(tsk, SCHED_FIFO, ¶m); + set_freezable(); + while (!kthread_should_stop()) { + int x1 = 0, y1 = 0; + u8 buf[9]; + u8 type = 0; + unsigned int pendown = 0; + long timeout = 0; + if (tsdata->irq_pending) { + tsdata->irq_pending = 0; + enable_irq(tsdata->irq); + } + pendown = !unidisplay_ts_pen_up(); + 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"); + ret = -EIO; + timeout = MAX_SCHEDULE_TIMEOUT; + goto wait_event; + } + ret = i2c_master_recv(tsdata->client, buf, 9); + if (ret != 9) { + dev_err(&tsdata->client->dev,\ + "Unable to read to i2c touchscreen!\n"); + ret = -EIO; + timeout = MAX_SCHEDULE_TIMEOUT; + goto wait_event; + } + /* mark everything ok now */ + ret = 0; + 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, 255, 1); + } + if (type & 0x2) { + x1 = buf[6]; + x1 <<= 8; + x1 |= buf[5]; + y1 = buf[8]; + y1 <<= 8; + y1 |= buf[7]; + INPUT_REPORT(x1, y1, 2, 255, 1); + } + input_sync(tsdata->input); + timeout = msecs_to_jiffies(20); + } else { + INPUT_REPORT(0, 0, 0, 0 ,0); + INPUT_REPORT(0, 0, 0, 0, 0); + input_sync(tsdata->input); + timeout = MAX_SCHEDULE_TIMEOUT; + } +wait_event: + wait_event_freezable_timeout(tsdata->idle_wait, \ + tsdata->irq_pending || kthread_should_stop(), \ + timeout); + } + return ret; +} + +static int unidisplay_ts_open(struct input_dev *dev) +{ + struct unidisplay_ts_data *tsdata = input_get_drvdata(dev); + int ret = 0; + u8 addr = 0x10; + BUG_ON(tsdata->kidle_task); + + ret = i2c_master_send(tsdata->client, &addr, 1); + + if (ret != 1) { + dev_err(&tsdata->client->dev, "Unable to open touchscreen device\n"); + return -ENODEV; + } + + tsdata->kidle_task = kthread_run(unidisplay_ts_thread, tsdata, \ + "unidisplay_ts"); + if (IS_ERR(tsdata->kidle_task)) { + ret = PTR_ERR(tsdata->kidle_task); + tsdata->kidle_task = NULL; + return ret; + } + enable_irq(tsdata->irq); + + return 0; +} + +static void unidisplay_ts_close(struct input_dev *dev) +{ + struct unidisplay_ts_data *tsdata = input_get_drvdata(dev); + + if (tsdata->kidle_task) { + kthread_stop(tsdata->kidle_task); + tsdata->kidle_task = NULL; + } + + disable_irq(tsdata->irq); +} + +static int unidisplay_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct unidisplay_ts_data *tsdata; + int err; + + unidisplay_ts_config(); + unidisplay_ts_reset(); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c func not supported\n"); + err = -EIO; + goto end; + } + + tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL); + if (!tsdata) { + dev_err(&client->dev, "failed to allocate driver data!\n"); + err = -ENOMEM; + goto fail1; + } + + dev_set_drvdata(&client->dev, tsdata); + + tsdata->input = input_allocate_device(); + if (!tsdata->input) { + dev_err(&client->dev, "failed to allocate input device!\n"); + err = -ENOMEM; + goto fail2; + } + + 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; + + err = input_register_device(tsdata->input); + if (err) + goto fail2; + + device_init_wakeup(&client->dev, 1); + init_waitqueue_head(&tsdata->idle_wait); + + err = request_irq(tsdata->irq, unidisplay_ts_isr,\ + IRQF_TRIGGER_FALLING, client->name, tsdata); + if (err != 0) { + dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + goto fail3; + } + /* disable irq for now, will be enabled when device is opened */ + disable_irq(tsdata->irq); + pr_info("Unidisplay touch driver registered successfully\n"); + return err; +fail3: + input_unregister_device(tsdata->input); +fail2: + input_free_device(tsdata->input); + kfree(tsdata); +fail1: + dev_set_drvdata(&client->dev, NULL); +end: + return err; +} + + +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; +} +#ifdef CONFIG_PM +static int unidisplay_ts_suspend(struct device *dev) +{ + struct unidisplay_ts_data *tsdata = dev_get_drvdata(dev); + disable_irq(tsdata->irq); + return 0; +} + +static int unidisplay_ts_resume(struct device *dev) +{ + struct unidisplay_ts_data *tsdata = dev_get_drvdata(dev); + enable_irq(tsdata->irq); + return 0; +} +static const struct dev_pm_ops unidisplay_ts_pm = { + .suspend = unidisplay_ts_suspend, + .resume = unidisplay_ts_resume, +}; +#endif + +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 = { + .name = "Unidisplay Touch Driver", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &unidisplay_ts_pm, +#endif + }, + .probe = unidisplay_ts_probe, + .remove = unidisplay_ts_remove, + .id_table = unidisplay_ts_i2c_id, +}; + +static int __init unidisplay_ts_init(void) +{ + return i2c_add_driver(&unidisplay_ts_i2c_driver); +} +module_init(unidisplay_ts_init); + +static void __exit unidisplay_ts_exit(void) +{ + i2c_del_driver(&unidisplay_ts_i2c_driver); +} +module_exit(unidisplay_ts_exit); + +MODULE_AUTHOR("JHKIM"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_DESCRIPTION("unidisplay Touch-screen Driver"); + |