blob: f7bef9e81158956bf6bd511d7e8107a250d530d4 [file] [log] [blame]
/*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/types.h>
#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");