Linux下GT911驱动移植

Kernel版本:6.6.52

开始前首先确认所用屏幕背面IC的丝印,到底是ft5426还是gt911,二者驱动不通用

客制化驱动

若使用正点原子提供的gt9147.c驱动:

设备树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
&i2c2 {
gt911:gt911@14 {
compatible = "goodix,gt911", "goodix,gt9xx";
reg = <0x14>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gt911>;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
status = "okay";
};
};

&iomuxc {
pinctrl_gt911: gt911grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x79 /* TSC_INT */
MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0
>;
};
};

6.6.52版本的Kernel DT不需要把MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09单独移到iomuxc_snvs

驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#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/of.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/i2c.h>
#include <asm/unaligned.h>

#define GT_CTRL_REG 0X8040
#define GT_MODSW_REG 0X804D
#define GT_9xx_CFGS_REG 0X8047
#define GT_1xx_CFGS_REG 0X8050
#define GT_CHECK_REG 0X80FF
#define GT_PID_REG 0X8140

#define GT_GSTID_REG 0X814E
#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

struct gt911_dev {
int irq_pin,reset_pin;
int irqnum;
int irqtype;
int max_x;
int max_y;
void *private_data;
struct input_dev *input;
struct i2c_client *client;

};
struct gt911_dev gt911;

const u8 irq_table[] = {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH}; /* 触发方式 */

/* reset gt911 */
static int gt911_ts_reset(struct i2c_client *client, struct gt911_dev *dev)
{
int ret = 0;

/* request RESET gpio */
if (gpio_is_valid(dev->reset_pin)) {
ret = devm_gpio_request_one(&client->dev,
dev->reset_pin, GPIOF_OUT_INIT_HIGH,
"gt911 reset");
if (ret) {
return ret;
}
}

/* request INT gpio */
if (gpio_is_valid(dev->irq_pin)) {
ret = devm_gpio_request_one(&client->dev,
dev->irq_pin, GPIOF_OUT_INIT_HIGH,
"gt911 int");
if (ret) {
return ret;
}
}

/* initialize gt911 follow datasheet */
gpio_set_value(dev->reset_pin, 0);
msleep(10);
gpio_set_value(dev->reset_pin, 1);
msleep(10);
gpio_set_value(dev->irq_pin, 0);
msleep(50);
gpio_direction_input(dev->irq_pin);

return 0;
}

static int gt911_read_regs(struct gt911_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;

regdata[0] = reg >> 8;
regdata[1] = reg & 0xFF;

msg[0].addr = client->addr;
msg[0].flags = !I2C_M_RD;
msg[0].buf = &regdata[0];
msg[0].len = 2;

msg[1].addr = client->addr;
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;
}

static s32 gt911_write_regs(struct gt911_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;
b[1] = reg & 0XFF;
memcpy(&b[2],buf,len);

msg.addr = client->addr;
msg.flags = 0;

msg.buf = b;
msg.len = len + 2;

return i2c_transfer(client->adapter, &msg, 1);
}

static irqreturn_t gt911_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 gt911_dev *dev = dev_id;

ret = gt911_read_regs(dev, GT_GSTID_REG, &data, 1);
if (data == 0x00) { /* no touch data */
goto fail;
} else { /* calculate touch data */
touch_num = data & 0x0f;
}

if(touch_num) {
gt911_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;
gt911_write_regs(dev, GT_GSTID_REG, &data, 1);

fail:
return IRQ_HANDLED;
}


static int gt911_ts_irq(struct i2c_client *client, struct gt911_dev *dev)
{
int ret = 0;

/* requeset IRQ */
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
gt911_irq_handler, irq_table[dev->irqtype] | IRQF_ONESHOT,
client->name, &gt911);
if (ret) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return ret;
}

return 0;
}


static int gt911_read_firmware(struct i2c_client *client, struct gt911_dev *dev)
{
int ret = 0, version = 0;
u16 id = 0;
u8 data[7]={0};
char id_str[5];
ret = gt911_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 = gt911_read_regs(dev, GT_1xx_CFGS_REG, data, 7);
break;
default:
ret = gt911_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;
}

static int gt911_probe(struct i2c_client *client)
{
u8 data, ret;
gt911.client = client;

gt911.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
gt911.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);

ret = gt911_ts_reset(client, &gt911);
if(ret < 0) {
goto fail;
}

data = 0x02;
gt911_write_regs(&gt911, GT_CTRL_REG, &data, 1);
mdelay(100);
data = 0x0;
gt911_write_regs(&gt911, GT_CTRL_REG, &data, 1);
mdelay(100);


ret = gt911_read_firmware(client, &gt911);
if(ret != 0) {
printk("Fail !!! check !!\r\n");
goto fail;
}

