根据TP的厂商和型号写相应的代码,不过大致都是差不多的,话不多说。
来吧!展示!上驱动代码:
/* drivers/input/touchscreen/gt9x.c*/
#include <linux/regulator/consumer.h>
#include "gt9xx.h"
#include <linux/of_gpio.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/input/mt.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#define GOODIX_DEV_NAME "Goodix-CTP"
#define CFG_MAX_TOUCH_POINTS 5
#define GOODIX_COORDS_ARR_SIZE 4
#define MAX_BUTTONS 4
#define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0]))
#define GOODIX_VTG_MIN_UV 2600000
#define GOODIX_VTG_MAX_UV 3300000
#define GOODIX_I2C_VTG_MIN_UV 1800000
#define GOODIX_I2C_VTG_MAX_UV 1800000
#define GOODIX_VDD_LOAD_MIN_UA 0
#define GOODIX_VDD_LOAD_MAX_UA 10000
#define GOODIX_VIO_LOAD_MIN_UA 0
#define GOODIX_VIO_LOAD_MAX_UA 10000
//#define RESET_DELAY_T3_US 200 /* T3: > 100us */
#define RESET_DELAY_T4 20 /* T4: > 5ms */
#define PHY_BUF_SIZE 32
#define PROP_NAME_SIZE 24
#define GTP_MAX_TOUCH 5
#define GTP_ESD_CHECK_CIRCLE_MS 2000
static void gtp_int_sync(struct goodix_ts_data *ts, int ms);
static int gtp_i2c_test(struct i2c_client *client);
static int goodix_power_off(struct goodix_ts_data *ts);
static int goodix_power_on(struct goodix_ts_data *ts);
#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data);
static int goodix_ts_suspend(struct device *dev);
static int goodix_ts_resume(struct device *dev);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
static void goodix_ts_early_suspend(struct early_suspend *h);
static void goodix_ts_late_resume(struct early_suspend *h);
#endif
#if GTP_ESD_PROTECT
static struct delayed_work gtp_esd_check_work;
static struct workqueue_struct *gtp_esd_check_workqueue;
static void gtp_esd_check_func(struct work_struct *work);
static int gtp_init_ext_watchdog(struct i2c_client *client);
#endif
enum doze {
DOZE_DISABLED = 0,
DOZE_ENABLED = 1,
DOZE_WAKEUP = 2,
};
static enum doze doze_status = DOZE_DISABLED;
static s8 gtp_enter_doze(struct goodix_ts_data *ts);
bool init_done;
static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */
u8 grp_cfg_version;
struct i2c_client *i2c_connect_client;
#define GTP_DEBUGFS_DIR "ts_debug"
#define GTP_DEBUGFS_FILE_SUSPEND "suspend"
#define GTP_DEBUGFS_FILE_DATA "data"
#define GTP_DEBUGFS_FILE_ADDR "addr"
/*******************************************************
Function:
Read data from the i2c slave device.
Input:
client: i2c device.
buf[0~1]: read start address.
buf[2~len-1]: read data buffer.
len: GTP_ADDR_LENGTH + read bytes count
Output:
numbers of i2c_msgs to transfer:
2: succeed, otherwise: failed
*********************************************************/
int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
int ret = -EIO;
u8 retries;
struct i2c_msg msgs[2] = {
{
.flags = !I2C_M_RD,
.addr = client->addr,
.len = GTP_ADDR_LENGTH,
.buf = &buf[0],
},
{
.flags = I2C_M_RD,
.addr = client->addr,
.len = len - GTP_ADDR_LENGTH,
.buf = &buf[GTP_ADDR_LENGTH],
},
};
for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2)
break;
// dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
}
if (retries == GTP_I2C_RETRY_5) {
dev_err(&client->dev, "I2C retry times: %d\n", GTP_I2C_RETRY_5);
if (ts->pdata->slide_wakeup)
/* reset chip would quit doze mode */
if (DOZE_ENABLED == doze_status)
return ret;
if (init_done)
gtp_reset_guitar(ts, 10);
else
dev_warn(&client->dev,
"gtp_reset_guitar exit init_done=%d:\n",
init_done);
}
return ret;
}
/*******************************************************
Function:
Write data to the i2c slave device.
Input:
client: i2c device.
buf[0~1]: write start address.
buf[2~len-1]: data buffer
len: GTP_ADDR_LENGTH + write bytes count
Output:
numbers of i2c_msgs to transfer:
1: succeed, otherwise: failed
*********************************************************/
int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
int ret = -EIO;
u8 retries;
struct i2c_msg msg = {
.flags = !I2C_M_RD,
.addr = client->addr,
.len = len,
.buf = buf,
};
for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)
break;
dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
}
if ((retries == GTP_I2C_RETRY_5)) {
if (ts->pdata->slide_wakeup)
if (DOZE_ENABLED == doze_status)
return ret;
if (init_done)
gtp_reset_guitar(ts, 10);
else
dev_warn(&client->dev,
"gtp_reset_guitar exit init_done=%d:\n",
init_done);
}
return ret;
}
/*******************************************************
Function:
i2c read twice, compare the results
Input:
client: i2c device
addr: operate address
rxbuf: read data to store, if compare successful
len: bytes to read
Output:
FAIL: read failed
SUCCESS: read successful
*********************************************************/
int gtp_i2c_read_dbl_check(struct i2c_client *client,
u16 addr, u8 *rxbuf, int len)
{
u8 buf[16] = {
0};
u8 confirm_buf[16] = {
0};
u8 retry = 0;
while (retry++ < GTP_I2C_RETRY_3) {
memset(buf, 0xAA, 16);
buf[0] = (u8)(addr >> 8);
buf[1] = (u8)(addr & 0xFF);
gtp_i2c_read(client, buf, len + 2);
memset(confirm_buf, 0xAB, 16);
confirm_buf[0] = (u8)(addr >> 8);
confirm_buf[1] = (u8)(addr & 0xFF);
gtp_i2c_read(client, confirm_buf, len + 2);
if (!memcmp(buf, confirm_buf, len + 2))
break;
}
if (retry < GTP_I2C_RETRY_3) {
memcpy(rxbuf, confirm_buf + 2, len);
return SUCCESS;
} else {
dev_err(&client->dev,
"i2c read 0x%04X, %d bytes, double check failed!",
addr, len);
return FAIL;
}
}
/*******************************************************
Function:
Send config data.
Input:
client: i2c device.
Output:
result of i2c write operation.
> 0: succeed, otherwise: failed
*********************************************************/
int gtp_send_cfg(struct goodix_ts_data *ts)
{
int ret = 0;
int retry;
if (ts->pdata->driver_send_cfg) {
if (ts->fixed_cfg) {
dev_dbg(&ts->client->dev,
"Ic fixed config, no config sent!");
ret = 2;
} else {
for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) {
ret = gtp_i2c_write(ts->client,
ts->config_data,
GTP_CONFIG_MAX_LENGTH +
GTP_ADDR_LENGTH);
if (ret > 0)
break;
}
}
}
return ret;
}
/*******************************************************
Function:
Disable irq function
Input:
ts: goodix i2c_client private data
Output:
None.
*********************************************************/
void gtp_irq_disable(struct goodix_ts_data *ts)
{
unsigned long irqflags;
spin_lock_irqsave(&ts->irq_lock, irqflags);
if (!ts->irq_is_disabled) {
ts->irq_is_disabled = true;
disable_irq_nosync(ts->client->irq);
}
spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}
/*******************************************************
Function:
Enable irq function
Input:
ts: goodix i2c_client private data
Output:
None.
*********************************************************/
void gtp_irq_enable(struct goodix_ts_data *ts)
{
unsigned long irqflags = 0;
spin_lock_irqsave(&ts->irq_lock, irqflags);
if (ts->irq_is_disabled) {
enable_irq(ts->client->irq);
ts->irq_is_disabled = false;
}
spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}
/*******************************************************
Function:
Report touch point event
Input:
ts: goodix i2c_client private data
id: trackId
x: input x coordinate
y: input y coordinate
w: input pressure
Output:
None.
*********************************************************/
static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y,
int w)
{
if (ts->pdata->change_x2y)
swap(x, y);
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
}
/*******************************************************
Function:
Report touch release event
Input:
ts: goodix i2c_client private data
Output:
None.
*********************************************************/
static void gtp_touch_up(struct goodix_ts_data *ts, int id)
{
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
}
/*******************************************************
Function:
Goodix touchscreen work function
Input:
work: work struct of goodix_workqueue
Output:
None.
*********************************************************/
static void goodix_ts_work_func(struct work_struct *work)
{
u8 end_cmd[3] = {
GTP_READ_COOR_ADDR >> 8,
GTP_READ_COOR_ADDR & 0xFF, 0};
u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = {
GTP_READ_COOR_ADDR >> 8,
GTP_READ_COOR_ADDR & 0xFF};
u8 touch_num = 0;
u8 finger = 0;
static u16 pre_touch;
static u8 pre_key;
static u8 pre_pen;
u8 key_value = 0;
u8 *coor_data = NULL;
s32 input_x = 0;
s32 input_y = 0;
s32 input_w = 0;
s32 id = 0;
s32 i = 0;
int ret = -1;
struct goodix_ts_data *ts = NULL;
u8 doze_buf[3] = {
0x81, 0x4B};
ts = container_of(work, struct goodix_ts_data, work);
#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
if (ts->enter_update)
return;
#endif
if (ts->pdata->slide_wakeup) {
if (DOZE_ENABLED == doze_status) {
ret = gtp_i2c_read(ts->client, doze_buf, 3);
if (ret > 0) {
if (doze_buf[2] == 0xAA) {
dev_dbg(&ts->client->dev,
"Slide(0xAA) To Light up the screen!");
doze_status = DOZE_WAKEUP;
input_report_key(
ts->input_dev, KEY_POWER, 1);
input_sync(ts->input_dev);
input_report_key(
ts->input_dev, KEY_POWER, 0);
input_sync(ts->input_dev);
/* clear 0x814B */
doze_buf[2] = 0x00;
gtp_i2c_write(ts->client, doze_buf, 3);
} else if (doze_buf[2] == 0xBB) {
dev_dbg(&ts->client->dev,
"Slide(0xBB) To Light up the screen!");
doze_status = DOZE_WAKEUP;
input_report_key(ts->input_dev,
KEY_POWER, 1);
input_sync(ts->input_dev);
input_report_key(ts->input_dev,
KEY_POWER, 0);
input_sync(ts->input_dev);
/* clear 0x814B*/
doze_buf[2] = 0x00;
gtp_i2c_write(ts->client, doze_buf, 3);
} else if (0xC0 == (doze_buf[2] & 0xC0)) {
dev_dbg(&ts->client->dev,
"double click to light up the screen!");
doze_status = DOZE_WAKEUP;
input_report_key(ts->input_dev,
KEY_POWER, 1);
input_sync(ts->input_dev);
input_report_key(ts->input_dev,
KEY_POWER, 0);
input_sync(ts->input_dev);
/* clear 0x814B */
doze_buf[2] = 0x00;
gtp_i2c_write(ts->client, doze_buf, 3);
} else {
gtp_enter_doze(ts);
}
}
if (ts->use_irq)
gtp_irq_enable(ts);
return;
}
}
ret = gtp_i2c_read(ts->client, point_data, 12);
if (ret < 0) {
dev_err(&ts->client->dev,
"I2C transfer error. errno:%d\n ", ret);
goto exit_work_func;
}
finger = point_data[GTP_ADDR_LENGTH];
if ((finger & 0x80) == 0)
goto exit_work_func;
touch_num = finger & 0x0f;
if (touch_num > GTP_MAX_TOUCH)
goto exit_work_func;
if (touch_num > 1) {
u8 buf[8 * GTP_MAX_TOUCH] = {
(GTP_READ_COOR_ADDR + 10) >> 8,
(GTP_READ_COOR_ADDR + 10) & 0xff };
ret = gtp_i2c_read(ts->client, buf,
2 + 8 * (touch_num - 1));
memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
}
key_value = point_data[3 + 8 * touch_num];
if (key_value || pre_key) {
for (i = 0; i < ts->pdata->num_button; i++) {
input_report_key(ts->input_dev,
ts->pdata->button_map[i],
key_value & (0x01<<i));
}
touch_num = 0;
pre_touch = 0;
}
pre_key = key_value;
if (ts->pdata->with_pen) {
if (pre_pen && (touch_num == 0)) {
dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!");
input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
input_mt_slot(ts->input_dev, 5);
input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
pre_pen = 0;
}
}
if (pre_touch || touch_num) {
s32 pos = 0;
u16 touch_index = 0;
coor_data = &point_data[3];
if (touch_num) {
id = coor_data[pos] & 0x0F;
if (ts->pdata->with_pen) {
id = coor_data[pos];
if (id == 128) {
dev_dbg(&ts->client->dev,
"Pen touch DOWN(Slot)!");
input_x = coor_data[pos + 1]
| (coor_data[pos + 2] << 8);
input_y = coor_data[pos + 3]
| (coor_data[pos + 4] << 8);
input_w = coor_data[pos + 5]
| (coor_data[pos + 6] << 8);
input_report_key(ts->input_dev,
BTN_TOOL_PEN, 1);
input_mt_slot(ts->input_dev, 5);
input_report_abs(ts->input_dev,
ABS_MT_TRACKING_ID, 5);
input_report_abs(ts->input_dev,
ABS_MT_POSITION_X, input_x);
input_report_abs(ts->input_dev,
ABS_MT_POSITION_Y, input_y);
input_report_abs(ts->input_dev,
ABS_MT_TOUCH_MAJOR, input_w);
dev_dbg(&ts->client->dev,
"Pen/Stylus: (%d, %d)[%d]",
input_x, input_y, input_w);
pre_pen = 1;
pre_touch = 0;
}
}
touch_index |= (0x01<<id);
}
for (i = 0; i < GTP_MAX_TOUCH; i++) {
if (ts->pdata->with_pen)
if (pre_pen == 1)
break;
if (touch_index & (0x01<<i)) {
input_x = coor_data[pos + 1] |
coor_data[pos + 2] << 8;
input_y = coor_data[pos + 3] |
coor_data[pos + 4] << 8;
input_w = coor_data[pos + 5] |
coor_data[pos + 6] << 8;
gtp_touch_down(ts, id,
input_x, input_y, input_w);
pre_touch |= 0x01 << i;
pos += 8;
id = coor_data[pos] & 0x0F;
touch_index |= (0x01<<id);
} else {
gtp_touch_up(ts, i);
pre_touch &= ~(0x01