diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 8a09804d..6778be4d 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_MACH_BRAVOC) += board-bravoc-wifi.o htc_awb_cal.o obj-$(CONFIG_MACH_BRAVOC) += board-bravoc-microp.o clock.o obj-$(CONFIG_MACH_HTCLEO) += board-htcleo.o board-htcleo-spi.o board-htcleo-panel.o board-htcleo-keypad.o +obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-ts.o obj-$(CONFIG_MACH_HTCLEO) += clock-wince.o # MSM7x30 boards diff --git a/arch/arm/mach-msm/board-htcleo-ts.c b/arch/arm/mach-msm/board-htcleo-ts.c new file mode 100644 index 00000000..02656bfe --- /dev/null +++ b/arch/arm/mach-msm/board-htcleo-ts.c @@ -0,0 +1,675 @@ +/* board-htcleo-ts.c + * + * Copyright (C) 2010 Cotulla + * + * 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. + * + */ + +//#define DEBUG + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "board-htcleo.h" +#include "board-htcleo-ts.h" +#include "gpio_chip.h" + + + + +#define TOUCH_TYPE_UNKNOWN 0 +#define TOUCH_TYPE_B8 1 +#define TOUCH_TYPE_68 2 +#define TOUCH_TYPE_2A 3 + + +#define LEO_TYPE_1 1 +#define LEO_TYPE_2 2 +#define LEO_TYPE_3 3 + + + +#define TYPE_68_DEVID (0x68 >> 1) +#define TYPE_B8_DEVID (0xB8 >> 1) +#define TYPE_2A_DEVID (0x2A >> 1) + + + +#define MAKEWORD(a, b) ((uint16_t)(((uint8_t)(a)) | ((uint16_t)((uint8_t)(b))) << 8)) + + +struct leo_ts_data +{ + struct i2c_client *client; + struct input_dev *input_dev; + uint32_t prev_ptcount; + int ts_type; + int intr_type; // 0 - FAILING, 1- RISING + int pressed1; + int pressed2; + struct work_struct work; + uint16_t version; + struct early_suspend early_suspend; +}; + +struct workqueue_struct *leo_touch_wq; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void leo_ts_early_suspend(struct early_suspend *h); +static void leo_ts_late_resume(struct early_suspend *h); +#endif + + + +static int I2C_Read(struct leo_ts_data *ts, uint8_t dev, uint8_t addr, uint32_t sz, uint8_t* bf) +{ + struct i2c_msg msg[2]; + int ret = 0; + + msg[0].addr = dev; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &addr; + + msg[1].addr = dev; + msg[1].flags = I2C_M_RD; + msg[1].len = sz; + msg[1].buf = bf; + + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) + { + dev_dbg(&ts->client->dev, "TS: I2C_Read(%x %x) failed!\n", dev, addr); + return 0; + } + return 1; +} + +static int I2C_ReadNo(struct leo_ts_data *ts, uint8_t dev, uint32_t sz, uint8_t* bf) +{ + struct i2c_msg msg[1]; + int ret = 0; + + msg[0].addr = dev; + msg[0].flags = I2C_M_RD; + msg[0].len = sz; + msg[0].buf = bf; + + ret = i2c_transfer(ts->client->adapter, msg, 1); + if (ret < 0) + { + dev_dbg(&ts->client->dev, "TS: I2C_ReadNo(%x) failed!\n", dev); + return 0; + } + return 1; +} + +static int leo_detect_ts_type(struct leo_ts_data *ts) +{ + uint8_t bt[4]; + + ts->ts_type = TOUCH_TYPE_UNKNOWN; + if (I2C_Read(ts, TYPE_B8_DEVID, 0x00, 1, bt)) + { + dev_dbg(&ts->client->dev, "TS: DETECTED TYPE B8\n"); + ts->ts_type = TOUCH_TYPE_B8; + return 1; + } + + if (I2C_Read(ts, TYPE_68_DEVID, 0x00, 1, bt)) + { + dev_dbg(&ts->client->dev, "TS: DETECTED TYPE 68\n"); + ts->ts_type = TOUCH_TYPE_68; + return 1; + } + + if (I2C_ReadNo(ts, TYPE_2A_DEVID, 4, bt) && bt[0] == 0x55) + { + dev_dbg(&ts->client->dev, "TS: DETECTED TYPE 2A\n"); + ts->ts_type = TOUCH_TYPE_2A; + return 1; + } + dev_dbg(&ts->client->dev, "TS: NOT DETECTED\n"); + return -1; +} + + +static int leo_reset_ts(struct leo_ts_data *ts) +{ +// only for XC + gpio_direction_output(HTCLEO_GPIO_TS_SEL, 1); + gpio_direction_output(HTCLEO_GPIO_TS_POWER, 0); + msleep(100); + gpio_direction_input(HTCLEO_GPIO_TS_IRQ); +// gpio_configure(92, GPIOF_INPUT | IRQF_TRIGGER_FALLING); + + while (gpio_get_value(HTCLEO_GPIO_TS_POWER)) + { + gpio_set_value(HTCLEO_GPIO_TS_SEL, 1); + gpio_set_value(HTCLEO_GPIO_TS_POWER, 0); + gpio_set_value(82, 0); + gpio_set_value(27, 0); + msleep(10); + } + gpio_set_value(82, 1); + gpio_set_value(27, 1); + + while (!gpio_get_value(HTCLEO_GPIO_TS_POWER)) + { + gpio_set_value(HTCLEO_GPIO_TS_POWER, 1); + } + msleep(20); + while (gpio_get_value(HTCLEO_GPIO_TS_IRQ)) + { + msleep(10); + } + + while (gpio_get_value(HTCLEO_GPIO_TS_SEL)) + { + gpio_set_value(HTCLEO_GPIO_TS_SEL, 0); + } + dev_dbg(&ts->client->dev, "TS: reset done\n"); + return 1; +} + +static int leo_init_ts(struct leo_ts_data *ts) +{ + uint8_t bt[6]; + struct i2c_msg msg; + int ret; + + switch (ts->ts_type) + { + case TOUCH_TYPE_2A: + bt[0] = 0xD0; + bt[1] = 0x00; + bt[2] = 0x01; + + msg.addr = 0x2A >> 1; + msg.flags = 0; + msg.len = 3; + msg.buf = bt; + + ret = i2c_transfer(ts->client->adapter, &msg, 1); + if (ret < 0) + { + goto error; + } + break; + default: + dev_dbg(&ts->client->dev, "TS: wrong type %x\n", ts->ts_type); + goto error; + } + dev_dbg(&ts->client->dev, "TS: init done\n"); + return 1; +error: + return -1; +} + + +static void leo_deinit_ts(void) +{ + gpio_set_value(HTCLEO_GPIO_TS_POWER, 0); +} + +static int leo_init_intr(struct leo_ts_data *ts) +{ + switch (ts->ts_type) + { + case TOUCH_TYPE_B8: + ts->intr_type = 1; + break; + case TOUCH_TYPE_68: + case TOUCH_TYPE_2A: + ts->intr_type = 0; + break; + default: + dev_dbg(&ts->client->dev, "TS: wrong type %x\n", ts->ts_type); + ts->intr_type = 0; + goto error; + } + return 1; +error: + return -1; +} + + +static void leo_ts_work_func(struct work_struct *work) +{ + uint8_t buf[9]; + uint32_t ptcount; + uint32_t ptx[2]; + uint32_t pty[2]; + struct leo_ts_data *ts = container_of(work, struct leo_ts_data, work); + uint8_t finger2_pressed; + int pressed1, pressed2; + + ptcount = 0; + switch (ts->ts_type) + { + case TOUCH_TYPE_2A: + if (!I2C_ReadNo(ts, TYPE_2A_DEVID, 9, buf)) + { + dev_dbg(&ts->client->dev, "TS: ReadPos failed\n"); + goto error; + } + if (buf[0] != 0x5A) + { + dev_dbg(&ts->client->dev, "TS: ReadPos wrmark\n"); + goto error; + } + + ptcount = (buf[8] >> 1) & 3; + if (ptcount > 2) + { + ptcount = 2; + } + + if (ptcount >= 1) + { + ptx[0] = MAKEWORD(buf[2], (buf[1] & 0xF0) >> 4); + pty[0] = MAKEWORD(buf[3], (buf[1] & 0x0F) >> 0); + } + if (ptcount == 2) + { + ptx[1] = MAKEWORD(buf[5], (buf[4] & 0xF0) >> 4); + pty[1] = MAKEWORD(buf[6], (buf[4] & 0x0F) >> 0); + } + break; + default: + dev_dbg(&ts->client->dev, "TS: wrong type %x\n", ts->ts_type); + goto error; + } + + + if (ptcount == 0) + dev_dbg(&ts->client->dev, "TS: not pressed\n"); + else if (ptcount == 1) + dev_dbg(&ts->client->dev, "TS: pressed1 (%d, %d)\n", ptx[0], pty[0]); + else if (ptcount == 2) + dev_dbg(&ts->client->dev, "TS: pressed2 (%d, %d) (%d, %d)\n", ptx[0], pty[0], ptx[1], pty[1]); + else + dev_dbg(&ts->client->dev, "TS: BUGGY!\n"); + + + if (ptcount == 0) + { + pressed1 = 0; + pressed2 = 0; + } + else if (ptcount == 1) + { + pressed1 = 1; + pressed2 = 0; + } + else if (ptcount == 2) + { + pressed1 = 1; + pressed2 = 1; + } + else + { + pressed1 = 0; + pressed2 = 0; + } + + if (pressed1) + { + dev_dbg(&ts->client->dev, "pressed1\n"); + input_report_abs(ts->input_dev, ABS_X, ptx[0]); + input_report_abs(ts->input_dev, ABS_Y, pty[0]); + input_report_abs(ts->input_dev, ABS_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#ifdef CONFIG_HTCLEO_ENABLE_MULTI_TOUCH + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ptx[0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pty[0]); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); +#endif + } + else if (ts->pressed1) + { + dev_dbg(&ts->client->dev, "unpressed1\n"); + input_report_abs(ts->input_dev, ABS_X, ptx[0]); + input_report_abs(ts->input_dev, ABS_Y, pty[0]); + input_report_abs(ts->input_dev, ABS_PRESSURE, 0); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, 0); + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#ifdef CONFIG_HTCLEO_ENABLE_MULTI_TOUCH + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ptx[0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pty[0]); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); +#endif + } +#ifdef CONFIG_HTCLEO_ENABLE_MULTI_TOUCH + input_mt_sync(ts->input_dev); + + + if (pressed2) + { + dev_dbg(&ts->client->dev, "pressed2\n"); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ptx[1]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pty[1]); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + } + else if (ts->pressed2) + { + dev_dbg(&ts->client->dev, "unpressed2\n"); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + } + input_mt_sync(ts->input_dev); +#endif + input_sync(ts->input_dev); + + ts->pressed1 = pressed1; + ts->pressed2 = pressed2; + +error: + ts->prev_ptcount = ptcount; + + /* prepare for next intr */ + enable_irq(ts->client->irq); +} + +static irqreturn_t leo_ts_irq_handler(int irq, void *dev_id) +{ + struct leo_ts_data *ts = dev_id; + disable_irq_nosync(ts->client->irq); + queue_work(leo_touch_wq, &ts->work); + return IRQ_HANDLED; +} + + + +//////////////////////////////////////////////////////////////////////////// + +static int leo_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct leo_ts_data *ts; + int ret = 0; + int x_start, y_start, x_end, y_end; + + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct leo_ts_data), GFP_KERNEL); + if (ts == NULL) + { + dev_err(&client->dev, "allocate leo_ts_data failed\n"); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + INIT_WORK(&ts->work, leo_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + ts->prev_ptcount = 0; + + gpio_request(HTCLEO_GPIO_TS_POWER, "htcleo_ts"); + gpio_request(HTCLEO_GPIO_TS_SEL, "htcleo_ts"); + gpio_request(HTCLEO_GPIO_TS_IRQ, "htcleo_ts"); + gpio_request(82, "htcleo_ts"); + gpio_request(27, "htcleo_ts"); + + + ret = leo_reset_ts(ts); + if (ret < 0) + { + dev_err(&client->dev, "TS: leo_reset_ts() failed\n"); + goto err_detect_failed; + } + + ret = leo_detect_ts_type(ts); + if (ret < 0) + { + dev_err(&client->dev, "TS: leo_detect_ts_type() failed\n"); + goto err_detect_failed; + } + +// !!!other types are not supported yet!!! + if (ts->ts_type == TOUCH_TYPE_68 || ts->ts_type == TOUCH_TYPE_B8) + { + dev_err(&ts->client->dev, "TS: NOT SUPPORTED\n"); + BUG(); + } + + ret = leo_init_ts(ts); + if (ret < 0) + { + dev_err(&client->dev, "TS: leo_init_ts() failed\n"); + goto err_detect_failed; + } + + ret = leo_init_intr(ts); + if (ret < 0) + { + dev_err(&client->dev, "TS: leo_init_intr() failed\n"); + goto err_detect_failed; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) + { + ret = -ENOMEM; + dev_err(&client->dev, "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + + ts->input_dev->name = "leo-touchscreen"; + + +// inital touch calibraion + switch (ts->ts_type) + { + case TOUCH_TYPE_2A: + x_start = 6; + y_start = 6; + x_end = 576; + y_end = 952; + break; + } + + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + + input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH); +#ifdef CONFIG_HTCLEO_ENABLE_MULTI_TOUCH + input_set_capability(ts->input_dev, EV_KEY, BTN_2); +#endif + + input_set_abs_params(ts->input_dev, ABS_X, x_start, x_end, 5, 0); + input_set_abs_params(ts->input_dev, ABS_Y, y_start, y_end, 5, 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, 5, 0, 0); +#ifdef CONFIG_HTCLEO_ENABLE_MULTI_TOUCH + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, x_start, x_end, 5, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, y_start, y_end, 5, 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, 1, 0, 0); +#endif +// input_set_abs_params(ts->input_dev, ABS_HAT0X, x_start, x_end, 0, 0); +// input_set_abs_params(ts->input_dev, ABS_HAT0Y, y_start, y_end, 0, 0); +// input_set_abs_params(ts->input_dev, ABS_PRESSURE, pdata->abs_pressure_min, pdata->abs_pressure_max, 0, 0); +// input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, pdata->abs_width_min, pdata->abs_width_max, 0, 0); + + ret = input_register_device(ts->input_dev); + if (ret) + { + dev_err(&client->dev, + "leo_ts_probe: Unable to register %s input device\n", + ts->input_dev->name); + goto err_input_register_device_failed; + } + + ts->client->irq = gpio_to_irq(HTCLEO_GPIO_TS_IRQ); + ret = request_irq(ts->client->irq, leo_ts_irq_handler, + ts->intr_type ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW, + LEO_TOUCH_DRV_NAME, ts); + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = leo_ts_early_suspend; + ts->early_suspend.resume = leo_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + dev_info(&client->dev, "Start touchscreen %s\n", ts->input_dev->name); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: + // POWER OFF + leo_deinit_ts(); +err_detect_failed: + kfree(ts); + +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + + + +static int leo_ts_remove(struct i2c_client *client) +{ + struct leo_ts_data *ts = i2c_get_clientdata(client); + + unregister_early_suspend(&ts->early_suspend); + + free_irq(client->irq, ts); + + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +static int leo_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct leo_ts_data *ts = i2c_get_clientdata(client); + int ret; + + disable_irq_nosync(client->irq); + + ret = cancel_work_sync(&ts->work); + if (ret) + { + enable_irq(client->irq); + } + + // POWER OFF + leo_deinit_ts(); + + return 0; +} + +static int leo_ts_resume(struct i2c_client *client) +{ + struct leo_ts_data *ts = i2c_get_clientdata(client); + + // POWER ON + leo_reset_ts(ts); + leo_detect_ts_type(ts); + leo_init_ts(ts); + leo_init_intr(ts); + + enable_irq(client->irq); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void leo_ts_early_suspend(struct early_suspend *h) +{ + struct leo_ts_data *ts; + ts = container_of(h, struct leo_ts_data, early_suspend); + leo_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void leo_ts_late_resume(struct early_suspend *h) +{ + struct leo_ts_data *ts; + ts = container_of(h, struct leo_ts_data, early_suspend); + leo_ts_resume(ts->client); +} +#endif + + +static const struct i2c_device_id leo_ts_id[] = +{ + { LEO_TOUCH_DRV_NAME, 0 }, + { } +}; + + +static struct i2c_driver leo_ts_driver = +{ + .driver = + { + .name = LEO_TOUCH_DRV_NAME, + .owner = THIS_MODULE, + }, + .id_table = leo_ts_id, + .probe = leo_ts_probe, + .remove = leo_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = leo_ts_suspend, + .resume = leo_ts_resume, +#endif +}; + + +static int __devinit leo_ts_init(void) +{ + leo_touch_wq = create_singlethread_workqueue("leo_touch_wq"); + if (!leo_touch_wq) + { + return -ENOMEM; + } + return i2c_add_driver(&leo_ts_driver); +} + +static void __exit leo_ts_exit(void) +{ + if (leo_touch_wq) + { + destroy_workqueue(leo_touch_wq); + leo_touch_wq = NULL; + } + i2c_del_driver(&leo_ts_driver); +} + +module_init(leo_ts_init); +module_exit(leo_ts_exit); + + +MODULE_DESCRIPTION("HTC LEO Touchscreen Support Driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-htcleo-ts.h b/arch/arm/mach-msm/board-htcleo-ts.h new file mode 100644 index 00000000..fd28f4c9 --- /dev/null +++ b/arch/arm/mach-msm/board-htcleo-ts.h @@ -0,0 +1,38 @@ +/* board-htcleo-ts.h + * + * Copyright (C) 2010 Cotulla + * + * 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. + * + */ + +#ifndef HTCLEO_TS_H +#define HTCLEO_TS_H + +#include + +#define LEO_TOUCH_DRV_NAME "leo_touch_name" + +struct htcleo_ts_i2c_platform_data +{ + uint16_t version; + int abs_x_min; + int abs_x_max; + int abs_y_min; + int abs_y_max; + int abs_pressure_min; + int abs_pressure_max; + int abs_width_min; + int abs_width_max; +}; + +#endif // HTCLEO_TS_H + + diff --git a/arch/arm/mach-msm/board-htcleo.c b/arch/arm/mach-msm/board-htcleo.c index 11d02d63..7611b483 100644 --- a/arch/arm/mach-msm/board-htcleo.c +++ b/arch/arm/mach-msm/board-htcleo.c @@ -40,6 +40,7 @@ #include #include "board-htcleo.h" +#include "board-htcleo-ts.h" #include "devices.h" #include "proc_comm.h" #include "dex_comm.h" @@ -134,6 +135,9 @@ static struct platform_device htcleo_timed_gpios = { static struct i2c_board_info base_i2c_devices[] = { + { + I2C_BOARD_INFO(LEO_TOUCH_DRV_NAME, 0), + }, { I2C_BOARD_INFO("tps65023", 0x48), .platform_data = tps65023_data,