gt911.input = devm_input_allocate_device(&client->dev);
if (!gt911.input) {
ret = -ENOMEM;
goto fail;
}
gt911.input->name = client->name;
gt911.input->id.bustype = BUS_I2C;
gt911.input->dev.parent = &client->dev;

__set_bit(EV_KEY, gt911.input->evbit);
__set_bit(EV_ABS, gt911.input->evbit);
__set_bit(BTN_TOUCH, gt911.input->keybit);

input_set_abs_params(gt911.input, ABS_X, 0, gt911.max_x, 0, 0);
input_set_abs_params(gt911.input, ABS_Y, 0, gt911.max_y, 0, 0);
input_set_abs_params(gt911.input, ABS_MT_POSITION_X,0, gt911.max_x, 0, 0);
input_set_abs_params(gt911.input, ABS_MT_POSITION_Y,0, gt911.max_y, 0, 0);
ret = input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, 0);
if (ret) {
goto fail;
}

ret = input_register_device(gt911.input);
if (ret)
goto fail;

ret = gt911_ts_irq(client, &gt911);
if(ret < 0) {
goto fail;
}

dev_info(&client->dev, "GT911 Probe OK\r\n");

return 0;

fail:
return ret;
}


static void gt911_remove(struct i2c_client *client)
{
input_unregister_device(gt911.input);
}


const struct of_device_id gt911_of_match_table[] = {
{.compatible = "goodix,gt911" },
{ }
};

struct i2c_driver gt911_i2c_driver = {
.driver = {
.name = "gt911",
.owner = THIS_MODULE,
.of_match_table = gt911_of_match_table,
},
.probe = gt911_probe,
.remove = gt911_remove,
};

module_i2c_driver(gt911_i2c_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("aki");

Kernel提供驱动

驱动文件为/drivers/input/touchscreen/goodix.c

设备树

按照binding要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/goodix.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Goodix GT9xx series touchscreen controller

maintainers:
- Dmitry Torokhov <dmitry.torokhov@gmail.com>

allOf:
- $ref: touchscreen.yaml#

properties:
compatible:
enum:
- goodix,gt1151
- goodix,gt1158
- goodix,gt5663
- goodix,gt5688
- goodix,gt911
- goodix,gt9110
- goodix,gt912
- goodix,gt9147
- goodix,gt917s
- goodix,gt927
- goodix,gt9271
- goodix,gt928
- goodix,gt9286
- goodix,gt967

reg:
enum: [ 0x5d, 0x14 ]

interrupts:
maxItems: 1

irq-gpios:
description: GPIO pin used for IRQ. The driver uses the interrupt gpio pin
as output to reset the device.
maxItems: 1

reset-gpios:
maxItems: 1

AVDD28-supply:
description: Analog power supply regulator on AVDD28 pin

VDDIO-supply:
description: GPIO power supply regulator on VDDIO pin

touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-swapped-x-y: true

additionalProperties: false

required:
- compatible
- reg
- interrupts

examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
gt928@5d {
compatible = "goodix,gt928";
reg = <0x5d>;
interrupt-parent = <&gpio>;
interrupts = <0 0>;
irq-gpios = <&gpio1 0 0>;
reset-gpios = <&gpio1 1 0>;
};
};

compatiblereginterrupts必须要有,其他的无所谓,修改设备树节点如下:

1
2
3
4
5
6
7
8
9
10
11
&i2c2 {
gt911:gt911@14 {
compatible = "goodix,gt911", "goodix,gt9xx";
reg = <0x14>;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>;
irq-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};

注意:这里的reset-gpio和irq-gpio有效电平均为高电平,不能设置成低电平,虽然硬件设计时INT和RST为外部上拉低电平有效。原因未知。

驱动由于找不到本地的xxx_cfg.bin文件导致长时间等待(一般为64s)后再进行input注册的问题可以通过在goodix.c中Probe函数中修改判断条件解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 注释掉本地cfg检索,防止找不到cfg.bin时长时间等待 */
//if (ts->load_cfg_from_disk) {
if (0){
/* update device config */
error = device_property_read_string(&client->dev,
"goodix,config-name",
&cfg_name);
if (!error)
snprintf(ts->cfg_name, sizeof(ts->cfg_name),
"goodix/%s", cfg_name);
else
snprintf(ts->cfg_name, sizeof(ts->cfg_name),
"goodix_%s_cfg.bin", ts->id);

error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
&client->dev, GFP_KERNEL, ts,
goodix_config_cb);
if (error) {
dev_err(&client->dev,
"Failed to invoke firmware loader: %d\n",
error);
return error;
}

return 0;
} else {
......
}

Linux下GT911驱动移植
http://akichen891.github.io/2025/04/13/Linux下GT911驱动移植/
作者
Aki
发布于
2025年4月13日
更新于
2025年4月16日
许可协议