版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JerryGou/article/details/82289842
一、电容触控芯片GT811
TQ210的电容触摸屏控制芯片是GT811。
GT811引出了6根脚,分别是VCC、GND、I2CSDA、I2CSCL、INT和RESET,虽然INT脚不是必须的,但是开发高效省资源的触屏驱动程序往往都采用中断方式,下面是GT811的引脚图:
使用万能表实际测量了一下触控模块的各个引脚,实际线序是GND、SDA、SDL、INT、RESET和VDD。
YP1连接到了GT811的RESET脚上,然后通过短路帽与GPIO1_26链接,YM1连接到GT811的INT脚上,通过短路帽与GPIO1_27链接,因此,需要将GPIO1_27配置为终端输入引脚,GPIO1_26配置为输出引脚。此外,查看GT811的芯片手册可获得INT和RESET的操作信息:
1. GT811检测到触摸时会拉低中断引脚,因此,GPIO1_27需要配置为下降沿触发。
2. GT811的RESET脚为低电平有效,因此,上电时需要拉低RESET引脚。
3. GT811的RESET引脚自带上拉,因此,使用GPIO1_26将GT811的RESET拉低复位后切换为悬浮输入太即可。
GT811的初始化顺序如下:
(1) 初始化INT脚为悬浮输入态并初始化RESET脚为输出态,并输出低电平
(2) 延时1ms
(3) 初始化RESET脚为悬浮输入态,并使能上拉
(4) 写入GT811寄存器配置表
(5) 根据需要配置INT脚
具体的操作可以参见代码部分。
二、 I2C驱动编写
I2C驱动也是基于总线结构的,不过分为两种,一种是Legacy方式,另一种是New Style方式,其中,Legacy方式在新内核中已经不支持了,不过韦东山老师的视频中还是分析的Legacy方式,New Style方式你可以自己用Source Insight追踪分析一下,具体的可以参考下面的代码。
Louis_touch.c
/*
* Author : LouisGou <[email protected]>
* Date : 2018/9/1
* License: GPL
* Blog : blog.csnd.net/LouisGou
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <plat/gpio-cfg.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
//#define GOODIX_MULTI_TOUCH
struct goodix_data{
unsigned int rst_pin;
unsigned int int_pin;
struct i2c_client *client;
struct workqueue_struct *wq;
struct work_struct work;
unsigned int revert_x_flag;
unsigned int revert_y_flag;
unsigned int exchange_x_y_flag;
unsigned int xmax;
unsigned int ymax;
unsigned int pmax;
};
struct goodix_data *ts = NULL;
static struct input_dev *ts_input;
static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, int len){
struct i2c_msg msgs[2];
msgs[0].flags=!I2C_M_RD;
msgs[0].addr=client->addr;
msgs[0].len=2;
msgs[0].buf=&buf[0];
msgs[1].flags=I2C_M_RD;
msgs[1].addr=client->addr;
msgs[1].len=len-2;
msgs[1].buf=&buf[2];
return i2c_transfer(client->adapter,msgs, 2);
}
static int i2c_write_bytes(struct i2c_client *client,uint8_t *data,int len){
struct i2c_msg msg;
msg.flags=!I2C_M_RD;
msg.addr=client->addr;
msg.len=len;
msg.buf=data;
return i2c_transfer(client->adapter,&msg, 1);
}
static int goodix_pre(void){
uint8_t pre_cmd_data[2]={0};
pre_cmd_data[0]=0x0f;
pre_cmd_data[1]=0xff;
return i2c_write_bytes(ts->client,pre_cmd_data,2);
}
static int goodix_end(void){
uint8_t end_cmd_data[2]={0};
end_cmd_data[0]=0x80;
end_cmd_data[1]=0x00;
return i2c_write_bytes(ts->client,end_cmd_data,2);
}
static const struct i2c_device_id ts_id[] = {
{ "Louis210-ts", 0 },
{ }
};
static int ts_init_panel(struct i2c_client *client){
short ret=-1;
uint8_t config_info[] = {
0x06,0xA2,
0x12,0x10,0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,0xE2,0x53,0xD2,0x53,0xC2,0x53,
0xB2,0x53,0xA2,0x53,0x92,0x53,0x82,0x53,0x72,0x53,0x62,0x53,0x52,0x53,0x42,0x53,
0x32,0x53,0x22,0x53,0x12,0x53,0x02,0x53,0xF2,0x53,0x0F,0x13,0x40,0x40,0x40,0x10,
0x10,0x10,0x0F,0x0F,0x0A,0x35,0x25,0x0C,0x03,0x00,0x05,0x20,0x03,0xE0,0x01,0x00,
0x00,0x34,0x2C,0x36,0x2E,0x00,0x00,0x03,0x19,0x03,0x08,0x00,0x00,0x00,0x00,0x00,
0x14,0x10,0xEC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0D,0x40,
0x30,0x3C,0x28,0x00,0x00,0x00,0x00,0xC0,0x12,0x01
};
config_info[62] = 480 >> 8;
config_info[61] = 480 & 0xff;
config_info[64] = 800 >> 8;
config_info[63] = 800 & 0xff;
ret = i2c_write_bytes(client, config_info, sizeof(config_info)/sizeof(config_info[0]));
if(ret < 0) {
printk(KERN_ERR "GT811 Send config failed!\n");
return ret;
}
return 0;
}
static irqreturn_t ts_irq_handler(int irq, void *devid){
disable_irq_nosync(ts->client->irq);
queue_work(ts->wq, &ts->work);
return IRQ_RETVAL(IRQ_HANDLED);
}
static void ts_work_func(struct work_struct* work){
int ret;
unsigned int count = 0;
unsigned char point_data[36] = {0x07, 0x21, 0}; // 2 + 2 + 5*5 +1
unsigned short input_x = 0;
unsigned short input_y = 0;
unsigned short input_w = 0;
unsigned int point_index = 0;
unsigned int point_count = 0;
unsigned int position = 0;
unsigned char track_id[5] = {0};
goodix_pre();
ret=i2c_read_bytes(ts->client, point_data, sizeof(point_data)/sizeof(point_data[0]));
goodix_end();
if(ret <= 0){
dev_err(&ts->client->dev, "Read gt811 touch points messages failed\n");
goto reirq_enable;
}
point_index = point_data[2]&0x1f;
for(count=0; (count < 5)&&point_index; count++){
if(point_index&0x01){
track_id[point_count++] = position;
}
point_index >>= 1;
}
if(point_count != 0){
for(count=0; count < point_count; count++){
if(track_id[count]!=3){
if(track_id[count]<3)
position = 4+track_id[count]*5;
else
position = 30;
input_x = (uint16_t)(point_data[position]<<8)+(uint16_t)point_data[position+1];
input_y = (uint16_t)(point_data[position+2]<<8)+(uint16_t)point_data[position+3];
input_w = point_data[position+4];
}
else{
input_x = (uint16_t)(point_data[19]<<8)+(uint16_t)point_data[26];
input_y = (uint16_t)(point_data[27]<<8)+(uint16_t)point_data[28];
input_w = point_data[29];
}
if(1 == ts->exchange_x_y_flag) {
swap(input_x, input_y);
}
if(1 == ts->revert_x_flag){
input_x = ts->xmax - input_x;
}
if(1 == ts->revert_y_flag){
input_y = ts->ymax - input_y;
}
//printk("point %d: x = %03d, y = %03d, p = %03d\n", count, input_x, input_y, input_w);
if((input_x > ts->xmax)||(input_y > ts->ymax)||(input_w > ts->pmax))continue;
#ifdef GOODIX_MULTI_TOUCH
input_report_abs(ts_input, ABS_MT_POSITION_X, input_x);
input_report_abs(ts_input, ABS_MT_POSITION_Y, input_y);
input_report_abs(ts_input, ABS_MT_TOUCH_MAJOR, input_w);
input_report_abs(ts_input, ABS_MT_TRACKING_ID, track_id[count]);
#else
if(count == 0){
input_report_abs(ts_input, ABS_X, input_x);
input_report_abs(ts_input, ABS_Y, input_y);
input_report_abs(ts_input, ABS_PRESSURE, 1);
}
#endif
}
}
else{
#ifdef GOODIX_MULTI_TOUCH
input_report_abs(ts_input, ABS_MT_TOUCH_MAJOR, 0);
#else
input_report_abs(ts_input, ABS_PRESSURE, 0);
#endif
}
#ifdef GOODIX_MULTI_TOUCH
input_mt_sync(ts_input);
#endif
input_report_key(ts_input, BTN_TOUCH, point_count > 0);
input_sync(ts_input);
reirq_enable:
enable_irq(ts->client->irq);
}
static int ts_probe(struct i2c_client *client, const struct i2c_device_id *id){
int retry, ret;
char test_data;
test_data = 0;
ts = (struct goodix_data *)kzalloc(sizeof(struct goodix_data), GFP_KERNEL);
if(ts == NULL){
dev_err(&client->dev, "Allocate ts failed!\n");
return -ENOMEM;
}
ts->rst_pin = S5PV210_GPD0(3);
ts->int_pin = S5PV210_GPH1(6);
gpio_request(ts->rst_pin, "reset");
gpio_request(ts->int_pin, "tsint");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "Must have I2C_FUNC_I2C.\n");
ret = -ENODEV;
goto free_ts;
}
s3c_gpio_setpull(ts->rst_pin, S3C_GPIO_PULL_UP);
for(retry=0;retry < 5; retry++){
gpio_direction_output(ts->rst_pin, 0);
msleep(1);
gpio_direction_input(ts->rst_pin);
msleep(100);
ret =i2c_write_bytes(client, &test_data, 1);
if (ret > 0)
break;
dev_info(&client->dev, "GT811 I2C TEST FAILED!Please check the HARDWARE connect\n");
}
if(ret <= 0){
dev_err(&client->dev, "Warnning: I2C communication might be ERROR!\n");
ret = -ENODEV;
goto free_ts;
}
for(retry = 0; retry != 5; ++retry){
ret = ts_init_panel(client);
if(ret == 0)
break;
}
if(ret != 0){
dev_err(&client->dev, "GT811 Configue failed!\n");
ret = -ENODEV;
goto free_ts;
}
ts->client = client;
ts_input = input_allocate_device();
if(IS_ERR(ts_input)){
dev_err(&client->dev, "GT811 allocate ts input device failed!\n");
ret = -ENOMEM;
goto free_ts;
}
ts_input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
ts_input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
ts_input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
ts->revert_x_flag = 1;
ts->revert_y_flag = 1;
ts->exchange_x_y_flag = 1;
ts->xmax = 799;
ts->ymax = 479;
ts->pmax = 255;
#ifdef GOODIX_MULTI_TOUCH
input_set_abs_params(ts_input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts_input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts_input, ABS_MT_POSITION_X, 0, ts->xmax, 0, 0);
input_set_abs_params(ts_input, ABS_MT_POSITION_Y, 0, ts->ymax, 0, 0);
input_set_abs_params(ts_input, ABS_MT_TRACKING_ID, 0, ts->pmax, 0, 0);
#else
input_set_abs_params(ts_input, ABS_X, 0, ts->xmax, 0, 0);
input_set_abs_params(ts_input, ABS_Y, 0, ts->ymax, 0, 0);
input_set_abs_params(ts_input, ABS_PRESSURE, 0, ts->pmax, 0, 0);
#endif
ts_input->name = "Louis210-ts";
ts_input->phys = "input/ts";
ts_input->id.bustype = BUS_I2C;
ts_input->id.product = 0xBEEF;
ts_input->id.vendor =0xDEAD;
ret = input_register_device(ts_input);
if(ret < 0){
dev_err(&client->dev, "Unable register %s input device!\n", ts_input->name);
ret = -ENOMEM;
goto free_input_dev;
}
client->irq = IRQ_EINT(14);
ts->wq = create_singlethread_workqueue("ts_handle_thread");
if(ts->wq == NULL){
dev_err(&client->dev, "Allocate work queue faile!\n");
ret = -ENOMEM;
goto free_input_dev;
}
INIT_WORK(&ts->work, ts_work_func);
s3c_gpio_setpull(ts->int_pin, S3C_GPIO_PULL_UP);
if(request_irq(IRQ_EINT(14), ts_irq_handler, IRQF_TRIGGER_FALLING, "gt811-int", NULL) < 0){
printk("Request irq for gt811 failed!\n");
ret = -ENOMEM;
goto free_wq;
}
return 0;
free_wq:
destroy_workqueue(ts->wq);
free_input_dev:
input_free_device(ts_input);
free_ts:
kfree(ts);
return ret;
}
static int ts_remove(struct i2c_client *client){
free_irq(IRQ_EINT(14), NULL);
destroy_workqueue(ts->wq);
input_unregister_device(ts_input);
input_free_device(ts_input);
gpio_free(ts->int_pin);
gpio_free(ts->rst_pin);
kfree(ts);
return 0;
}
static int ts_suspend(struct i2c_client *client, pm_message_t mesg){
int ret, retry;
unsigned char buf[3];
disable_irq(client->irq);
buf[0] = 0x06;
buf[1] = 0x92;
buf[2] = 0x01;
for(retry = 0; retry < 3; ++ retry){
ret = i2c_write_bytes(client, buf, 3);
if(!(ret < 0)){
break;
}
}
if(ret < 0){
dev_err(&client->dev, "Send the sleep command to the gt811 failed!\n");
return ret;
}
buf[0] = 0x07;
buf[1] = 0x0B;
buf[2] = 0x01;
for(retry = 0; retry < 3; ++ retry){
ret = i2c_write_bytes(client, buf, 3);
if(!(ret < 0)){
break;
}
}
if(ret < 0){
dev_err(&client->dev, "Send the sleep command to the gt811 failed!\n");
return ret;
}
return 0;
}
static int ts_resume(struct i2c_client *client){
s3c_gpio_setpull(ts->int_pin, S3C_GPIO_PULL_UP);
msleep(20);
s3c_gpio_setpull(ts->int_pin, S3C_GPIO_PULL_DOWN);
msleep(20);
enable_irq(client->irq);
return 0;
}
static struct i2c_driver ts_driver = {
.probe = ts_probe,
.remove = ts_remove,
.suspend = ts_suspend,
.resume = ts_resume,
.id_table = ts_id,
.driver = {
.name = "Louis210-ts",
.owner = THIS_MODULE,
},
};
static int ts_init(void){
i2c_add_driver(&ts_driver);
return 0;
}
static void ts_exit(void){
i2c_del_driver(&ts_driver);
}
module_init(ts_init);
module_exit(ts_exit);
MODULE_AUTHOR("LouisGou <[email protected]>");
MODULE_DESCRIPTION("Louis210 Gt811 I2C Touchscreen Driver");
MODULE_LICENSE("GPL");
这并不是完整的代码,一方面是没有做异常处理,另一方面是没有上报消息,只是简单的驱动了TQ210的触摸屏部分,如果您需要拿去自己略作修改即可使用。
三、 注册TS的I2C模块设备
注册TS的I2C模块很简单,在Linux内核文件arch/arm/mach-s5pv210/mach-Louis210.c文件的I2C通道2结构体中加入TS的I2C地址,也就是0x5d,添加后如下
static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
/* To Be Updated */
{ I2C_BOARD_INFO("Louis210-ts", 0x5d), },
};