STM32MP157驱动开发——多点电容触摸屏驱动
- 一、简介
- 二、电容触摸屏驱动框架简介
- 多点触摸(MT)协议详解
- 三、驱动开发
- 1.添加 FT5426 设备节点
- 2.FT5426 节点配置
- 3.驱动编写
- 4.运行测试
- 5.将驱动添加到内核中
- 1)将驱动文件放到合适的位置
- 2)修改Makefile
- 6.tslib 移植与使用
- 四、使用内核自带的驱动
- 1.使能内核自带的 FT5X06 驱动
- 2.修改设备树
- 五、4.3寸屏驱动
- 1.修改设备树
- 2.添加屏幕参数
参考文章:【正点原子】I.MX6U嵌入式Linux驱动开发——多点电容触摸屏
一、简介
一开始出现的触摸屏是电阻触摸屏,只能单点触摸,在以前的学习机、功能机时代被广泛使用。2007年苹果的一代 iPhone,也就是 iPhone 2G上使用了多点电容触摸屏。和电阻触摸屏相比,电容触摸屏最大的优点是支持多点触摸(现在的电阻屏也支持多点触控了)。此外,电容屏只需要手指轻触即可,而电阻屏是需要手指给予一定的压力才有反应,而且电容屏不需要校准。
正点原子的触摸屏采用的是 LCD + 触摸屏结合的方式。底下是 LCD 面板,上面是触摸面板,将两个封装到一起就成了带有触摸屏的 LCD 屏幕。电容触摸屏需要一个驱动 IC 来实现,一般使用的是 IIC 接口。主控制器可以通过 I2C 接口来读取驱动 IC里面的触摸坐标数据。原子的 ATK-7016、ATK-7084 触控 IC 为 FT5426,ATK-4342 使用的触控 IC 为 GT9147。
本节就对 FT5426 进行开发。FT5426 的 I2C 设备地址为 0X38,ATK-7016 的电容触摸屏部分有 4 个 IO 用于连接主控制器:SCL、SDA、RST 和 INT,SCL 和 SDA 是 I2C 引脚,RST 是复位引脚,INT 是中断引脚。一般通过 INT 引脚来通知主控制器有触摸点按下,然后在 INT 中断服务函数中读取触摸数据。
二、电容触摸屏驱动框架简介
多点触摸(MT)协议详解
电容触摸屏驱动其实就是以下几种 linux 驱动框架的组合:
- IIC 设备驱动,因为电容触摸 IC 基本都是 IIC 接口的,因此大框架就是 IIC 设备驱动
- 通过中断引脚(INT)向 linux 内核上报触摸信息,因此需要用到 linux 中断驱动框架。坐标的上报在中断服务函数中完成。
- 触摸屏的坐标信息、屏幕按下和抬起信息都属于 linux 的 input 子系统,因此向 linux 内核上报触摸屏坐标信息就得使用 input 子系统。
- 在中断处理程序中按照 linux 的 MT 协议上报坐标信息。
linux内核中有一份文档详细的讲解了多点电容触摸屏协议,文档路径为:Documentation/input/multitouch-protocol.txt。且老版本中不支持此协议。
MT 协议被分为两种类型,TypeA 和 TypeB:
Type A:适用于触摸点不能被区分或者追踪,此类型的设备通常上报原始数据(此类型在实际使用中非常少!)
Type B:适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个触摸点的信息,FT5426 就属于此类型,一般的多点电容触摸屏 IC 都有此能力。
Type A时序:x1–>y1–>mt-sync–>x2–>y2–>mt-sync–>input-sync。
Type B时序:mt-slot–>mt-id–>x1–>y1–…–>sync
在电容触摸屏驱动框架中,会通过中断来向系统上报触摸点坐标信息的。但是此中断的申请,使用的是 devm_request_threaded_irq 函数,而不是之前学习的 request_threaded_irq,此函数的作用是中断线程化。
硬件中断具有最高优先级,不论什么时候只要硬件中断发生,内核都会终止当前正在执行的操作,转而去执行中断处理程序(不考虑关闭中断和中断优先级的情况),如果中断非常频繁,那么内核将会频繁的执行中断处理程序,导致任务得不到及时的处理。中断线程化以后中断将作为内核线程运行,而且也可以被赋予不同的优先级,任务的优先级可能比中断线程的优先级高,这样做的目的就是保证高优先级的任务能被优先处理。
对于触摸屏而言,只要手指放到屏幕上,它可能就会一直产生中断(视具体芯片而定,FT5426 是这样的),中断处理程序里面需要通过 I2C 读取触摸信息并上报给内核,I2C 的速度最大只有 400KHz,算是低速外设。不断的产生中断、读取触摸信息、上报信息会导致处理器在触摸中断上花费大量的时间,但是触摸相对来说不是那么重要的事件,因此可以将触摸中断线程化。
使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要手动处理。
例:之前的驱动里面申请了很多资源,比如:gpio、irq、input_dev,那么就需要添加很多 goto 语句对其做处理,当这样的标签多了以后代码看起来就不整洁了。使用 devm_request_threaded_irq 函数来申请中断,那么就不需要再调用 free_irq 函数对其进行释放。
中断处理函数中上报信息示例:
1 static irqreturn_t xxx_handler(int irq, void *dev_id)
2 {
3
4 int num; /* 触摸点数量 */
5 int x[n], y[n]; /* 保存坐标值 */
6
7 /* 1、从触摸芯片获取各个触摸点坐标值 */
8 ......
9
10 /* 2、上报每一个触摸点坐标 */
11 for (i = 0; i < num; i++) {
12 input_mt_slot(input, id);
13 input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
14 input_report_abs(input, ABS_MT_POSITION_X, x[i]);
15 input_report_abs(input, ABS_MT_POSITION_Y, y[i]);
16 }
17 ......
18
19 input_sync(input);
20 ......
21
22 return IRQ_HANDLED;
23 }
三、驱动开发
原理图:
I2C2_SCL 和 I2C2_SDA 对应的引脚为 PH4 和 PH5。CT_INT 对应的引脚为 PI1,CT_RST 对应的引脚为 PH15。
对于中断触发模式,当 FT5426 检测到有触摸事件发生时,会将中断信号拉低,所以对主机控制器来说,是一个下降沿中断触发方式;当一直按着触摸屏不松开,则会一直触发中断,现在大部分的触摸 IC 都是这样设计的。
1.添加 FT5426 设备节点
在 stm32mp15-pinctrl.dtsi 中,添加引脚配置信息:
ft5426int_reset_pins_a: ft5426int_reset_pins-0 {
pins1 {
pinmux = <STM32_PINMUX('I', 1, GPIO)>, /* ft5426 INT */
<STM32_PINMUX('H', 15, GPIO)>; /* ft5426 RESET */
bias-pull-up;
slew-rate = <0>;
};
};
2.FT5426 节点配置
FT5426这个触控 IC 挂载在I2C2 总线接口上,因此需要向 I2C2 节点下添加一个子节点(省略了之前的hdmi节点):
&i2c2 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c2_pins_a>;
pinctrl-1 = <&i2c2_pins_sleep_a>;
status = "okay";
ft5426: ft5426@38 {
compatible = "edt,edt-ft5426";
pinctrl-0 = <&ft5426int_reset_pins_a>;
reg = <0x38>;
irq-gpios = <&gpioi 1 GPIO_ACTIVE_LOW>;
reset-gpios = <&gpioh 15 GPIO_ACTIVE_LOW>;
status = "okay";
};
};
FT5426 的器件地址为0X38,irq-gpios 属性描述中断 IO 对应的 GPIO 为 PI1,reset-gpios 属性描述复位 IO 对应的 GPIO 为 PH15。
3.驱动编写
ft5x06.c:
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input/mt.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
/* FT5426寄存器相关宏定义 */
#define FT5426_DEVIDE_MODE_REG 0x00 // 模式寄存器
#define FT5426_TD_STATUS_REG 0x02 // 状态寄存器
#define FT5426_TOUCH_DATA_REG 0x03 // 触摸数据读取的起始寄存器
#define FT5426_ID_G_MODE_REG 0xA4 // 中断模式寄存器
#define MAX_SUPPORT_POINTS 5 // ft5426最大支持5点触摸
#define TOUCH_EVENT_DOWN 0x00 // 按下
#define TOUCH_EVENT_UP 0x01 // 抬起
#define TOUCH_EVENT_ON 0x02 // 接触
#define TOUCH_EVENT_RESERVED 0x03 // 保留
struct edt_ft5426_dev {
struct i2c_client *client;
struct input_dev *input;
int reset_gpio;
int irq_gpio;
};
static int edt_ft5426_ts_write(struct edt_ft5426_dev *ft5426, u8 addr, u8 *buf, u16 len)
{
struct i2c_client *client = ft5426->client;
struct i2c_msg msg;
u8 send_buf[6] = {0};
int ret;
send_buf[0] = addr;
memcpy(&send_buf[1], buf, len);
msg.flags = 0; //i2c写
msg.addr = client->addr;
msg.buf = send_buf;
msg.len = len + 1;
ret = i2c_transfer(client->adapter, &msg, 1);
if (1 == ret)
return 0;
else {
dev_err(&client->dev, "%s: write error, addr=0x%x len=%d.\n", __func__, addr, len);
return -1;
}
}
static int edt_ft5426_ts_read(struct edt_ft5426_dev *ft5426, u8 addr, u8 *buf, u16 len)
{
struct i2c_client *client = ft5426->client;
struct i2c_msg msg[2];
int ret;
msg[0].flags = 0; // i2c写
msg[0].addr = client->addr;
msg[0].buf = &addr;
msg[0].len = 1; // 1个字节
msg[1].flags = I2C_M_RD; //i2c读
msg[1].addr = client->addr;
msg[1].buf = buf;
msg[1].len = len;
ret = i2c_transfer(client->adapter, msg, 2);
if (2 == ret)
return 0;
else {
dev_err(&client->dev, "%s: read error, addr=0x%x len=%d.\n", __func__, addr, len);
return -1;
}
}
static int edt_ft5426_ts_reset(struct edt_ft5426_dev *ft5426)
{
struct i2c_client *client = ft5426->client;
int ret;
/* 从设备树中获取复位管脚 */
ft5426->reset_gpio = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
if (!gpio_is_valid(ft5426->reset_gpio)) {
dev_err(&client->dev, "Failed to get ts reset gpio\n");
return ft5426->reset_gpio;
}
/* 申请使用管脚 */
ret = devm_gpio_request_one(&client->dev, ft5426->reset_gpio, GPIOF_OUT_INIT_HIGH, "ft5426 reset");
if (ret < 0)
return ret;
msleep(20);
gpio_set_value_cansleep(ft5426->reset_gpio, 0); // 拉低复位引脚
msleep(5);
gpio_set_value_cansleep(ft5426->reset_gpio, 1); // 拉高复位引脚,结束复位
return 0;
}
static irqreturn_t edt_ft5426_ts_isr(int irq, void *dev_id)
{
struct edt_ft5426_dev *ft5426 = dev_id;
u8 rdbuf[30] = {0};
int i, type, x, y, id;
bool down;
int ret;
/* 读取FT5426触摸点坐标从0x02寄存器开始,连续读取29个寄存器 */
ret = edt_ft5426_ts_read(ft5426, FT5426_TD_STATUS_REG, rdbuf, 29);
if (ret)
goto out;
for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
u8 *buf = &rdbuf[i * 6 + 1];
/* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0x03),各bit位描述如下:
* bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件
* bit5:4 保留
* bit3:0 X轴触摸点的11~8位
*/
type = buf[0] >> 6; // 获取触摸点的Event Flag
if (type == TOUCH_EVENT_RESERVED)
continue;
/* 我们所使用的触摸屏和FT5426是反过来的 */
x = ((buf[2] << 8) | buf[3]) & 0x0fff;
y = ((buf[0] << 8) | buf[1]) & 0x0fff;
/* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0x05),各bit位描述如下:
* bit7:4 Touch ID 触摸ID,表示是哪个触摸点
* bit3:0 Y轴触摸点的11~8位。
*/
id = (buf[2] >> 4) & 0x0f;
down = type != TOUCH_EVENT_UP;
input_mt_slot(ft5426->input, id);
input_mt_report_slot_state(ft5426->input, MT_TOOL_FINGER, down);
if (!down)
continue;
input_report_abs(ft5426->input, ABS_MT_POSITION_X, x);
input_report_abs(ft5426->input, ABS_MT_POSITION_Y, y);
}
input_mt_report_pointer_emulation(ft5426->input, true);
input_sync(ft5426->input);
out:
return IRQ_HANDLED;
}
static int edt_ft5426_ts_irq(struct edt_ft5426_dev *ft5426)
{
struct i2c_client *client = ft5426->client;
int ret;
/* 从设备树中获取中断管脚 */
ft5426->irq_gpio = of_get_named_gpio(client->dev.of_node, "irq-gpios", 0);
if (!gpio_is_valid(ft5426->irq_gpio)) {
dev_err(&client->dev, "Failed to get ts interrupt gpio\n");
return ft5426->irq_gpio;
}
/* 申请使用管脚 */
ret = devm_gpio_request_one(&client->dev, ft5426->irq_gpio, GPIOF_IN, "ft5426 interrupt");
if (ret < 0)
return ret;
/* 注册中断服务函数 */
ret = devm_request_threaded_irq(&client->dev, gpio_to_irq(ft5426->irq_gpio),
NULL, edt_ft5426_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, ft5426);
if (ret) {
dev_err(&client->dev, "Failed to request touchscreen IRQ.\n");
return ret;
}
return 0;
}
static int edt_ft5426_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct edt_ft5426_dev *ft5426;
struct input_dev *input;
u8 data;
int ret;
/* 实例化一个struct edt_ft5426_dev对象 */
ft5426 = devm_kzalloc(&client->dev, sizeof(struct edt_ft5426_dev), GFP_KERNEL);
if (!ft5426) {
dev_err(&client->dev, "Failed to allocate ft5426 driver data.\n");
return -ENOMEM;
}
ft5426->client = client;
/* 复位FT5426触摸芯片 */
ret = edt_ft5426_ts_reset(ft5426);
if (ret)
return ret;
msleep(5);
/* 初始化FT5426 */
data = 0;
edt_ft5426_ts_write(ft5426, FT5426_DEVIDE_MODE_REG, &data, 1);
data = 1;
edt_ft5426_ts_write(ft5426, FT5426_ID_G_MODE_REG, &data, 1);
/* 申请、注册中断服务函数 */
ret = edt_ft5426_ts_irq(ft5426);
if (ret)
return ret;
/* 注册input设备 */
input = devm_input_allocate_device(&client->dev);
if (!input) {
dev_err(&client->dev, "Failed to allocate input device.\n");
return -ENOMEM;
}
ft5426->input = input;
input->name = "FocalTech FT5426 TouchScreen";
input->id.bustype = BUS_I2C;
input_set_abs_params(input, ABS_MT_POSITION_X, 0, 1024, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 600, 0, 0);
ret = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT);
if (ret) {
dev_err(&client->dev, "Failed to init MT slots.\n");
return ret;
}
ret = input_register_device(input);
if (ret)
return ret;
i2c_set_clientdata(client, ft5426);
return 0;
}
static int edt_ft5426_ts_remove(struct i2c_client *client)
{
struct edt_ft5426_dev *ft5426 = i2c_get_clientdata(client);
input_unregister_device(ft5426->input);
return 0;
}
static const struct of_device_id edt_ft5426_of_match[] = {
{ .compatible = "edt,edt-ft5426", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, edt_ft5426_of_match);
static struct i2c_driver edt_ft5426_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "edt_ft5426",
.of_match_table = of_match_ptr(edt_ft5426_of_match),
},
.probe = edt_ft5426_ts_probe,
.remove = edt_ft5426_ts_remove,
};
module_i2c_driver(edt_ft5426_ts_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("amonter");
MODULE_INFO(intree, "Y");
①自定义结构体 edt_ft5426_dev,用于描述 FT5426 设备,其中包括 I2C 从机设备指针 client、input 输入设备指针 input、一个复位引脚 reset_gpio 以及一个中断引脚 irq_gpio。
②自定义函数 edt_ft5426_ts_write,用于向 FT5426 内部寄存器连续写入数据,可指定写入数据字节长度。
③自定义函数 edt_ft5426_ts_read,用于连续读取 FT5426 内部寄存器中的数据,可指定读取数据字节长度。
④自定义函数 edt_ft5426_ts_reset,用于复位 FT5426 触摸芯片,FT5426 的复位 IO 为低电平有效。FT5426 的复位过程没有什么特殊要求,直接将复位 IO 拉低,延时 5ms 然后再将其拉高即可。复位结束之后先延时一段时间再进行读写寄存器操作。
⑤edt_ft5426_ts_isr 函数,触摸屏中断服务函数,触摸点坐标的上报就是在此函数中完成的。从 0x02 这个地址开始,一共 29 个寄存器,是存储的所有触摸点信息。使用for循环一个一个上报,使用 Type B 时序,最后通过 input_sync 函数上报 SYN_REPORT 事件。
⑥自定义函数 edt_ft5426_ts_irq,用于初始化 FT5426 中断 IO,使用函数 devm_request_threaded_irq 申请并注册中断,为下降沿触发。
⑦I2C 总线的 probe 函数 edt_ft5426_ts_probe,当 I2C 从机设备与驱动匹配成功之后就会执行这个函数,一般在此函数中完成一些初始化工作。
⑧input_mt_init_slots 函数是用来初始化 slots,第二个参数表示触摸屏设置支持的最大触摸点数量,FT5426 是个 5 点电容触摸芯片,因此一共 5 个 slot;最后一个参数是一个标志位,表示设备类型,INPUT_MT_DIRECT 表示为触摸屏设备类型。
⑨edt_ft5426_ts_remove 函数用来卸载驱动模块,因为前面很多资源都是用“ devm_”前缀函数来申请的,因此不需要手动释放。此函数只需要调用 input_unregister_device 来释放掉前面添加到内核中的 input_dev。
4.运行测试
将驱动文件编译,放入nfs指定目录然后启动开发板。加载驱动模块后就会生成/dev/input/eventX(X=1,2,3…)。
数据解析:
使用hexdump /dev/input/event1
命令,然后手动操作电容屏,终端就可以接受到数据。上报的信息是按照 input_event 类型呈现的。
5.将驱动添加到内核中
将驱动添加到内核,系统启动时就会自动挂载,不需要再手动挂载了。
1)将驱动文件放到合适的位置
ft5x06.c 是个触摸屏驱动,linux 内核里将触摸屏驱动放到了 drivers/input/touchscreen 目录下,因此将 ft5x06.c 也拷贝到此目录下。
2)修改Makefile
修改 drivers/input/touchscreen 目录下的 Makefile,在最下面添加一行:
obj-y += ft5x06.o
这样就将驱动文件添加进了内核编译。修改完成以后重新编译 linux 内核,然后用新的 uImage 启动开发板。如果驱动添加成功,系统启动的时候就会输出下图信息:
6.tslib 移植与使用
tslib 是一个开源的第三方库,用于触摸屏性能调试,使用电阻屏的时候一般使用 tslib 进行校准。虽然电容屏不需要校准,但是由于电容屏加工的原因,有的时候其不一定精准,因此有时候也需要进行校准。
在 buildroot 的 menuconfig 中使能 tslib 库:
然后使用sudo make
编译出文件系统,并解压到 nfs 的对应目录即可。
测试:
校准命令:ts_calibrate
,如果对校准结果不满意,直接删除掉 /etc/pointercal 文件。
使用ts_test_mt
命令测试触摸屏工作是否正常,以及多点触摸是否有效。此时会打开一个测试界面,有三个按钮“Drag”、“Draw”和“Quit”,这三个按钮的功能如下:
Drag:拖拽按钮,默认就是此功能,可以看到屏幕中间有一个十字光标,可以通过触摸屏幕来拖拽此光标。一个触摸点一个十字光标,对于 5 点电容触摸屏,如果 5 个手指都放到屏幕上,那么就有 5 个光标,一个手指一个。
Draw:绘制按钮,按下此按钮就可以在屏幕上进行简单的绘制,可以通过此功能检测多点触摸工作是否正常。
Quit:退出按钮,退出 ts_test_mt 测试软件。
点击“Draw”按钮,使用绘制功能,5 个手指一起划过屏幕,如果多点电容屏工作正常的话就会在屏幕上留下 5 条线。
四、使用内核自带的驱动
内核自带的 FT5426 的驱动文件为 drivers/input/touchscreen/edt-ft5x06.c,此驱动文件不仅能够驱动 FT5426,FT5206、FT5406 这些都可以驱动。
先将之前添加进内核编译的自己的驱动去除,将Makefile中添加的命令去掉即可。
1.使能内核自带的 FT5X06 驱动
在 Linux 内核的 menuconfig 进行使能:
2.修改设备树
修改之前编写的 ft5426 设备节点,添加 compatible 属性。edt-ft5x06.c 所支持的 compatible 属性列表如下:
将 compatible 属性修改为某一个兼容值即可。
ft5426: ft5426@38 {
compatible = "edt,edt-ft5406";
pinctrl-0 = <&ft5426int_reset_pins_a>;
reg = <0x38>;
irq-gpios = <&gpioi 1 GPIO_ACTIVE_LOW>;
reset-gpios = <&gpioh 15 GPIO_ACTIVE_LOW>;
status = "okay";
};
修改完成后重新编译出 uImage 和设备树,然后启动开发板即可。
五、4.3寸屏驱动
4.3寸屏幕的驱动 IC 都是 GT9147,因此本质上就是编写 GT9147 驱动。
1.修改设备树
在 i2c2 节点下添加一个子节点:
gt9147: gt9147@14 {
compatible = "atk-gt9147";
reg = <0x14>;
interrupt-parent = <&gpioi>;
interrupts = <1 IRQ_TYPE_EDGE_RISING>;
interrupt-gpios = <&gpioi 1 GPIO_ACTIVE_LOW>;
reset-gpios = <&gpioh 15 GPIO_ACTIVE_LOW>;
status = "okay";
};
在 ST 官方的内核配置中,已经把 gt9147 这个驱动编译进内核了,所以这里的 compatible 的属性值为“atk-gt9147”,免得使用内核的 gt9147 驱动代码。
2.添加屏幕参数
在 drivers/gpu/drm/panel/panel-simple.c 文件下,添加对应屏幕的参数,4.3 寸 800480 和 480272 这两款屏幕参数如下:
static const struct drm_display_mode ATK4384_mode = {
.clock = 33300,
.hdisplay = 800,
.hsync_start = 800 + 88,
.hsync_end = 800 + 88 + 48,
.htotal = 800 + 88 + 48 + 40,
.vdisplay = 480,
.vsync_start = 480 + 32,
.vsync_end = 480 + 32 + 3,
.vtotal = 480 + 32 + 3 + 13,
.vrefresh = 60,
};
static const struct drm_display_mode ATK4342_mode = {
.clock = 9000,
.hdisplay = 480,
.hsync_start = 480 + 40,
.hsync_end = 480 + 40 + 1,
.htotal = 480 + 40 + 1 + 5,
.vdisplay = 272,
.vsync_start = 272 + 8,
.vsync_end = 272 + 8 + 1,
.vtotal = 272 + 8 + 1 + 8,
.vrefresh = 60,
};
static const struct panel_desc alientek_desc = {
.modes = &ATK7016_mode,
.num_modes = 1,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
前两个为 800480 和 480272 两个屏的参数,第三个中,modes 属性为“ATK7016_mode”,如果屏为 480*272 就把 modes 属性修改为 ATK4342_mode。
将原子的驱动文件拷贝进来然后编译,编译完成后手动挂载即可。如果在使用 tslib 的时候提示找不到设备,使用export TSLIB_TSDEVICE=/dev/input/event1
命令手动声明设备即可。
注:如果想直接使用 Linux 自带的 gt9147 驱动,直接修改设备树的 compatible 属性值为“goodix,gt9147”即可。另外,原子哥的驱动中,因为 GT9147 没有硬件检测每个触摸点的按下和抬起,因此只能单点触摸。