1054 lines
27 KiB
C
1054 lines
27 KiB
C
/*
|
|
* drivers/input/touchscreen/sitronix_i2c_touch.c
|
|
*
|
|
* Touchscreen driver for Sitronix (I2C bus)
|
|
*
|
|
* Copyright (C) 2011 Sitronix Technology Co., Ltd.
|
|
* Rudy Huang <rudy_huang@sitronix.com.tw>
|
|
*/
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*/
|
|
#include <linux/version.h>
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
|
|
#if defined(CONFIG_FB)
|
|
#include <linux/notifier.h>
|
|
#include <linux/fb.h>
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
#include <linux/earlysuspend.h>
|
|
#endif
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/input.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/slab.h> // to be compatible with linux kernel 3.2.15
|
|
#include <linux/gpio.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/path.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
#include "sitronix_ts_custom_func.h"
|
|
#include "sitronix_ts.h"
|
|
|
|
//#define SITRONIX_SWAP_XY
|
|
|
|
#define DRIVER_AUTHOR "Sitronix, Inc."
|
|
#define DRIVER_NAME "sitronix"
|
|
#define DRIVER_DESC "Sitronix I2C touch"
|
|
#define DRIVER_DATE "20181115"
|
|
#define DRIVER_MAJOR 2
|
|
#define DRIVER_MINOR 11
|
|
#define DRIVER_PATCHLEVEL 1
|
|
|
|
#define MAX_BUTTONS 4
|
|
MODULE_AUTHOR("Petitk Kao<petitk_kao@sitronix.com.tw>");
|
|
MODULE_DESCRIPTION("Sitronix I2C multitouch panels");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#define SITRONIX_TOUCH_DRIVER_VERSION 0x03
|
|
#define SITRONIX_I2C_TOUCH_DRV_NAME "sitronix"
|
|
#define SITRONIX_I2C_TOUCH_MT_INPUT_DEV_NAME "SITRONIX"
|
|
#define SITRONIX_I2C_TOUCH_KEY_INPUT_DEV_NAME "sitronix-i2c-touch-key"
|
|
|
|
char sitronix_sensor_key_status = 0;
|
|
struct sitronix_sensor_key_t sitronix_sensor_key_array[] = {
|
|
{ KEY_MENU }, // bit 2
|
|
{ KEY_HOMEPAGE }, // bit 1
|
|
{ KEY_BACK }, // bit 0
|
|
};
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
static void sitronix_ts_early_suspend(struct early_suspend *h);
|
|
static void sitronix_ts_late_resume(struct early_suspend *h);
|
|
#endif
|
|
static int sitronix_ts_resume(struct i2c_client *client);
|
|
static int sitronix_ts_suspend(struct i2c_client *client);
|
|
static int fb_notifier_callback(struct notifier_block *self,
|
|
unsigned long event, void *data);
|
|
|
|
struct sitronix_ts_data stx_gpts = { 0 };
|
|
|
|
static int g_i2cErrorCount = 0;
|
|
#define I2C_CONTINUE_ERROR_CNT 30
|
|
|
|
static inline void sitronix_ts_pen_down(struct input_dev *input_dev, int id,
|
|
u16 x, u16 y)
|
|
{
|
|
#ifdef SITRONIX_SUPPORT_MT_SLOT
|
|
input_mt_slot(input_dev, id);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
|
|
#else
|
|
input_report_abs(input_dev, ABS_MT_TRACKING_ID, id + 1);
|
|
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, 255);
|
|
input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 255);
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE, 255);
|
|
|
|
input_mt_sync(input_dev);
|
|
#endif
|
|
STX_DEBUG("sitronix: [%d](%d, %d)+", id, x, y);
|
|
}
|
|
|
|
static inline void sitronix_ts_pen_up(struct input_dev *input_dev, int id)
|
|
{
|
|
#ifdef SITRONIX_SUPPORT_MT_SLOT
|
|
input_mt_slot(input_dev, id);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
|
|
#else
|
|
input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
|
|
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 0);
|
|
input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 0);
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE, 0);
|
|
#endif
|
|
STX_DEBUG("[%d]-", id);
|
|
}
|
|
|
|
static inline void sitronix_ts_pen_allup(struct sitronix_ts_data *ts_data)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ts_data->ts_dev_info.max_touches; i++)
|
|
sitronix_ts_pen_up(ts_data->input_dev, i);
|
|
|
|
input_report_key(ts_data->input_dev, BTN_TOUCH, 0);
|
|
input_sync(ts_data->input_dev);
|
|
}
|
|
|
|
static inline void sitronix_ts_handle_sensor_key(
|
|
struct input_dev *input_dev, struct sitronix_sensor_key_t *key_array,
|
|
char *pre_key_status, char cur_key_status, int key_count)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < key_count; i++) {
|
|
if (cur_key_status & (1 << i)) {
|
|
STX_DEBUG("sitronix down now key %d i=%d",
|
|
cur_key_status, i);
|
|
input_report_key(input_dev, key_array[i].code, 1);
|
|
input_sync(input_dev);
|
|
} else {
|
|
if (*pre_key_status & (1 << i)) {
|
|
STX_DEBUG("sitronix sensor key i=[%d] up", i);
|
|
input_report_key(input_dev, key_array[i].code,
|
|
0);
|
|
input_sync(input_dev);
|
|
}
|
|
}
|
|
}
|
|
*pre_key_status = cur_key_status;
|
|
}
|
|
|
|
void stx_irq_enable(void)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (stx_gpts.irq_registered == 0) {
|
|
STX_ERROR("INT have not been registered yet");
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&stx_gpts.irq_lock, flags);
|
|
|
|
if (stx_gpts.irq_is_enable == 0) {
|
|
stx_gpts.irq_is_enable = 1;
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
enable_irq(stx_gpts.client->irq);
|
|
STX_DEBUG("stx_irq_enable, stx_gpts.irq_is_enable=%d",
|
|
stx_gpts.irq_is_enable);
|
|
} else if (stx_gpts.irq_is_enable == 1) {
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
STX_INFO("Touch Eint already enabled!");
|
|
} else {
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
STX_ERROR("Invalid stx_gpts.irq_is_enable %d!",
|
|
stx_gpts.irq_is_enable);
|
|
}
|
|
}
|
|
|
|
void stx_irq_disable(void)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (stx_gpts.irq_registered == 0) {
|
|
STX_ERROR("INT have not been registered yet");
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&stx_gpts.irq_lock, flags);
|
|
|
|
if (stx_gpts.irq_is_enable == 1) {
|
|
stx_gpts.irq_is_enable = 0;
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
disable_irq_nosync(stx_gpts.client->irq);
|
|
STX_DEBUG("stx_irq_disable, stx_gpts.irq_is_enable=%d",
|
|
stx_gpts.irq_is_enable);
|
|
} else if (stx_gpts.irq_is_enable == 0) {
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
STX_INFO("Touch Eint already disabled!");
|
|
} else {
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
STX_ERROR("Invalid stx_gpts.irq_is_enable %d!",
|
|
stx_gpts.irq_is_enable);
|
|
}
|
|
}
|
|
|
|
static void sitronix_ts_work_func(struct work_struct *work)
|
|
{
|
|
int i;
|
|
int ret;
|
|
struct sitronix_ts_data *ts =
|
|
container_of(work, struct sitronix_ts_data, work);
|
|
u16 x, y;
|
|
uint8_t buf[1 + ST_MAX_TOUCHES * PIXEL_DATA_LENGTH_A] = { 0 };
|
|
uint8_t PixelCount = 0;
|
|
static u16 pre_index = 0;
|
|
|
|
STX_FUNC_ENTER();
|
|
|
|
#ifdef SITRONIX_GESTURE
|
|
if (!ts->suspend_state) {
|
|
ret = stx_i2c_read_bytes(FINGERS, buf, 1);
|
|
STX_DEBUG("SITRONIX_GESTURE ret:%d ,value:0x%X", ret, buf[0]);
|
|
buf[0] &= 0xF;
|
|
if ((ret == 1 && buf[0] == G_PALM)) {
|
|
sitronix_gesture_func(ts->input_dev, buf[0]);
|
|
goto exit_data_handled;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef ST_SMART_WAKE_UP
|
|
if (stx_gpts.fsmart_wakeup == 1) {
|
|
if (ts->suspend_state) {
|
|
if (sitronix_swk_func(ts->input_dev) == 0)
|
|
goto exit_data_handled;
|
|
}
|
|
}
|
|
#endif
|
|
ret = stx_i2c_read(stx_gpts.client, buf,
|
|
ts->ts_dev_info.max_touches * 4, 0x11);
|
|
if (ret < 0) {
|
|
STX_ERROR("read finger error (%d)", ret);
|
|
g_i2cErrorCount++;
|
|
goto exit_invalid_data;
|
|
} else {
|
|
g_i2cErrorCount = 0;
|
|
}
|
|
|
|
for (i = 0; i < ts->ts_dev_info.max_touches; i++) {
|
|
if (buf[1 + 4 * i] & 0x80) {
|
|
x = (int)(buf[1 + i * 4] & 0x70) << 4 |
|
|
buf[1 + i * 4 + 1];
|
|
y = (int)(buf[1 + i * 4] & 0x0F) << 8 |
|
|
buf[1 + i * 4 + 2];
|
|
if (sitronix_cases_mode_check(x, y)) {
|
|
PixelCount++;
|
|
STX_DEBUG("SITRONIX touch point: %d (%d,%d)", i,
|
|
x, y);
|
|
sitronix_ts_pen_down(ts->input_dev, i, x, y);
|
|
|
|
pre_index |= 0x01 << i;
|
|
}
|
|
} else if (pre_index & (0x01 << i)) {
|
|
pre_index &= ~(0x01 << i);
|
|
#ifdef SITRONIX_SUPPORT_MT_SLOT
|
|
sitronix_ts_pen_up(ts->input_dev, i);
|
|
#endif
|
|
}
|
|
}
|
|
#ifndef SITRONIX_SUPPORT_MT_SLOT
|
|
if (PixelCount == 0)
|
|
sitronix_ts_pen_up(ts->input_dev, 0);
|
|
#endif
|
|
input_report_key(ts->input_dev, BTN_TOUCH, PixelCount > 0);
|
|
input_sync(ts->input_dev);
|
|
|
|
sitronix_ts_handle_sensor_key(ts->input_dev, sitronix_sensor_key_array,
|
|
&sitronix_sensor_key_status, buf[0],
|
|
(sizeof(sitronix_sensor_key_array) /
|
|
sizeof(struct sitronix_sensor_key_t)));
|
|
|
|
exit_invalid_data:
|
|
if (g_i2cErrorCount >= I2C_CONTINUE_ERROR_CNT) {
|
|
STX_ERROR("I2C abnormal in work_func(), reset it! ");
|
|
st_reset_ic();
|
|
g_i2cErrorCount = 0;
|
|
}
|
|
exit_data_handled:
|
|
stx_irq_enable();
|
|
}
|
|
|
|
static irqreturn_t sitronix_ts_irq_handler(int irq, void *dev_id)
|
|
{
|
|
unsigned long flags;
|
|
struct sitronix_ts_data *ts = dev_id;
|
|
STX_FUNC_ENTER();
|
|
|
|
spin_lock_irqsave(&stx_gpts.irq_lock, flags);
|
|
|
|
if (stx_gpts.irq_is_enable == 0) {
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
STX_ERROR("%s irq_is_enable is %d", __FUNCTION__,
|
|
stx_gpts.irq_is_enable);
|
|
return IRQ_HANDLED;
|
|
}
|
|
stx_gpts.irq_is_enable = 0;
|
|
|
|
spin_unlock_irqrestore(&stx_gpts.irq_lock, flags);
|
|
/* enter EINT handler disable INT, make sure INT is disable when handle touch event including top/bottom half */
|
|
/* use _nosync to avoid deadlock */
|
|
disable_irq_nosync(ts->client->irq);
|
|
#ifdef ST_MONITOR_THREAD
|
|
sitronix_monitor_delay();
|
|
#endif
|
|
schedule_work(&ts->work);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
void st_reset_ic(void)
|
|
{
|
|
STX_DEBUG("%s", __func__);
|
|
gpio_direction_output(stx_gpts.host_if->reset_gpio, 1);
|
|
msleep(10);
|
|
gpio_direction_output(stx_gpts.host_if->reset_gpio, 0);
|
|
msleep(10);
|
|
gpio_direction_output(stx_gpts.host_if->reset_gpio, 1);
|
|
msleep(150);
|
|
}
|
|
|
|
static int sitronix_parse_dt(struct device *dev,
|
|
struct sitronix_ts_platform_data *pdata)
|
|
{
|
|
int rc;
|
|
struct device_node *np = dev->of_node;
|
|
struct property *prop;
|
|
|
|
STX_DEBUG("%s,%d", __FUNCTION__, __LINE__);
|
|
|
|
pdata->name = "sitronix";
|
|
|
|
/* reset, irq gpio info */
|
|
pdata->irq_gpio = of_get_named_gpio_flags(
|
|
np, "sitronix,interrupt-gpios", 0, &pdata->irq_gpio_flags);
|
|
STX_INFO("%s,pdata->irq_gpio=%x", __FUNCTION__, pdata->irq_gpio);
|
|
|
|
if (pdata->irq_gpio < 0)
|
|
return pdata->irq_gpio;
|
|
|
|
pdata->reset_gpio = of_get_named_gpio_flags(
|
|
np, "sitronix,reset-gpios", 0, &pdata->reset_gpio_flags);
|
|
STX_INFO("%s,pdata->reset_gpio=%x", __FUNCTION__, pdata->reset_gpio);
|
|
|
|
if (pdata->reset_gpio < 0)
|
|
return pdata->reset_gpio;
|
|
|
|
prop = of_find_property(np, "sitronix,button-map", NULL);
|
|
if (prop) {
|
|
pdata->num_button = prop->length / sizeof(u32);
|
|
if (pdata->num_button > MAX_BUTTONS)
|
|
return -EINVAL;
|
|
|
|
rc = of_property_read_u32_array(np, "sitronix,button-map",
|
|
pdata->button_map,
|
|
pdata->num_button);
|
|
if (rc) {
|
|
STX_ERROR("Unable to read key codes");
|
|
return rc;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#ifdef STX_POWER_SUPPLY_EN
|
|
static int sitronix_power_on(struct sitronix_ts_data *ts)
|
|
{
|
|
int ret = 0;
|
|
if (ts->vdd_ana) {
|
|
ret = regulator_enable(ts->vdd_ana);
|
|
if (ret) {
|
|
STX_ERROR("Regulator vdd enable failed ret =%d", ret);
|
|
}
|
|
}
|
|
if (ts->vcc_i2c) {
|
|
ret = regulator_enable(ts->vcc_i2c);
|
|
if (ret) {
|
|
STX_ERROR("Regulator vcc_i2c enable failed ret =%d",
|
|
ret);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int sitronix_power_off(struct sitronix_ts_data *ts)
|
|
{
|
|
int ret = 0;
|
|
if (ts->vcc_i2c) {
|
|
ret = regulator_disable(ts->vcc_i2c);
|
|
if (ret) {
|
|
STX_ERROR("Regulator vcc_i2c disable failed ret =%d",
|
|
ret);
|
|
}
|
|
}
|
|
if (ts->vdd_ana) {
|
|
ret = regulator_disable(ts->vdd_ana);
|
|
if (ret) {
|
|
STX_ERROR("Regulator vdd_ana disable failed ret =%d",
|
|
ret);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int sitronix_power_init(struct sitronix_ts_data *ts)
|
|
{
|
|
int ret;
|
|
|
|
ts->vdd_ana = regulator_get(&ts->client->dev, "vdd_ana");
|
|
if (IS_ERR(ts->vdd_ana)) {
|
|
ts->vdd_ana = NULL;
|
|
ret = PTR_ERR(ts->vdd_ana);
|
|
STX_ERROR("get vdd_ana regulator failed,ret=%d", ret);
|
|
return ret;
|
|
}
|
|
ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc_i2c");
|
|
if (IS_ERR(ts->vcc_i2c)) {
|
|
ts->vcc_i2c = NULL;
|
|
ret = PTR_ERR(ts->vcc_i2c);
|
|
STX_ERROR("get vcc_i2c regulator failed,ret=%d", ret);
|
|
goto err_get_vcc;
|
|
}
|
|
STX_FUNC_EXIT();
|
|
return 0;
|
|
|
|
err_get_vcc:
|
|
regulator_put(ts->vdd_ana);
|
|
STX_FUNC_EXIT();
|
|
return ret;
|
|
}
|
|
|
|
static int sitronix_power_deinit(struct sitronix_ts_data *ts)
|
|
{
|
|
if (ts->vdd_ana)
|
|
regulator_put(ts->vdd_ana);
|
|
if (ts->vcc_i2c)
|
|
regulator_put(ts->vcc_i2c);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void sitronix_ts_input_set_params(struct sitronix_ts_data *ts_data)
|
|
{
|
|
#ifdef SITRONIX_SUPPORT_MT_SLOT
|
|
input_mt_init_slots(ts_data->input_dev,
|
|
ts_data->ts_dev_info.max_touches, INPUT_MT_DIRECT);
|
|
#else
|
|
set_bit(ABS_MT_TOUCH_MAJOR, ts_data->input_dev->absbit);
|
|
set_bit(ABS_MT_TOUCH_MINOR, ts_data->input_dev->absbit);
|
|
set_bit(ABS_MT_POSITION_X, ts_data->input_dev->absbit);
|
|
set_bit(ABS_MT_POSITION_Y, ts_data->input_dev->absbit);
|
|
set_bit(ABS_MT_BLOB_ID, ts_data->input_dev->absbit);
|
|
set_bit(ABS_MT_TRACKING_ID, ts_data->input_dev->absbit);
|
|
set_bit(INPUT_PROP_DIRECT, ts_data->input_dev->propbit);
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_TRACKING_ID, 0,
|
|
ts_data->ts_dev_info.max_touches, 0, 0);
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0,
|
|
0);
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_TOUCH_MINOR, 0, 255, 0,
|
|
0);
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
|
#endif
|
|
|
|
#ifndef SITRONIX_SWAP_XY
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_POSITION_X, 0,
|
|
(ts_data->ts_dev_info.x_res - 1), 0, 0);
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_POSITION_Y, 0,
|
|
(ts_data->ts_dev_info.y_res - 1), 0, 0);
|
|
#else
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_POSITION_X, 0,
|
|
(ts_data->ts_dev_info.y_res - 1), 0, 0);
|
|
input_set_abs_params(ts_data->input_dev, ABS_MT_POSITION_Y, 0,
|
|
(ts_data->ts_dev_info.x_res - 1), 0, 0);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
static int sitronix_ts_input_dev_init(struct sitronix_ts_data *ts_data)
|
|
{
|
|
int ret = 0;
|
|
int i;
|
|
|
|
ts_data->input_dev = input_allocate_device();
|
|
if (ts_data->input_dev == NULL) {
|
|
STX_ERROR("%s: Can not allocate input device!", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ts_data->input_dev->name =
|
|
SITRONIX_I2C_TOUCH_MT_INPUT_DEV_NAME; //ts_data->name;
|
|
ts_data->input_dev->id.bustype = BUS_I2C;
|
|
ts_data->input_dev->id.product = 0;
|
|
ts_data->input_dev->id.version = 0;
|
|
ts_data->input_dev->dev.parent = &ts_data->client->dev;
|
|
input_set_drvdata(ts_data->input_dev, ts_data);
|
|
|
|
set_bit(EV_KEY, ts_data->input_dev->evbit);
|
|
set_bit(EV_ABS, ts_data->input_dev->evbit);
|
|
set_bit(BTN_TOUCH, ts_data->input_dev->keybit);
|
|
|
|
#ifdef ST_SMART_WAKE_UP
|
|
st_gesture_init();
|
|
#endif
|
|
for (i = 0; i < 3; i++) {
|
|
set_bit(sitronix_sensor_key_array[i].code,
|
|
ts_data->input_dev->keybit);
|
|
}
|
|
|
|
sitronix_ts_input_set_params(ts_data);
|
|
|
|
ret = input_register_device(ts_data->input_dev);
|
|
if (ret) {
|
|
STX_ERROR("%s: Failed to register input device", __func__);
|
|
goto err_register_input;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_register_input:
|
|
input_free_device(ts_data->input_dev);
|
|
ts_data->input_dev = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Name: stx_gpio_configure
|
|
* Brief: IRQ & RESET GPIO INIT
|
|
* Input:
|
|
* Output:
|
|
* Return: return 0 if succuss
|
|
*****************************************************************************/
|
|
static int stx_gpio_configure(struct sitronix_ts_data *data)
|
|
{
|
|
int ret = 0;
|
|
|
|
STX_FUNC_ENTER();
|
|
/* request irq gpio */
|
|
if (gpio_is_valid(data->host_if->irq_gpio)) {
|
|
ret = gpio_request(data->host_if->irq_gpio, "stx_irq_gpio");
|
|
if (ret) {
|
|
STX_ERROR("[GPIO]irq gpio request failed");
|
|
goto err_irq_gpio_req;
|
|
}
|
|
|
|
ret = gpio_direction_input(data->host_if->irq_gpio);
|
|
if (ret) {
|
|
STX_ERROR("[GPIO]set_direction for irq gpio failed");
|
|
goto err_irq_gpio_dir;
|
|
}
|
|
}
|
|
|
|
/* request reset gpio */
|
|
if (gpio_is_valid(data->host_if->reset_gpio)) {
|
|
ret = gpio_request(data->host_if->reset_gpio, "stx_reset_gpio");
|
|
if (ret) {
|
|
STX_ERROR("[GPIO]reset gpio request failed");
|
|
goto err_irq_gpio_dir;
|
|
}
|
|
|
|
ret = gpio_direction_output(data->host_if->reset_gpio, 1);
|
|
if (ret) {
|
|
STX_ERROR("[GPIO]set_direction for reset gpio failed");
|
|
goto err_reset_gpio_dir;
|
|
}
|
|
msleep(10);
|
|
gpio_direction_output(data->host_if->reset_gpio, 0);
|
|
msleep(10);
|
|
gpio_direction_output(data->host_if->reset_gpio, 1);
|
|
msleep(150);
|
|
}
|
|
|
|
STX_FUNC_EXIT();
|
|
return 0;
|
|
|
|
err_reset_gpio_dir:
|
|
if (gpio_is_valid(data->host_if->reset_gpio)) {
|
|
gpio_free(data->host_if->reset_gpio);
|
|
STX_ERROR("Reset GPIO Free\n");
|
|
}
|
|
err_irq_gpio_dir:
|
|
if (gpio_is_valid(data->host_if->irq_gpio)) {
|
|
gpio_free(data->host_if->irq_gpio);
|
|
STX_ERROR("Irq GPIO Free\n");
|
|
}
|
|
err_irq_gpio_req:
|
|
STX_FUNC_EXIT();
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t cf11xx_tp_info(struct file *file, char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
printk("cf11xx_tp_info..........\n");
|
|
return 0;
|
|
}
|
|
|
|
static void cf11xx_resume(void)
|
|
{
|
|
//gpio_direction_output(stx_gpts.host_if->reset_gpio, 0);
|
|
//msleep(10);
|
|
//gpio_direction_output(stx_gpts.host_if->reset_gpio, 1);
|
|
//msleep(150);
|
|
STX_INFO("cf11xx tsresume client %x %x\n", stx_gpts.client,
|
|
&stx_gpts);
|
|
//stx_irq_enable();
|
|
sitronix_ts_resume(stx_gpts.client);
|
|
}
|
|
|
|
static void cf11xx_suspend(void)
|
|
{
|
|
STX_INFO("cf11xx ts sleep client %x %x\n", stx_gpts.client,
|
|
&stx_gpts);
|
|
//stx_irq_disable();
|
|
sitronix_ts_suspend(stx_gpts.client);
|
|
}
|
|
|
|
static ssize_t cf11xx_power_enable(struct file *filp, const char __user *buff,
|
|
size_t len, loff_t *off)
|
|
{
|
|
int mode;
|
|
char buffer[64];
|
|
|
|
if (len >= sizeof(buffer))
|
|
return -1;
|
|
|
|
if (copy_from_user(buffer, buff, len))
|
|
return -1;
|
|
|
|
buffer[len] = '\0';
|
|
|
|
kstrtouint(buffer, 10, &mode);
|
|
if (mode == 1) {
|
|
//sitronix_pm_resume(struct device *dev)
|
|
cf11xx_resume();
|
|
} else if (mode == 0) {
|
|
//sitronix_pm_suspend(struct device *dev)
|
|
cf11xx_suspend();
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static const struct file_operations tp_proc_info_fops = {
|
|
.read = cf11xx_tp_info,
|
|
.write = cf11xx_power_enable,
|
|
};
|
|
|
|
static int sitronix_ts_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int ret = 0;
|
|
uint8_t err_code = 0;
|
|
uint8_t dev_status = 0;
|
|
struct sitronix_ts_platform_data *pdata;
|
|
STX_FUNC_ENTER();
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
STX_ERROR("sitronix Failed check I2C functionality");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (client->dev.of_node) {
|
|
pdata = devm_kzalloc(&client->dev,
|
|
sizeof(struct sitronix_ts_platform_data),
|
|
GFP_KERNEL);
|
|
if (!pdata) {
|
|
STX_ERROR(
|
|
"Failed to allocate memory for platform data");
|
|
return -ENOMEM;
|
|
}
|
|
ret = sitronix_parse_dt(&client->dev, pdata);
|
|
if (ret)
|
|
STX_ERROR("[DTS]DT parsing failed");
|
|
} else {
|
|
pdata = client->dev.platform_data;
|
|
}
|
|
|
|
if (!pdata) {
|
|
STX_ERROR("no ts platform data found");
|
|
return -EINVAL;
|
|
}
|
|
|
|
stx_gpts.host_if = pdata;
|
|
stx_gpts.client = client;
|
|
i2c_set_clientdata(client, &stx_gpts);
|
|
|
|
stx_gpts.is_upgrading = false;
|
|
stx_gpts.is_testing = false;
|
|
stx_gpts.suspend_state = 0;
|
|
stx_gpts.irq_is_enable = 1;
|
|
stx_gpts.fsmart_wakeup = 1;
|
|
stx_gpts.rawTestResult = 1;
|
|
stx_gpts.irq_registered = 0;
|
|
stx_gpts.fapp_in = 0;
|
|
stx_gpts.glove_mode = false;
|
|
stx_gpts.cases_mode = false;
|
|
#ifdef STX_POWER_SUPPLY_EN
|
|
ret = sitronix_power_init(&stx_gpts);
|
|
if (ret) {
|
|
STX_ERROR("sitronix failed get regulator");
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
|
|
ret = sitronix_power_on(&stx_gpts);
|
|
if (ret) {
|
|
STX_ERROR("regulator failed power on device");
|
|
ret = -EINVAL;
|
|
goto exit_deinit_power;
|
|
}
|
|
#endif
|
|
|
|
stx_gpio_configure(&stx_gpts);
|
|
|
|
#ifdef ST_UPGRADE_FIRMWARE
|
|
//kthread_run(st_upgrade_fw_handler, "Sitronix", "sitronix_update");
|
|
st_upgrade_fw();
|
|
#endif
|
|
|
|
ret = st_get_dev_status(client, &err_code, &dev_status);
|
|
if ((ret < 0) || (dev_status == 0x6) ||
|
|
((err_code == 0x8) && (dev_status == 0x0))) {
|
|
ret = -EPERM;
|
|
goto err_device_info_error;
|
|
}
|
|
|
|
ret = st_get_touch_info(&stx_gpts);
|
|
if (ret < 0)
|
|
goto err_device_info_error;
|
|
|
|
mutex_init(&stx_gpts.dev_mutex);
|
|
spin_lock_init(&stx_gpts.irq_lock);
|
|
|
|
INIT_WORK(&(stx_gpts.work), sitronix_ts_work_func);
|
|
|
|
#ifdef ST_MONITOR_THREAD
|
|
sitronix_monitor_delay();
|
|
sitronix_monitor_start();
|
|
#endif
|
|
|
|
if ((ret = sitronix_ts_input_dev_init(&stx_gpts))) {
|
|
STX_ERROR("%s: Failed to set up input device", __func__);
|
|
ret = -EINVAL;
|
|
goto err_input_register_device_failed;
|
|
}
|
|
|
|
stx_gpts.client->irq = gpio_to_irq(stx_gpts.host_if->irq_gpio);
|
|
STX_INFO("irq num:%d", stx_gpts.client->irq);
|
|
if (stx_gpts.client->irq) {
|
|
STX_INFO("irq = %d", stx_gpts.client->irq);
|
|
stx_gpts.irq_flags =
|
|
IRQF_TRIGGER_FALLING; // | IRQF_ONESHOT;// IRQF_NO_SUSPEND;
|
|
//ret = request_threaded_irq(stx_gpts.client->irq, NULL, sitronix_ts_irq_handler, stx_gpts.irq_flags, client->name, &stx_gpts);
|
|
ret = request_irq(stx_gpts.client->irq, sitronix_ts_irq_handler,
|
|
stx_gpts.irq_flags, client->name, &stx_gpts);
|
|
if (ret < 0) {
|
|
STX_ERROR("request_irq failed");
|
|
goto err_request_irq_failed;
|
|
}
|
|
stx_gpts.irq_registered = 1;
|
|
}
|
|
|
|
proc_create("sprocomm_tpInfo", 0444, NULL, &tp_proc_info_fops);
|
|
|
|
ret = st_create_sysfs(client);
|
|
if (ret < 0) {
|
|
STX_ERROR("create sysfs node fail");
|
|
}
|
|
|
|
#ifdef ST_DEVICE_NODE
|
|
ret = st_dev_node_init();
|
|
if (ret < 0)
|
|
STX_ERROR("create sitronix node fail");
|
|
#endif
|
|
|
|
STX_DEBUG("%s,line=%d", __FUNCTION__, __LINE__);
|
|
|
|
#if defined(CONFIG_FB)
|
|
stx_gpts.fb_notif.notifier_call = fb_notifier_callback;
|
|
ret = fb_register_client(&stx_gpts.fb_notif);
|
|
|
|
if (ret)
|
|
STX_ERROR("Unable to register fb_notifier: %d", ret);
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
stx_gpts.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
|
|
stx_gpts.early_suspend.suspend = sitronix_ts_early_suspend;
|
|
stx_gpts.early_suspend.resume = sitronix_ts_late_resume;
|
|
register_early_suspend(&stx_gpts.early_suspend);
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
err_request_irq_failed:
|
|
if (stx_gpts.input_dev) {
|
|
input_free_device(stx_gpts.input_dev);
|
|
stx_gpts.input_dev = NULL;
|
|
}
|
|
err_input_register_device_failed:
|
|
#ifdef ST_MONITOR_THREAD
|
|
sitronix_monitor_stop();
|
|
#endif
|
|
err_device_info_error:
|
|
if (gpio_is_valid(stx_gpts.host_if->reset_gpio))
|
|
gpio_free(stx_gpts.host_if->reset_gpio);
|
|
if (gpio_is_valid(stx_gpts.host_if->irq_gpio))
|
|
gpio_free(stx_gpts.host_if->irq_gpio);
|
|
#ifdef STX_POWER_SUPPLY_EN
|
|
sitronix_power_off(&stx_gpts);
|
|
exit_deinit_power:
|
|
sitronix_power_deinit(&stx_gpts);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int sitronix_ts_remove(struct i2c_client *client)
|
|
{
|
|
struct sitronix_ts_data *ts = i2c_get_clientdata(client);
|
|
#if defined(CONFIG_FB)
|
|
if (fb_unregister_client(&ts->fb_notif))
|
|
STX_ERROR("Error occurred while unregistering fb_notifier.");
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
unregister_early_suspend(&ts->early_suspend);
|
|
#endif
|
|
|
|
#ifdef ST_DEVICE_NODE
|
|
st_dev_node_exit();
|
|
#endif
|
|
|
|
#ifdef ST_MONITOR_THREAD
|
|
sitronix_monitor_stop();
|
|
#endif
|
|
#ifdef STX_POWER_SUPPLY_EN
|
|
sitronix_power_off(&stx_gpts);
|
|
sitronix_power_deinit(&stx_gpts);
|
|
#endif
|
|
i2c_set_clientdata(client, NULL);
|
|
free_irq(client->irq, ts);
|
|
if (ts->input_dev)
|
|
input_unregister_device(ts->input_dev);
|
|
|
|
mutex_destroy(&stx_gpts.dev_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static int sitronix_ts_suspend(struct i2c_client *client)
|
|
{
|
|
struct sitronix_ts_data *ts = i2c_get_clientdata(client);
|
|
STX_FUNC_ENTER();
|
|
STX_INFO("suspend client %x %x %x %x\n", stx_gpts.client, client,
|
|
&stx_gpts, ts);
|
|
|
|
if (ts->fapp_in) {
|
|
STX_INFO("%s fapp_in = %d", __func__, ts->fapp_in);
|
|
return 0;
|
|
}
|
|
|
|
if (ts->suspend_state) {
|
|
STX_ERROR("Already in suspend state");
|
|
return 0;
|
|
}
|
|
|
|
#ifdef ST_MONITOR_THREAD //hfst1
|
|
sitronix_monitor_stop();
|
|
#endif
|
|
|
|
#ifdef ST_SMART_WAKE_UP
|
|
if (stx_gpts.fsmart_wakeup == 1) {
|
|
sitronix_swk_enable(); //hfst001
|
|
st_power_down(ts);
|
|
enable_irq_wake(stx_gpts.client->irq); //hfst
|
|
sitronix_ts_pen_allup(ts);
|
|
ts->suspend_state = 1;
|
|
STX_FUNC_EXIT();
|
|
return 0;
|
|
}
|
|
#endif
|
|
stx_irq_disable();
|
|
sitronix_ts_pen_allup(ts);
|
|
ts->suspend_state = 1;
|
|
|
|
st_power_down(ts);
|
|
STX_FUNC_EXIT();
|
|
return 0;
|
|
}
|
|
|
|
static int sitronix_ts_resume(struct i2c_client *client)
|
|
{
|
|
struct sitronix_ts_data *ts = i2c_get_clientdata(client);
|
|
STX_FUNC_ENTER();
|
|
|
|
STX_INFO("resume client %x %x %x %x\n", stx_gpts.client, client,
|
|
&stx_gpts, ts);
|
|
if (ts->fapp_in) {
|
|
STX_INFO("%s fapp_in = %d", __func__, ts->fapp_in);
|
|
return 0;
|
|
}
|
|
|
|
if (!ts->suspend_state) {
|
|
STX_ERROR("Already in resume state");
|
|
return 0;
|
|
}
|
|
|
|
st_power_up(ts);
|
|
//stx_gpio_configure(ts); //GPIO失败
|
|
//gpio_direction_output(stx_gpts.host_if->reset_gpio, 0);
|
|
//msleep(10);
|
|
//gpio_direction_output(stx_gpts.host_if->reset_gpio, 1);
|
|
//msleep(150);
|
|
|
|
//st_power_up(ts);
|
|
#ifdef ST_MONITOR_THREAD
|
|
sitronix_monitor_start();
|
|
#endif
|
|
|
|
#ifdef ST_SMART_WAKE_UP
|
|
if (stx_gpts.fsmart_wakeup == 1) {
|
|
sitronix_swk_disable();
|
|
disable_irq_wake(stx_gpts.client->irq); //hfst
|
|
ts->suspend_state = 0;
|
|
STX_FUNC_EXIT();
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
ts->suspend_state = 0;
|
|
stx_irq_enable();
|
|
|
|
STX_FUNC_EXIT();
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_FB)
|
|
|
|
/*******************************************************************************
|
|
* Name: fb_notifier_callback
|
|
* Brief:
|
|
* Input:
|
|
* Output:
|
|
* Return:
|
|
*******************************************************************************/
|
|
static int fb_notifier_callback(struct notifier_block *self,
|
|
unsigned long event, void *data)
|
|
{
|
|
struct fb_event *evdata = data;
|
|
int *blank;
|
|
struct sitronix_ts_data *sitronix_data =
|
|
container_of(self, struct sitronix_ts_data, fb_notif);
|
|
|
|
if (evdata && evdata->data && event == FB_EVENT_BLANK &&
|
|
sitronix_data && sitronix_data->client) {
|
|
blank = evdata->data;
|
|
if (*blank == FB_BLANK_UNBLANK)
|
|
sitronix_ts_resume(sitronix_data->client);
|
|
else if (*blank == FB_BLANK_POWERDOWN)
|
|
sitronix_ts_suspend(sitronix_data->client);
|
|
}
|
|
return 0;
|
|
}
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
static void sitronix_ts_early_suspend(struct early_suspend *h)
|
|
{
|
|
struct sitronix_ts_data *ts;
|
|
STX_DEBUG("%s", __FUNCTION__);
|
|
ts = container_of(h, struct sitronix_ts_data, early_suspend);
|
|
sitronix_ts_suspend(ts->client);
|
|
}
|
|
|
|
static void sitronix_ts_late_resume(struct early_suspend *h)
|
|
{
|
|
struct sitronix_ts_data *ts;
|
|
STX_DEBUG("%s", __FUNCTION__);
|
|
ts = container_of(h, struct sitronix_ts_data, early_suspend);
|
|
sitronix_ts_resume(ts->client);
|
|
}
|
|
#endif
|
|
|
|
static const struct i2c_device_id sitronix_ts_id[] = {
|
|
{ SITRONIX_I2C_TOUCH_DRV_NAME, 0 },
|
|
{}
|
|
};
|
|
|
|
static struct of_device_id sitronix_match_table[] = {
|
|
{
|
|
.compatible = "sitronix,st1633i",
|
|
},
|
|
{
|
|
.compatible = "sitronix,cf1216",
|
|
},
|
|
{},
|
|
};
|
|
|
|
#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
|
|
static int sitronix_pm_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
sitronix_ts_suspend(client);
|
|
return 0;
|
|
}
|
|
static int sitronix_pm_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
sitronix_ts_resume(client);
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops sitronix_ts_dev_pm_ops = {
|
|
.suspend = sitronix_pm_suspend,
|
|
.resume = sitronix_pm_resume,
|
|
};
|
|
#endif
|
|
static struct i2c_driver sitronix_ts_driver = {
|
|
.probe = sitronix_ts_probe,
|
|
.remove = sitronix_ts_remove,
|
|
.id_table = sitronix_ts_id,
|
|
.driver = {
|
|
.name = SITRONIX_I2C_TOUCH_DRV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = sitronix_match_table,
|
|
#if CONFIG_PM
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
static int __init sitronix_ts_init(void)
|
|
{
|
|
s32 ret_iic = -2;
|
|
|
|
STX_DEBUG("Sitronix touch driver %d.%d.%d", DRIVER_MAJOR, DRIVER_MINOR,
|
|
DRIVER_PATCHLEVEL);
|
|
|
|
ret_iic = i2c_add_driver(&sitronix_ts_driver);
|
|
STX_INFO("IIC ADD DRIVER %s,ret_iic=%d", __FUNCTION__, ret_iic);
|
|
|
|
return ret_iic;
|
|
}
|
|
|
|
static void __exit sitronix_ts_exit(void)
|
|
{
|
|
i2c_del_driver(&sitronix_ts_driver);
|
|
}
|
|
|
|
module_init(sitronix_ts_init);
|
|
module_exit(sitronix_ts_exit);
|
|
|
|
MODULE_DESCRIPTION("Sitronix Multi-Touch Driver");
|
|
MODULE_LICENSE("GPL");
|