From 3ec578794188a36426b2b7980646b4a45dfc0a80 Mon Sep 17 00:00:00 2001 From: Alejandro Lozano Date: Tue, 6 Sep 2016 16:41:13 -0500 Subject: MLK-13244 input: touchscreen: add support for vtl touchscreen Add the support for a CT36X based touchscreens using the CT36X controller and i2c touchscreen interface. Signed-off-by: Alejandro Lozano Signed-off-by: Juan Gutierrez Signed-off-by: Alejandro Sierra --- .../bindings/input/touchscreen/vtl_ts.txt | 18 + drivers/input/touchscreen/Kconfig | 13 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/vtl/Makefile | 5 + drivers/input/touchscreen/vtl/vtl_ts.c | 496 +++++++++++++++++++++ drivers/input/touchscreen/vtl/vtl_ts.h | 181 ++++++++ 6 files changed, 714 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt create mode 100644 drivers/input/touchscreen/vtl/Makefile create mode 100644 drivers/input/touchscreen/vtl/vtl_ts.c create mode 100644 drivers/input/touchscreen/vtl/vtl_ts.h diff --git a/Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt new file mode 100644 index 000000000000..a41a0b993006 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt @@ -0,0 +1,18 @@ +* VTL Touchscreen Controller + +Required properties: +- compatible: must be "vtl,ct365" +- reg: i2c slave address +- interrupt-parent: the phandle for the interrupt controller +- interrupts: touch controller interrupt +- gpios: the gpio pin to be used for reset + +Example: + + touchscreen@01 { + compatible = "vtl,ct365"; + reg = <0x01>; + interrupt-parent = <&gpio6>; + interrupts = <14 0>; + gpios = <&gpio4 10 0>; + }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 5c488b615b05..1beb19718488 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -46,6 +46,19 @@ config TOUCHSCREEN_ADS7846 To compile this driver as a module, choose M here: the module will be called ads7846. +config TOUCHSCREEN_CT36X_WLD + default y + tristate "CT36X based touchscreens for WLD" + help + Say Y here if you have a touchscreen interface using the + CT36X controller, i2c touchscreen + controller. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called vtl_ts. + config TOUCHSCREEN_AD7877 tristate "AD7877 based touchscreens" depends on SPI_MASTER diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 1af3ebfb6eb0..27de5a491f7a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_CT36X_WLD) += vtl/ obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o diff --git a/drivers/input/touchscreen/vtl/Makefile b/drivers/input/touchscreen/vtl/Makefile new file mode 100644 index 000000000000..2ee7f7aefe43 --- /dev/null +++ b/drivers/input/touchscreen/vtl/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the VTL touchscreen driver. +# + +obj-y += vtl_ts.o diff --git a/drivers/input/touchscreen/vtl/vtl_ts.c b/drivers/input/touchscreen/vtl/vtl_ts.c new file mode 100644 index 000000000000..f7bef9e81158 --- /dev/null +++ b/drivers/input/touchscreen/vtl/vtl_ts.c @@ -0,0 +1,496 @@ +/* + * VTL CTP driver + * + * Copyright (C) 2013 VTL Corporation + * Copyright (C) 2016 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FORCE_SINGLE_EVENT 1 + +#include "vtl_ts.h" + +#define MIN_X 0x00 +#define MIN_Y 0x00 +#define MAX_X 1023 +#define MAX_Y 767 +#define MAX_AREA 0xff +#define MAX_FINGERS 2 + + +/* Global or static variables */ +struct ts_driver g_driver; + +static struct ts_info g_ts = { + .driver = &g_driver, +}; +static struct ts_info *pg_ts = &g_ts; + +static struct i2c_device_id vtl_ts_id[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vtl_ts_id); + + +static int vtl_ts_config(struct ts_info *ts) +{ + struct device *dev; + + DEBUG(); + + dev = &ts->driver->client->dev; + + /* ts config */ + ts->config_info.touch_point_number = TOUCH_POINT_NUM; + + pr_info("Configuring vtl\n"); + ts->config_info.screen_max_x = SCREEN_MAX_X; + ts->config_info.screen_max_y = SCREEN_MAX_y; + return 0; +} + +void vtl_ts_free_gpio(void) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + gpio_free(ts->config_info.irq_gpio_number); +} + +void vtl_ts_hw_reset(void) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + gpio_set_value(ts->config_info.rst_gpio_number, 0); + mdelay(50); + gpio_set_value(ts->config_info.rst_gpio_number, 1); +} + +static irqreturn_t vtl_ts_irq(int irq, void *dev) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + queue_work(ts->driver->workqueue, &ts->driver->event_work); + + return IRQ_HANDLED; +} + +static union ts_xy_data *vtl_read_xy_data(struct ts_info *ts) +{ + struct i2c_msg msgs; + int err; + + DEBUG(); + + msgs.addr = ts->driver->client->addr; + msgs.flags = 0x01; + msgs.len = sizeof(ts->xy_data.buf); + msgs.buf = ts->xy_data.buf; + + err = i2c_transfer(ts->driver->client->adapter, &msgs, 1); + if (err != 1) { + pr_err("___%s:i2c read err___\n", __func__); + return NULL; + } + return &ts->xy_data; +} + +static void vtl_report_xy_coord(struct input_dev *input_dev, + union ts_xy_data *xy_data, + unsigned char touch_point_number) +{ + struct ts_info *ts; + int id; + int sync; + int x, y; + unsigned int press; + static unsigned int release; + + ts = pg_ts; + DEBUG(); + + /* report points */ + sync = 0; press = 0; + for (id = 0; id < touch_point_number; id++) { + if ((xy_data->point[id].xhi != 0xFF) && + (xy_data->point[id].yhi != 0xFF) && + ((xy_data->point[id].status == 1) || + (xy_data->point[id].status == 2))) { + x = (xy_data->point[id].xhi<<4) | + (xy_data->point[id].xlo&0xF); + y = (xy_data->point[id].yhi<<4) | + (xy_data->point[id].ylo&0xF); + + if (ts->config_info.exchange_x_y_flag) + swap(x, y); + + if (ts->config_info.revert_x_flag) + x = ts->config_info.screen_max_x - x; + + if (ts->config_info.revert_y_flag) + y = ts->config_info.screen_max_y - y; +#ifndef FORCE_SINGLE_EVENT + input_mt_slot(input_dev, xy_data->point[id].id - 1); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, true); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 30); + input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 128); +#else + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_PRESSURE, 1); +#endif + sync = 1; + press |= 0x01 << (xy_data->point[id].id - 1); + } + } + + release &= (release ^ press); /*release point flag */ + for (id = 0; id < touch_point_number; id++) { + if (release & (0x01 << id)) { +#ifndef FORCE_SINGLE_EVENT + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, false); +#else + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); +#endif + sync = 1; + } + + } + release = press; + + if (sync) + input_sync(input_dev); +} + +static void vtl_ts_workfunc(struct work_struct *work) +{ + + union ts_xy_data *xy_data; + struct input_dev *input_dev; + unsigned char touch_point_number; + + DEBUG(); + + input_dev = pg_ts->driver->input_dev; + touch_point_number = pg_ts->config_info.touch_point_number; + + xy_data = vtl_read_xy_data(pg_ts); + if (xy_data != NULL) + vtl_report_xy_coord(input_dev, xy_data, touch_point_number); + else + pr_err("____xy_data error___\n"); +} + +#ifdef CONFIG_PM_SLEEP +int vtl_ts_suspend(struct device *dev) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + disable_irq(ts->config_info.irq_number); + cancel_work_sync(&ts->driver->event_work); + + return 0; +} + +int vtl_ts_resume(struct device *dev) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + /* Hardware reset */ + vtl_ts_hw_reset(); + enable_irq(ts->config_info.irq_number); + + return 0; +} +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void vtl_ts_early_suspend(struct early_suspend *handler) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + vtl_ts_suspend(ts->driver->client, PMSG_SUSPEND); +} + +static void vtl_ts_early_resume(struct early_suspend *handler) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + vtl_ts_resume(ts->driver->client); +} +#endif + +int vtl_ts_remove(struct i2c_client *client) +{ + struct ts_info *ts; + + ts = pg_ts; + DEBUG(); + + /* Driver clean up */ + + free_irq(ts->config_info.irq_number, ts); + vtl_ts_free_gpio(); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->driver->early_suspend); +#endif + + cancel_work_sync(&ts->driver->event_work); + destroy_workqueue(ts->driver->workqueue); + + input_unregister_device(ts->driver->input_dev); + input_free_device(ts->driver->input_dev); + + if (ts->driver->proc_entry != NULL) + remove_proc_entry(DRIVER_NAME, NULL); + + return 0; +} + +static int init_input_dev(struct ts_info *ts) +{ + struct input_dev *input_dev; + struct device *dev; + int err; + + DEBUG(); + + dev = &ts->driver->client->dev; + + /* allocate input device */ + ts->driver->input_dev = devm_input_allocate_device(dev); + if (ts->driver->input_dev == NULL) { + dev_err(dev, "Unable to allocate input device for device %s\n", + DRIVER_NAME); + return -1; + } + + input_dev = ts->driver->input_dev; + + input_dev->name = "VTL for wld"; + input_dev->phys = "I2C"; + input_dev->id.bustype = BUS_I2C; + input_dev->id.vendor = 0xaaaa; + input_dev->id.product = 0x5555; + input_dev->id.version = 0x0001; + input_dev->dev.parent = dev; + + /* config input device */ + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + +#ifdef FORCE_SINGLE_EVENT + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0); + input_set_abs_params(input_dev, ABS_X, MIN_X, MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_Y, MIN_Y, MAX_Y, 0, 0); +#else + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_mt_init_slots(input_dev, TOUCH_POINT_NUM, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + ts->config_info.screen_max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + ts->config_info.screen_max_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); +#endif + /* register input device */ + err = input_register_device(input_dev); + if (err) { + dev_err(dev, "Unable to register input device for device %s\n", + DRIVER_NAME); + return -1; + } + + return 0; +} + +int ct36x_test_tp(struct i2c_client *client) +{ + struct i2c_msg msgs; + char buf; + + msgs.addr = 0x7F; + msgs.flags = 0x01; + msgs.len = 1; + msgs.buf = &buf; + + if (i2c_transfer(client->adapter, &msgs, 1) != 1) + return -1; + + return 0; +} + +int vtl_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int err = -1; + struct ts_info *ts; + struct device *dev; + + ts = pg_ts; + ts->driver->client = client; + dev = &ts->driver->client->dev; + + /*Probing TouchScreen*/ + pr_info("Probing vtl touchscreen, touchscreen node found\n"); + if (ct36x_test_tp(client) < 0) { + pr_err("vtl tp not found\n"); + goto ERR_TS_CONFIG; + } + + /* Request platform resources (gpio/interrupt pins) */ + err = vtl_ts_config(ts); + if (err) { + dev_err(dev, "VTL touch screen config Failed.\n"); + goto ERR_TS_CONFIG; + } + + /*Requestion GPIO*/ + ts->config_info.rst_gpio_number = of_get_gpio(client->dev.of_node, 0); + if (gpio_is_valid(ts->config_info.rst_gpio_number)) { + err = devm_gpio_request(dev, + ts->config_info.rst_gpio_number, NULL); + if (err) { + dev_err(dev, "Unable to request GPIO %d\n", + ts->config_info.rst_gpio_number); + return err; + } + } + + /* Check I2C Functionality */ + err = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); + if (!err) { + dev_err(dev, "Check I2C Functionality Failed.\n"); + return -ENODEV; + } + + err = devm_request_threaded_irq(dev, client->irq, + NULL, vtl_ts_irq, + IRQF_ONESHOT, + client->name, ts); + if (err) { + dev_err(&client->dev, "VTL Failed to register interrupt\n"); + + goto ERR_IRQ_REQ; + } + + vtl_ts_hw_reset(); + + /*init input dev*/ + err = init_input_dev(ts); + if (err) { + + dev_err(dev, "init input dev failed.\n"); + goto ERR_INIT_INPUT; + } + + /* register early suspend */ +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->driver->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->driver->early_suspend.suspend = vtl_ts_early_suspend; + ts->driver->early_suspend.resume = vtl_ts_early_resume; + register_early_suspend(&ts->driver->early_suspend); +#endif + /* Create work queue */ + INIT_WORK(&ts->driver->event_work, vtl_ts_workfunc); + ts->driver->workqueue = create_singlethread_workqueue(DRIVER_NAME); + + return 0; + +ERR_IRQ_REQ: + cancel_work_sync(&ts->driver->event_work); + destroy_workqueue(ts->driver->workqueue); + +ERR_INIT_INPUT: + input_free_device(ts->driver->input_dev); + gpio_free(ts->config_info.rst_gpio_number); +ERR_TS_CONFIG: + + return err; +} + + +static SIMPLE_DEV_PM_OPS(vtl_ts_pm_ops, vtl_ts_suspend, vtl_ts_resume); + +static const struct of_device_id vtl_ts_dt_ids[] = { + { .compatible = "vtl,ct365", }, + { } +}; +MODULE_DEVICE_TABLE(of, vtl_ts_dt_ids); + + +static struct i2c_driver vtl_ts_driver = { + .probe = vtl_ts_probe, + .remove = vtl_ts_remove, + .id_table = vtl_ts_id, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .pm = &vtl_ts_pm_ops, + .of_match_table = of_match_ptr(vtl_ts_dt_ids), + }, +}; + +module_i2c_driver(vtl_ts_driver); + +MODULE_AUTHOR("VTL"); +MODULE_DESCRIPTION("VTL TouchScreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/vtl/vtl_ts.h b/drivers/input/touchscreen/vtl/vtl_ts.h new file mode 100644 index 000000000000..9f61565d6639 --- /dev/null +++ b/drivers/input/touchscreen/vtl/vtl_ts.h @@ -0,0 +1,181 @@ +/* + * VTL CTP driver + * + * Copyright (C) 2016 Freescale Semiconductor, Inc + * + * Using code from: + * - github.com/qdk0901/q98_source:drivers/input/touchscreen/vtl/vtl_ts.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 _TS_CORE_H_ +#define _TS_CORE_H_ + +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + + +#ifdef TS_DEBUG +#define DEBUG() pr_debug("___%s___\n", __func__) +#else +#define DEBUG() +#endif + + +/* platform define */ +#define COMMON 0x01 /* Samsung,Freescale,Amlogic,actions */ +#define ROCKCHIP 0X02 +#define ALLWINER 0X03 +#define MTK 0X04 + +/* vtl touch IC define */ +#define CT36X 0x01 +#define CT360 0x02 + +/* xy data protocol */ +#define OLD_PROTOCOL 0x01 +#define NEW_PROTOCOL 0x02 + + +/* vtl ts driver config */ + +/*platform config*/ +#define PLATFORM COMMON + +/*vtl ts driver name*/ +#define DRIVER_NAME "vtl_ts" + +/*vtl chip ID*/ +#define CHIP_ID CT36X + +#define XY_DATA_PROTOCOL NEW_PROTOCOL + + +/* maybe not use,please refer to the function + * vtl_ts_config() in the file "vtl_ts.c" + */ +#define SCREEN_MAX_X 1024 +#define SCREEN_MAX_y 600 + +#define TS_IRQ_GPIO_NUM /* RK30_PIN4_PC2 */ +#define TS_RST_GPIO_NUM /* RK30_PIN4_PD0 */ +#define TS_I2C_SPEED 400000 /* for rockchip */ + + +/* priate define and declare */ +#if (CHIP_ID == CT360) +#define TOUCH_POINT_NUM 1 +#elif (CHIP_ID == CT36X) +#define TOUCH_POINT_NUM 1 +#endif + + +#if (CHIP_ID == CT360) +struct xy_data { +#if (XY_DATA_PROTOCOL == OLD_PROTOCOL) + unsigned char status:4; /* Action information, 1:Down; + 2: Move; 3: Up */ + unsigned char id:4; /* ID information, from 1 to + CFG_MAX_POINT_NUM */ +#endif + unsigned char xhi; /* X coordinate Hi */ + unsigned char yhi; /* Y coordinate Hi */ + unsigned char ylo:4; /* Y coordinate Lo */ + unsigned char xlo:4; /* X coordinate Lo */ +#if (XY_DATA_PROTOCOL == NEW_PROTOCOL) + unsigned char status:4; /* Action information, 1: Down; + 2: Move; 3: Up */ + unsigned char id:4; /* ID information, from 1 to + CFG_MAX_POINT_NUM */ +#endif +}; +#else +struct xy_data { +#if (XY_DATA_PROTOCOL == OLD_PROTOCOL) + unsigned char status:3; /* Action information, 1: Down; + 2: Move; 3: Up */ + unsigned char id:5; /* ID information, from 1 to + CFG_MAX_POINT_NUM */ +#endif + unsigned char xhi; /* X coordinate Hi */ + unsigned char yhi; /* Y coordinate Hi */ + unsigned char ylo:4; /* Y coordinate Lo */ + unsigned char xlo:4; /* X coordinate Lo */ +#if (XY_DATA_PROTOCOL == NEW_PROTOCOL) + unsigned char status:3; /* Action information, 1: Down; + 2: Move; 3: Up */ + unsigned char id:5; /* ID information, from 1 to + CFG_MAX_POINT_NUM */ +#endif + unsigned char area; /* Touch area */ + unsigned char pressure; /* Touch Pressure */ +}; +#endif + + +union ts_xy_data { + struct xy_data point[TOUCH_POINT_NUM]; + unsigned char buf[TOUCH_POINT_NUM * sizeof(struct xy_data)]; +}; + + +struct ts_driver { + + struct i2c_client *client; + + /* input devices */ + struct input_dev *input_dev; + + struct proc_dir_entry *proc_entry; + + /* Work thread settings */ + struct work_struct event_work; + struct workqueue_struct *workqueue; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +struct ts_config_info { + + unsigned int screen_max_x; + unsigned int screen_max_y; + unsigned int irq_gpio_number; + unsigned int irq_number; + unsigned int rst_gpio_number; + unsigned char touch_point_number; + unsigned char ctp_used; + unsigned char i2c_bus_number; + unsigned char revert_x_flag; + unsigned char revert_y_flag; + unsigned char exchange_x_y_flag; + int (*tp_enter_init)(void); + void (*tp_exit_init)(int state); +}; + + +struct ts_chip_info { + unsigned char chip_id; +}; + +struct ts_info { + + struct ts_driver *driver; + struct ts_config_info config_info; + struct ts_chip_info chip_info; + union ts_xy_data xy_data; +}; + +#endif -- cgit v1.2.3