目录
本学习笔记是基于正点原子的IMX6ULL开发板的学习。
触摸IC动框架
首先在Linux中,为了便于开发实现了内核和驱动的分离分层,在本触摸IC的驱动中,也沿用了这一思想,市面上的触摸IC五花八门,驱动方式各有千秋,Linux是如何把它整合起来的呢?很简单,制定了一种协议——MT 协议。这种协议把驱动ic分成了两大类,分别是Type A和Type B,这两类的区别如下。
Type A:适用于那些触摸点不能被区分或追踪的设备。
Type B:适用于有硬件追踪并能区分触摸点的触摸设备。这种类型的设备通过slot(也就是触摸点ID)更新某一个触摸点的信息。
也就是说一块触摸IC想要适配Linux内核他只需要按照MT协议,向Linux内核上报触点信息即可,有了这个特性,市面上五花八门的触摸IC想和Linux内核适配,也就变成了可用程序要实现的事情了。
目前我们市面上大多数的触摸IC都是Type B类型为主,本章的学习,也是基于Type B类型的触摸IC。
设备树配置
由于本次使用的触摸IC是GT911,用IIC驱动控制的,自然而然也就绕不开设备树了。在编写驱动的代码前,我们需要先配置好设备树。
先明确要用几个引脚,复位引脚、中断引脚(触摸屏被触碰会触发中断)、SDA、SCL一共有四个,所以我们要配置这4个引脚。
配置复位引脚,在&iomuxc_snvs下配置:
配置中断引脚,在&iomuxc下配置:
这里值得声明的是,在&iomuxc下或在&iomuxc_snvs下,并不是由我们自己决定的,是根据开发板的GPIO分布决定的,可以理解为两个块,所有的IO都分别挂载在这两个块上,用到对应的IO就要在对应的块下配置。属于iomuxc_snvs的大多数IO的命名都有以下关键词BOOT_MODE0、SNVS_TAMPER0、而在iomuxc更多的是通用IO,具体可以参考数据手册。
配置IIC的两个引脚:本次实验触摸IC用的是板载的i2c2,所以我们只需要在设备树中找到i2c2这个节点,往里添加一个设备即可。
这里稍微分析一下这些参数
gt9147:gt9147@14{
compatible = "goodix,gt9147","goodix,gt9xx";//这是匹配关键词,可以理解为ID,满足其中一个的便可和这个设备进行匹配
reg = <0x14>;//这是IIC设备的器件ID
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc
&pinctrl_tsc_reset>; //这个地方,因为我们引用了一个复位和中断,所以我们要和这两个引脚的节点进行匹配,这里就是通过这两个引脚的节点名字去索引这两个节点。
interrupt-parent = <&gpio1>; //中断控制器,可以理解为一个父节点,就是这个父节点下有很多子节点,这些子节点的中断都是交给父节点去处理,这个就是中断控制器。
interrupts = <9 0>; //这里是中断号和触发方式,串在一起理解则是父节点下的9号子节点可以以0号方式触发中断
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; //这里是声明复位引脚用的是gpio5(一个节点号)下的9号引脚,复位有效电平是低电平。
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;//这里是声明中断引脚用的是gpio1下的9号引脚,复位有效电平是低电平(决定了复位后是高电平还是低电平)。
status = "okay";
};
完成以上后,设备树就配置完成了,下一步便是驱动代码的编写。
驱动代码的编写
因为这里涉及了IIC驱动,驱动框架和我之前写的笔记类似,区别是,现在不需要再创建一个字符设备去实现读写了。IIC框架的搭建方面就不再赘述了。比较有意思的是,在这里正点原子的代码用了两种不同的方法去加载驱动,在这里我也一并给贴出来。
FT5X06的编写(传统方法)
/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init ft5x06_init(void)
{
int ret = 0;
ret = i2c_add_driver(&ft5x06_ts_driver);
return ret;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit ft5x06_exit(void)
{
i2c_del_driver(&ft5x06_ts_driver);
}
module_init(ft5x06_init);
module_exit(ft5x06_exit);
//这种加载方法,用于所有Linux内核模块,无论它们执行什么功能。
FT5X06的编写(IIC驱动方法)
struct i2c_driver gt9147_i2c_driver = {
.driver = {
.name = "gt9147",
.owner = THIS_MODULE,
.of_match_table = gt9147_of_match_table,
},
.id_table = gt9147_id_table,
.probe = gt9147_probe,
.remove = gt9147_remove,
};
module_i2c_driver(gt9147_i2c_driver);
//这里用了个module_i2c_driver直接传入一个iic设备结构体便可实现加载驱动,也不需要exit,应该是在卸载驱动时由内核自动exit了
在内核里扒了一下module_i2c_driver这个宏,发现这里其实也是靠封装init和exit实现的。
除去这里,其余的地方除了芯片的型号有所不同,寄存器的配置有所不同外,其他的框架基本上是一样的,我这里就以gt911的代码为例进行分析了。
//这是本代码用到的用户定义设备结构体
struct gt9147_dev {
int irq_pin,reset_pin; /* 中断和复位IO */
int irqnum; /* 中断号 */
int irqtype; /* 中断类型 */
int max_x; /* 最大横坐标 */
int max_y; /* 最大纵坐标 */
void *private_data; /* 私有数据 */
struct input_dev *input; /* input结构体 */
struct i2c_client *client; /* I2C客户端 */
};
probe函数
这个是和设备树实现匹配后执行的函数。这里着重讲一下input子系统和中断。
//总体来说,实现的功能主要是
//1.获取中断、复位引脚
//2.对芯片进行硬件复位
//3.配置寄存器实现软复位和初始化
//4.配置一个input子系统
//5.配置一个中断
int gt9147_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
u8 data, ret;
gt9147.client = client;
/* 1,获取设备树中的中断和复位引脚 */
gt9147.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
gt9147.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
/* 2,复位GT9147 */
ret = gt9147_ts_reset(client, >9147);
if(ret < 0) {
goto fail;
}
/* 3,初始化GT9147 */
data = 0x02;
gt9147_write_regs(>9147, GT_CTRL_REG, &data, 1); /* 软复位 */
mdelay(100);
data = 0x0;
gt9147_write_regs(>9147, GT_CTRL_REG, &data, 1); /* 停止软复位 */
mdelay(100);
/* 4,初始化GT9147,读取固件 */
ret = gt9147_read_firmware(client, >9147);
if(ret != 0) {
printk("Fail !!! check !!\r\n");
goto fail;
}
/* 5,input设备注册 */
gt9147.input = devm_input_allocate_device(&client->dev);
if (!gt9147.input) {
ret = -ENOMEM;
goto fail;
}
gt9147.input->name = client->name;
gt9147.input->id.bustype = BUS_I2C;
gt9147.input->dev.parent = &client->dev;
__set_bit(EV_KEY, gt9147.input->evbit);
__set_bit(EV_ABS, gt9147.input->evbit);
__set_bit(BTN_TOUCH, gt9147.input->keybit);
input_set_abs_params(gt9147.input, ABS_X, 0, gt9147.max_x, 0, 0);
input_set_abs_params(gt9147.input, ABS_Y, 0, gt9147.max_y, 0, 0);
input_set_abs_params(gt9147.input, ABS_MT_POSITION_X,0, gt9147.max_x, 0, 0);
input_set_abs_params(gt9147.input, ABS_MT_POSITION_Y,0, gt9147.max_y, 0, 0);
ret = input_mt_init_slots(gt9147.input, MAX_SUPPORT_POINTS, 0);
if (ret) {
goto fail;
}
ret = input_register_device(gt9147.input);
if (ret)
goto fail;
/* 6,最后初始化中断 */
ret = gt9147_ts_irq(client, >9147);
if(ret < 0) {
goto fail;
}
return 0;
fail:
return ret;
}
input子系统
本质上是一个字符设备,经过封装以后,用户只需要去负责上报输入事件。在本实验中,上报的便是触摸IC获取到的坐标信息。
具体使用方法:申请一个input_dev
//申请一个input设备,用devm申请,由系统自动释放免去手动释放。
gt9147.input = devm_input_allocate_device(&client->dev);//这里传入的是一个device结构体,在我们申请input子设备之前,只有一个设备,即iic设备,是在我们注册iic时系统帮我分配的,在client下。
if (!gt9147.input) {
ret = -ENOMEM;
goto fail;
}
gt9147.input->name = client->name; //这个input子设备的名字,和iic设备一样
gt9147.input->id.bustype = BUS_I2C; //总线类型,如果是按键的话这里不用配置
gt9147.input->dev.parent = &client->dev; //父设备,即我们基于哪个设备创建的这个子设备
配置input子设备的事件
__set_bit(EV_KEY, gt9147.input->evbit);
__set_bit(EV_ABS, gt9147.input->evbit);
__set_bit(BTN_TOUCH, gt9147.input->keybit);
//在 gt9147 输入设备的事件类型位图中设置 EV_KEY 位,表示该输入设备支持按键事件。这样,当系统或应用程序查询该输入设备支持哪些事件类型时,就能知道它可以处理按键事件。相当于声明,支持EV_KEY事件的输入
//这里的参数,第一个,哪个input子设备,第二个X轴,第三个最小值为0,第四个最大值为(动态获取屏幕分辨率),五六全给0,暂时用不到。
input_set_abs_params(gt9147.input, ABS_X, 0, gt9147.max_x, 0, 0);
input_set_abs_params(gt9147.input, ABS_Y, 0, gt9147.max_y, 0, 0);
//这里是上报一个MT事件,把触摸坐标上报上去
input_set_abs_params(gt9147.input, ABS_MT_POSITION_X,0, gt9147.max_x, 0, 0);
input_set_abs_params(gt9147.input, ABS_MT_POSITION_Y,0, gt9147.max_y, 0, 0);
//多点触摸初始化一个槽。MAX_SUPPORT_POINTS是支持最多多少个点触摸
ret = input_mt_init_slots(gt9147.input, MAX_SUPPORT_POINTS, 0);
if (ret) {
goto fail;
}
注册input子设备
ret = input_register_device(gt9147.input);
完成以上操作后,我们便得到了一个配置好支持上报事件类型的input子设备,接下来只需要在中断中执行上报操作即可。
中断配置
中断的初始化和配置
/* 6,最后初始化中断 */
ret = gt9147_ts_irq(client, >9147);
if(ret < 0) {
goto fail;
}
return 0;
static int gt9147_ts_irq(struct i2c_client *client, struct gt9147_dev *dev)
{
int ret = 0;
/* 2,申请中断,client->irq就是IO中断, */
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
gt9147_irq_handler, irq_table[dev->irqtype] | IRQF_ONESHOT,
client->name, >9147);
if (ret) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return ret;
}
return 0;
}
//由于IIC设备是支持中断的,我们在设备树已经配置了中断引脚,所以我们可以直接在client结构体里获取到我们想要注册的中断号。
//int devm_request_threaded_irq(struct device *dev, unsigned int irq,
// irq_handler_t handler, irq_handler_t thread_fn,
// unsigned long flags, const char *name, void *dev_id)
//这个函数的参数描述
/*
dev: 指向设备的 struct device 结构体,内核将使用它来管理这个中断请求。
irq: 要请求的中断号。
handler: 主中断处理函数,这个函数在中断发生时首先被调用。它应该迅速执行,本实验用作上报触摸屏的坐标信息。如果不需要线程化的处理函数,可以将其设置为 NULL。
thread_fn: 线程化的中断处理函数,这个函数在 handler 返回 IRQ_WAKE_THREAD 后,由内核在单独的线程中调用。它允许进行耗时较长的处理,而不会阻塞中断处理。
flags: 中断标志,如 IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING 等,用于指定中断的触发条件。
name: 中断请求的名称,通常用于调试和日志记录。
dev_id: 一个 void * 类型的参数,将传递给中断处理函数,用户可自定义传入的内容。
*/
中断回调函数
static irqreturn_t gt9147_irq_handler(int irq, void *dev_id)
{
int touch_num = 0;
int input_x, input_y;
int id = 0;
int ret = 0;
u8 data;
u8 touch_data[5];
struct gt9147_dev *dev = dev_id;
ret = gt9147_read_regs(dev, GT_GSTID_REG, &data, 1);
if (data == 0x00) { /* 没有触摸数据,直接返回 */
goto fail;
} else { /* 统计触摸点数据 */
touch_num = data & 0x0f;
}
/* 由于GT9147没有硬件检测每个触摸点按下和抬起,因此每个触摸点的抬起和按
* 下不好处理,尝试过一些方法,但是效果都不好,因此这里暂时使用单点触摸
*/
if(touch_num) { /* 单点触摸按下 */
gt9147_read_regs(dev, GT_TP1_REG, touch_data, 5);
id = touch_data[0] & 0x0F;
if(id == 0) {
input_x = touch_data[1] | (touch_data[2] << 8);
input_y = touch_data[3] | (touch_data[4] << 8);
//这里执行了上报操作
input_mt_slot(dev->input, id);//设置当前要操作的slot的编号
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);
//那个input子系统,事件,值
input_report_abs(dev->input, ABS_MT_POSITION_X, input_x);
input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y);
}
} else if(touch_num == 0){ /* 单点触摸释放 */
input_mt_slot(dev->input, id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false);
}
input_mt_report_pointer_emulation(dev->input, true); // true,获取有效的触摸点数量; false,获取追踪到的触摸点数量多于当前上报的数量。 具体作用我也不是很清楚
input_sync(dev->input); //同步输入设备状态
data = 0x00; /* 向0X814E寄存器写0 */
gt9147_write_regs(dev, GT_GSTID_REG, &data, 1);
fail:
return IRQ_HANDLED;
}
总结
触摸IC的驱动框架基本上都大差不差,将来遇到其他型号的芯片时可以参照这个来写。全部代码:
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/i2c.h>
#include <asm/unaligned.h>
#define GT_CTRL_REG 0X8040 /* GT9147控制寄存器 */
#define GT_MODSW_REG 0X804D /* GT9147模式切换寄存器 */
#define GT_9xx_CFGS_REG 0X8047 /* GT9147配置起始地址寄存器 */
#define GT_1xx_CFGS_REG 0X8050 /* GT1151配置起始地址寄存器 */
#define GT_CHECK_REG 0X80FF /* GT9147校验和寄存器 */
#define GT_PID_REG 0X8140 /* GT9147产品ID寄存器 */
#define GT_GSTID_REG 0X814E /* GT9147当前检测到的触摸情况 */
#define GT_TP1_REG 0X814F /* 第一个触摸点数据地址 */
#define GT_TP2_REG 0X8157 /* 第二个触摸点数据地址 */
#define GT_TP3_REG 0X815F /* 第三个触摸点数据地址 */
#define GT_TP4_REG 0X8167 /* 第四个触摸点数据地址 */
#define GT_TP5_REG 0X816F /* 第五个触摸点数据地址 */
#define MAX_SUPPORT_POINTS 5 /* 最多5点电容触摸 */
struct gt9147_dev {
int irq_pin,reset_pin; /* 中断和复位IO */
int irqnum; /* 中断号 */
int irqtype; /* 中断类型 */
int max_x; /* 最大横坐标 */
int max_y; /* 最大纵坐标 */
void *private_data; /* 私有数据 */
struct input_dev *input; /* input结构体 */
struct i2c_client *client; /* I2C客户端 */
};
struct gt9147_dev gt9147;
const u8 irq_table[] = {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH}; /* 触发方式 */
/*
* @description : 复位GT9147
* @param - client : 要操作的i2c
* @param - multidev: 自定义的multitouch设备
* @return : 0,成功;其他负值,失败
*/
static int gt9147_ts_reset(struct i2c_client *client, struct gt9147_dev *dev)
{
int ret = 0;
/* 申请复位IO*/
if (gpio_is_valid(dev->reset_pin)) {
/* 申请复位IO,并且默认输出高电平 */
ret = devm_gpio_request_one(&client->dev,
dev->reset_pin, GPIOF_OUT_INIT_HIGH,
"gt9147 reset");
if (ret) {
return ret;
}
}
/* 申请中断IO*/
if (gpio_is_valid(dev->irq_pin)) {
/* 申请复位IO,并且默认输出高电平 */
ret = devm_gpio_request_one(&client->dev,
dev->irq_pin, GPIOF_OUT_INIT_HIGH,
"gt9147 int");
if (ret) {
return ret;
}
}
/* 4、初始化GT9147,要严格按照GT9147时序要求 */
gpio_set_value(dev->reset_pin, 0); /* 复位GT9147 */
msleep(10);
gpio_set_value(dev->reset_pin, 1); /* 停止复位GT9147 */
msleep(10);
gpio_set_value(dev->irq_pin, 0); /* 拉低INT引脚 */
msleep(50);
gpio_direction_input(dev->irq_pin); /* INT引脚设置为输入 */
return 0;
}
/*
* @description : 从GT9147读取多个寄存器数据
* @param - dev: GT9147设备
* @param - reg: 要读取的寄存器首地址
* @param - buf: 读取到的数据
* @param - len: 要读取的数据长度
* @return : 操作结果
*/
static int gt9147_read_regs(struct gt9147_dev *dev, u16 reg, u8 *buf, int len)
{
int ret;
u8 regdata[2];
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)dev->client;
/* GT9147寄存器长度为2个字节 */
regdata[0] = reg >> 8;
regdata[1] = reg & 0xFF;
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr; /* ft5x06地址 */
msg[0].flags = !I2C_M_RD; /* 标记为发送数据 */
msg[0].buf = ®data[0]; /* 读取的首地址 */
msg[0].len = 2; /* reg长度*/
/* msg[1]读取数据 */
msg[1].addr = client->addr; /* ft5x06地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
msg[1].buf = buf; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度*/
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
ret = -EREMOTEIO;
}
return ret;
}
/*
* @description : 向GT9147多个寄存器写入数据
* @param - dev: GT9147设备
* @param - reg: 要写入的寄存器首地址
* @param - val: 要写入的数据缓冲区
* @param - len: 要写入的数据长度
* @return : 操作结果
*/
static s32 gt9147_write_regs(struct gt9147_dev *dev, u16 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->client;
b[0] = reg >> 8; /* 寄存器首地址低8位 */
b[1] = reg & 0XFF; /* 寄存器首地址高8位 */
memcpy(&b[2],buf,len); /* 将要写入的数据拷贝到数组b里面 */
msg.addr = client->addr; /* gt9147地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要写入的数据缓冲区 */
msg.len = len + 2; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
static irqreturn_t gt9147_irq_handler(int irq, void *dev_id)
{
int touch_num = 0;
int input_x, input_y;
int id = 0;
int ret = 0;
u8 data;
u8 touch_data[5];
struct gt9147_dev *dev = dev_id;
ret = gt9147_read_regs(dev, GT_GSTID_REG, &data, 1);
if (data == 0x00) { /* 没有触摸数据,直接返回 */
goto fail;
} else { /* 统计触摸点数据 */
touch_num = data & 0x0f;
}
/* 由于GT9147没有硬件检测每个触摸点按下和抬起,因此每个触摸点的抬起和按
* 下不好处理,尝试过一些方法,但是效果都不好,因此这里暂时使用单点触摸
*/
if(touch_num) { /* 单点触摸按下 */
gt9147_read_regs(dev, GT_TP1_REG, touch_data, 5);
id = touch_data[0] & 0x0F;
if(id == 0) {
input_x = touch_data[1] | (touch_data[2] << 8);
input_y = touch_data[3] | (touch_data[4] << 8);
input_mt_slot(dev->input, id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);
input_report_abs(dev->input, ABS_MT_POSITION_X, input_x);
input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y);
}
} else if(touch_num == 0){ /* 单点触摸释放 */
input_mt_slot(dev->input, id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false);
}
input_mt_report_pointer_emulation(dev->input, true);
input_sync(dev->input);
data = 0x00; /* 向0X814E寄存器写0 */
gt9147_write_regs(dev, GT_GSTID_REG, &data, 1);
fail:
return IRQ_HANDLED;
}
/*
* @description : GT9147中断初始化
* @param - client : 要操作的i2c
* @param - multidev: 自定义的multitouch设备
* @return : 0,成功;其他负值,失败
*/
static int gt9147_ts_irq(struct i2c_client *client, struct gt9147_dev *dev)
{
int ret = 0;
/* 2,申请中断,client->irq就是IO中断, */
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
gt9147_irq_handler, irq_table[dev->irqtype] | IRQF_ONESHOT,
client->name, >9147);
if (ret) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return ret;
}
return 0;
}
/*
* @description : GT9147读取固件
* @param - client : 要操作的i2c
* @param - multidev: 自定义的multitouch设备
* @return : 0,成功;其他负值,失败
*/
static int gt9147_read_firmware(struct i2c_client *client, struct gt9147_dev *dev)
{
int ret = 0, version = 0;
u16 id = 0;
u8 data[7]={0};
char id_str[5];
ret = gt9147_read_regs(dev, GT_PID_REG, data, 6);
if (ret) {
dev_err(&client->dev, "Unable to read PID.\n");
return ret;
}
memcpy(id_str, data, 4);
id_str[4] = 0;
if (kstrtou16(id_str, 10, &id))
id = 0x1001;
version = get_unaligned_le16(&data[4]);
dev_info(&client->dev, "ID %d, version: %04x\n", id, version);
switch (id) { /* 由于不同的芯片配置寄存器地址不一样需要判断一下 */
case 1151:
case 1158:
case 5663:
case 5688: /* 读取固件里面的配置信息 */
ret = gt9147_read_regs(dev, GT_1xx_CFGS_REG, data, 7);
break;
default:
ret = gt9147_read_regs(dev, GT_9xx_CFGS_REG, data, 7);
break;
}
if (ret) {
dev_err(&client->dev, "Unable to read Firmware.\n");
return ret;
}
dev->max_x = (data[2] << 8) + data[1];
dev->max_y = (data[4] << 8) + data[3];
dev->irqtype = data[6] & 0x3;
printk("X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", dev->max_x, dev->max_y, dev->irqtype);
return 0;
}
int gt9147_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
u8 data, ret;
gt9147.client = client;
/* 1,获取设备树中的中断和复位引脚 */
gt9147.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
gt9147.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
/* 2,复位GT9147 */
ret = gt9147_ts_reset(client, >9147);
if(ret < 0) {
goto fail;
}
/* 3,初始化GT9147 */
data = 0x02;
gt9147_write_regs(>9147, GT_CTRL_REG, &data, 1); /* 软复位 */
mdelay(100);
data = 0x0;
gt9147_write_regs(>9147, GT_CTRL_REG, &data, 1); /* 停止软复位 */
mdelay(100);
/* 4,初始化GT9147,读取固件 */
ret = gt9147_read_firmware(client, >9147);
if(ret != 0) {
printk("Fail !!! check !!\r\n");
goto fail;
}
/* 5,input设备注册 */
gt9147.input = devm_input_allocate_device(&client->dev);
if (!gt9147.input) {
ret = -ENOMEM;
goto fail;
}
gt9147.input->name = client->name;
gt9147.input->id.bustype = BUS_I2C;
gt9147.input->dev.parent = &client->dev;
__set_bit(EV_KEY, gt9147.input->evbit);
__set_bit(EV_ABS, gt9147.input->evbit);
__set_bit(BTN_TOUCH, gt9147.input->keybit);
input_set_abs_params(gt9147.input, ABS_X, 0, gt9147.max_x, 0, 0);
input_set_abs_params(gt9147.input, ABS_Y, 0, gt9147.max_y, 0, 0);
input_set_abs_params(gt9147.input, ABS_MT_POSITION_X,0, gt9147.max_x, 0, 0);
input_set_abs_params(gt9147.input, ABS_MT_POSITION_Y,0, gt9147.max_y, 0, 0);
ret = input_mt_init_slots(gt9147.input, MAX_SUPPORT_POINTS, 0);
if (ret) {
goto fail;
}
ret = input_register_device(gt9147.input);
if (ret)
goto fail;
/* 6,最后初始化中断 */
ret = gt9147_ts_irq(client, >9147);
if(ret < 0) {
goto fail;
}
return 0;
fail:
return ret;
}
/*
* @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
* @param - client : i2c设备
* @return : 0,成功;其他负值,失败
*/
int gt9147_remove(struct i2c_client *client)
{
input_unregister_device(gt9147.input);
return 0;
}
/*
* 传统驱动匹配表
*/
const struct i2c_device_id gt9147_id_table[] = {
{ "goodix,gt9147", 0, },
{ /* sentinel */ }
};
/*
* 设备树匹配表
*/
const struct of_device_id gt9147_of_match_table[] = {
{.compatible = "goodix,gt9147" },
{ /* sentinel */ }
};
/* i2c驱动结构体 */
struct i2c_driver gt9147_i2c_driver = {
.driver = {
.name = "gt9147",
.owner = THIS_MODULE,
.of_match_table = gt9147_of_match_table,
},
.id_table = gt9147_id_table,
.probe = gt9147_probe,
.remove = gt9147_remove,
};
module_i2c_driver(gt9147_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");