kernel(十八)触摸屏GT811

版权声明:本文为博主原创文章,未经博主允许不得转载。 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), },
};

猜你喜欢

转载自blog.csdn.net/JerryGou/article/details/82289842